4c4288750b20706592ac9dd2823c95f8f4a2a44a
[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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 void UpdateGameDoorValues(void)
2873 {
2874   UpdateGameControlValues();
2875 }
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   if (tape.recording)
3580   {
3581     // initialize tape actions from game when recording tape
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584
3585     // initialize visible playfield size when recording tape (for team mode)
3586     tape.scr_fieldx = SCR_FIELDX;
3587     tape.scr_fieldy = SCR_FIELDY;
3588   }
3589
3590   // don't play tapes over network
3591   network_playing = (network.enabled && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607     player->buried = FALSE;
3608
3609     player->action = 0;
3610     player->effective_action = 0;
3611     player->programmed_action = 0;
3612     player->snap_action = 0;
3613
3614     player->mouse_action.lx = 0;
3615     player->mouse_action.ly = 0;
3616     player->mouse_action.button = 0;
3617     player->mouse_action.button_hint = 0;
3618
3619     player->effective_mouse_action.lx = 0;
3620     player->effective_mouse_action.ly = 0;
3621     player->effective_mouse_action.button = 0;
3622     player->effective_mouse_action.button_hint = 0;
3623
3624     for (j = 0; j < MAX_NUM_KEYS; j++)
3625       player->key[j] = FALSE;
3626
3627     player->num_white_keys = 0;
3628
3629     player->dynabomb_count = 0;
3630     player->dynabomb_size = 1;
3631     player->dynabombs_left = 0;
3632     player->dynabomb_xl = FALSE;
3633
3634     player->MovDir = initial_move_dir;
3635     player->MovPos = 0;
3636     player->GfxPos = 0;
3637     player->GfxDir = initial_move_dir;
3638     player->GfxAction = ACTION_DEFAULT;
3639     player->Frame = 0;
3640     player->StepFrame = 0;
3641
3642     player->initial_element = player->element_nr;
3643     player->artwork_element =
3644       (level.use_artwork_element[i] ? level.artwork_element[i] :
3645        player->element_nr);
3646     player->use_murphy = FALSE;
3647
3648     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3649     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3650
3651     player->gravity = level.initial_player_gravity[i];
3652
3653     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3654
3655     player->actual_frame_counter = 0;
3656
3657     player->step_counter = 0;
3658
3659     player->last_move_dir = initial_move_dir;
3660
3661     player->is_active = FALSE;
3662
3663     player->is_waiting = FALSE;
3664     player->is_moving = FALSE;
3665     player->is_auto_moving = FALSE;
3666     player->is_digging = FALSE;
3667     player->is_snapping = FALSE;
3668     player->is_collecting = FALSE;
3669     player->is_pushing = FALSE;
3670     player->is_switching = FALSE;
3671     player->is_dropping = FALSE;
3672     player->is_dropping_pressed = FALSE;
3673
3674     player->is_bored = FALSE;
3675     player->is_sleeping = FALSE;
3676
3677     player->was_waiting = TRUE;
3678     player->was_moving = FALSE;
3679     player->was_snapping = FALSE;
3680     player->was_dropping = FALSE;
3681
3682     player->force_dropping = FALSE;
3683
3684     player->frame_counter_bored = -1;
3685     player->frame_counter_sleeping = -1;
3686
3687     player->anim_delay_counter = 0;
3688     player->post_delay_counter = 0;
3689
3690     player->dir_waiting = initial_move_dir;
3691     player->action_waiting = ACTION_DEFAULT;
3692     player->last_action_waiting = ACTION_DEFAULT;
3693     player->special_action_bored = ACTION_DEFAULT;
3694     player->special_action_sleeping = ACTION_DEFAULT;
3695
3696     player->switch_x = -1;
3697     player->switch_y = -1;
3698
3699     player->drop_x = -1;
3700     player->drop_y = -1;
3701
3702     player->show_envelope = 0;
3703
3704     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3705
3706     player->push_delay       = -1;      // initialized when pushing starts
3707     player->push_delay_value = game.initial_push_delay_value;
3708
3709     player->drop_delay = 0;
3710     player->drop_pressed_delay = 0;
3711
3712     player->last_jx = -1;
3713     player->last_jy = -1;
3714     player->jx = -1;
3715     player->jy = -1;
3716
3717     player->shield_normal_time_left = 0;
3718     player->shield_deadly_time_left = 0;
3719
3720     player->last_removed_element = EL_UNDEFINED;
3721
3722     player->inventory_infinite_element = EL_UNDEFINED;
3723     player->inventory_size = 0;
3724
3725     if (level.use_initial_inventory[i])
3726     {
3727       for (j = 0; j < level.initial_inventory_size[i]; j++)
3728       {
3729         int element = level.initial_inventory_content[i][j];
3730         int collect_count = element_info[element].collect_count_initial;
3731         int k;
3732
3733         if (!IS_CUSTOM_ELEMENT(element))
3734           collect_count = 1;
3735
3736         if (collect_count == 0)
3737           player->inventory_infinite_element = element;
3738         else
3739           for (k = 0; k < collect_count; k++)
3740             if (player->inventory_size < MAX_INVENTORY_SIZE)
3741               player->inventory_element[player->inventory_size++] = element;
3742       }
3743     }
3744
3745     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3746     SnapField(player, 0, 0);
3747
3748     map_player_action[i] = i;
3749   }
3750
3751   network_player_action_received = FALSE;
3752
3753   // initial null action
3754   if (network_playing)
3755     SendToServer_MovePlayer(MV_NONE);
3756
3757   FrameCounter = 0;
3758   TimeFrames = 0;
3759   TimePlayed = 0;
3760   TimeLeft = level.time;
3761   TapeTime = 0;
3762
3763   ScreenMovDir = MV_NONE;
3764   ScreenMovPos = 0;
3765   ScreenGfxPos = 0;
3766
3767   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3768
3769   game.robot_wheel_x = -1;
3770   game.robot_wheel_y = -1;
3771
3772   game.exit_x = -1;
3773   game.exit_y = -1;
3774
3775   game.all_players_gone = FALSE;
3776
3777   game.LevelSolved = FALSE;
3778   game.GameOver = FALSE;
3779
3780   game.GamePlayed = !tape.playing;
3781
3782   game.LevelSolved_GameWon = FALSE;
3783   game.LevelSolved_GameEnd = FALSE;
3784   game.LevelSolved_SaveTape = FALSE;
3785   game.LevelSolved_SaveScore = FALSE;
3786
3787   game.LevelSolved_CountingTime = 0;
3788   game.LevelSolved_CountingScore = 0;
3789   game.LevelSolved_CountingHealth = 0;
3790
3791   game.panel.active = TRUE;
3792
3793   game.no_time_limit = (level.time == 0);
3794
3795   game.yamyam_content_nr = 0;
3796   game.robot_wheel_active = FALSE;
3797   game.magic_wall_active = FALSE;
3798   game.magic_wall_time_left = 0;
3799   game.light_time_left = 0;
3800   game.timegate_time_left = 0;
3801   game.switchgate_pos = 0;
3802   game.wind_direction = level.wind_direction_initial;
3803
3804   game.score = 0;
3805   game.score_final = 0;
3806
3807   game.health = MAX_HEALTH;
3808   game.health_final = MAX_HEALTH;
3809
3810   game.gems_still_needed = level.gems_needed;
3811   game.sokoban_fields_still_needed = 0;
3812   game.sokoban_objects_still_needed = 0;
3813   game.lights_still_needed = 0;
3814   game.players_still_needed = 0;
3815   game.friends_still_needed = 0;
3816
3817   game.lenses_time_left = 0;
3818   game.magnify_time_left = 0;
3819
3820   game.ball_active = level.ball_active_initial;
3821   game.ball_content_nr = 0;
3822
3823   game.explosions_delayed = TRUE;
3824
3825   game.envelope_active = FALSE;
3826
3827   for (i = 0; i < NUM_BELTS; i++)
3828   {
3829     game.belt_dir[i] = MV_NONE;
3830     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3831   }
3832
3833   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3834     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3835
3836 #if DEBUG_INIT_PLAYER
3837   DebugPrintPlayerStatus("Player status at level initialization");
3838 #endif
3839
3840   SCAN_PLAYFIELD(x, y)
3841   {
3842     Tile[x][y] = Last[x][y] = level.field[x][y];
3843     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3844     ChangeDelay[x][y] = 0;
3845     ChangePage[x][y] = -1;
3846     CustomValue[x][y] = 0;              // initialized in InitField()
3847     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3848     AmoebaNr[x][y] = 0;
3849     WasJustMoving[x][y] = 0;
3850     WasJustFalling[x][y] = 0;
3851     CheckCollision[x][y] = 0;
3852     CheckImpact[x][y] = 0;
3853     Stop[x][y] = FALSE;
3854     Pushed[x][y] = FALSE;
3855
3856     ChangeCount[x][y] = 0;
3857     ChangeEvent[x][y] = -1;
3858
3859     ExplodePhase[x][y] = 0;
3860     ExplodeDelay[x][y] = 0;
3861     ExplodeField[x][y] = EX_TYPE_NONE;
3862
3863     RunnerVisit[x][y] = 0;
3864     PlayerVisit[x][y] = 0;
3865
3866     GfxFrame[x][y] = 0;
3867     GfxRandom[x][y] = INIT_GFX_RANDOM();
3868     GfxElement[x][y] = EL_UNDEFINED;
3869     GfxAction[x][y] = ACTION_DEFAULT;
3870     GfxDir[x][y] = MV_NONE;
3871     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3872   }
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3877       emulate_bd = FALSE;
3878     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3879       emulate_sb = FALSE;
3880     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3881       emulate_sp = FALSE;
3882
3883     InitField(x, y, TRUE);
3884
3885     ResetGfxAnimation(x, y);
3886   }
3887
3888   InitBeltMovement();
3889
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     // set number of special actions for bored and sleeping animation
3895     player->num_special_action_bored =
3896       get_num_special_action(player->artwork_element,
3897                              ACTION_BORING_1, ACTION_BORING_LAST);
3898     player->num_special_action_sleeping =
3899       get_num_special_action(player->artwork_element,
3900                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3901   }
3902
3903   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3904                     emulate_sb ? EMU_SOKOBAN :
3905                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3906
3907   // initialize type of slippery elements
3908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3909   {
3910     if (!IS_CUSTOM_ELEMENT(i))
3911     {
3912       // default: elements slip down either to the left or right randomly
3913       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3914
3915       // SP style elements prefer to slip down on the left side
3916       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3917         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3918
3919       // BD style elements prefer to slip down on the left side
3920       if (game.emulation == EMU_BOULDERDASH)
3921         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3922     }
3923   }
3924
3925   // initialize explosion and ignition delay
3926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3927   {
3928     if (!IS_CUSTOM_ELEMENT(i))
3929     {
3930       int num_phase = 8;
3931       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3932                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3933                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3934       int last_phase = (num_phase + 1) * delay;
3935       int half_phase = (num_phase / 2) * delay;
3936
3937       element_info[i].explosion_delay = last_phase - 1;
3938       element_info[i].ignition_delay = half_phase;
3939
3940       if (i == EL_BLACK_ORB)
3941         element_info[i].ignition_delay = 1;
3942     }
3943   }
3944
3945   // correct non-moving belts to start moving left
3946   for (i = 0; i < NUM_BELTS; i++)
3947     if (game.belt_dir[i] == MV_NONE)
3948       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3949
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951   // use preferred player also in local single-player mode
3952   if (!network.enabled && !game.team_mode)
3953   {
3954     int new_index_nr = setup.network_player_nr;
3955
3956     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3957     {
3958       for (i = 0; i < MAX_PLAYERS; i++)
3959         stored_player[i].connected_locally = FALSE;
3960
3961       stored_player[new_index_nr].connected_locally = TRUE;
3962     }
3963   }
3964
3965   for (i = 0; i < MAX_PLAYERS; i++)
3966   {
3967     stored_player[i].connected = FALSE;
3968
3969     // in network game mode, the local player might not be the first player
3970     if (stored_player[i].connected_locally)
3971       local_player = &stored_player[i];
3972   }
3973
3974   if (!network.enabled)
3975     local_player->connected = TRUE;
3976
3977   if (tape.playing)
3978   {
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980       stored_player[i].connected = tape.player_participates[i];
3981   }
3982   else if (network.enabled)
3983   {
3984     // add team mode players connected over the network (needed for correct
3985     // assignment of player figures from level to locally playing players)
3986
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988       if (stored_player[i].connected_network)
3989         stored_player[i].connected = TRUE;
3990   }
3991   else if (game.team_mode)
3992   {
3993     // try to guess locally connected team mode players (needed for correct
3994     // assignment of player figures from level to locally playing players)
3995
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       if (setup.input[i].use_joystick ||
3998           setup.input[i].key.left != KSYM_UNDEFINED)
3999         stored_player[i].connected = TRUE;
4000   }
4001
4002 #if DEBUG_INIT_PLAYER
4003   DebugPrintPlayerStatus("Player status after level initialization");
4004 #endif
4005
4006 #if DEBUG_INIT_PLAYER
4007   Debug("game:init:player", "Reassigning players ...");
4008 #endif
4009
4010   // check if any connected player was not found in playfield
4011   for (i = 0; i < MAX_PLAYERS; i++)
4012   {
4013     struct PlayerInfo *player = &stored_player[i];
4014
4015     if (player->connected && !player->present)
4016     {
4017       struct PlayerInfo *field_player = NULL;
4018
4019 #if DEBUG_INIT_PLAYER
4020       Debug("game:init:player",
4021             "- looking for field player for player %d ...", i + 1);
4022 #endif
4023
4024       // assign first free player found that is present in the playfield
4025
4026       // first try: look for unmapped playfield player that is not connected
4027       for (j = 0; j < MAX_PLAYERS; j++)
4028         if (field_player == NULL &&
4029             stored_player[j].present &&
4030             !stored_player[j].mapped &&
4031             !stored_player[j].connected)
4032           field_player = &stored_player[j];
4033
4034       // second try: look for *any* unmapped playfield player
4035       for (j = 0; j < MAX_PLAYERS; j++)
4036         if (field_player == NULL &&
4037             stored_player[j].present &&
4038             !stored_player[j].mapped)
4039           field_player = &stored_player[j];
4040
4041       if (field_player != NULL)
4042       {
4043         int jx = field_player->jx, jy = field_player->jy;
4044
4045 #if DEBUG_INIT_PLAYER
4046         Debug("game:init:player", "- found player %d",
4047               field_player->index_nr + 1);
4048 #endif
4049
4050         player->present = FALSE;
4051         player->active = FALSE;
4052
4053         field_player->present = TRUE;
4054         field_player->active = TRUE;
4055
4056         /*
4057         player->initial_element = field_player->initial_element;
4058         player->artwork_element = field_player->artwork_element;
4059
4060         player->block_last_field       = field_player->block_last_field;
4061         player->block_delay_adjustment = field_player->block_delay_adjustment;
4062         */
4063
4064         StorePlayer[jx][jy] = field_player->element_nr;
4065
4066         field_player->jx = field_player->last_jx = jx;
4067         field_player->jy = field_player->last_jy = jy;
4068
4069         if (local_player == player)
4070           local_player = field_player;
4071
4072         map_player_action[field_player->index_nr] = i;
4073
4074         field_player->mapped = TRUE;
4075
4076 #if DEBUG_INIT_PLAYER
4077         Debug("game:init:player", "- map_player_action[%d] == %d",
4078               field_player->index_nr + 1, i + 1);
4079 #endif
4080       }
4081     }
4082
4083     if (player->connected && player->present)
4084       player->mapped = TRUE;
4085   }
4086
4087 #if DEBUG_INIT_PLAYER
4088   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4089 #endif
4090
4091 #else
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       for (j = 0; j < MAX_PLAYERS; j++)
4101       {
4102         struct PlayerInfo *field_player = &stored_player[j];
4103         int jx = field_player->jx, jy = field_player->jy;
4104
4105         // assign first free player found that is present in the playfield
4106         if (field_player->present && !field_player->connected)
4107         {
4108           player->present = TRUE;
4109           player->active = TRUE;
4110
4111           field_player->present = FALSE;
4112           field_player->active = FALSE;
4113
4114           player->initial_element = field_player->initial_element;
4115           player->artwork_element = field_player->artwork_element;
4116
4117           player->block_last_field       = field_player->block_last_field;
4118           player->block_delay_adjustment = field_player->block_delay_adjustment;
4119
4120           StorePlayer[jx][jy] = player->element_nr;
4121
4122           player->jx = player->last_jx = jx;
4123           player->jy = player->last_jy = jy;
4124
4125           break;
4126         }
4127       }
4128     }
4129   }
4130 #endif
4131
4132 #if 0
4133   Debug("game:init:player", "local_player->present == %d",
4134         local_player->present);
4135 #endif
4136
4137   // set focus to local player for network games, else to all players
4138   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4139   game.centered_player_nr_next = game.centered_player_nr;
4140   game.set_centered_player = FALSE;
4141   game.set_centered_player_wrap = FALSE;
4142
4143   if (network_playing && tape.recording)
4144   {
4145     // store client dependent player focus when recording network games
4146     tape.centered_player_nr_next = game.centered_player_nr_next;
4147     tape.set_centered_player = TRUE;
4148   }
4149
4150   if (tape.playing)
4151   {
4152     // when playing a tape, eliminate all players who do not participate
4153
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155
4156     if (!game.team_mode)
4157     {
4158       for (i = 0; i < MAX_PLAYERS; i++)
4159       {
4160         if (stored_player[i].active &&
4161             !tape.player_participates[map_player_action[i]])
4162         {
4163           struct PlayerInfo *player = &stored_player[i];
4164           int jx = player->jx, jy = player->jy;
4165
4166 #if DEBUG_INIT_PLAYER
4167           Debug("game:init:player", "Removing player %d at (%d, %d)",
4168                 i + 1, jx, jy);
4169 #endif
4170
4171           player->active = FALSE;
4172           StorePlayer[jx][jy] = 0;
4173           Tile[jx][jy] = EL_EMPTY;
4174         }
4175       }
4176     }
4177
4178 #else
4179
4180     for (i = 0; i < MAX_PLAYERS; i++)
4181     {
4182       if (stored_player[i].active &&
4183           !tape.player_participates[i])
4184       {
4185         struct PlayerInfo *player = &stored_player[i];
4186         int jx = player->jx, jy = player->jy;
4187
4188         player->active = FALSE;
4189         StorePlayer[jx][jy] = 0;
4190         Tile[jx][jy] = EL_EMPTY;
4191       }
4192     }
4193 #endif
4194   }
4195   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4196   {
4197     // when in single player mode, eliminate all but the local player
4198
4199     for (i = 0; i < MAX_PLAYERS; i++)
4200     {
4201       struct PlayerInfo *player = &stored_player[i];
4202
4203       if (player->active && player != local_player)
4204       {
4205         int jx = player->jx, jy = player->jy;
4206
4207         player->active = FALSE;
4208         player->present = FALSE;
4209
4210         StorePlayer[jx][jy] = 0;
4211         Tile[jx][jy] = EL_EMPTY;
4212       }
4213     }
4214   }
4215
4216   for (i = 0; i < MAX_PLAYERS; i++)
4217     if (stored_player[i].active)
4218       game.players_still_needed++;
4219
4220   if (level.solved_by_one_player)
4221     game.players_still_needed = 1;
4222
4223   // when recording the game, store which players take part in the game
4224   if (tape.recording)
4225   {
4226 #if USE_NEW_PLAYER_ASSIGNMENTS
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228       if (stored_player[i].connected)
4229         tape.player_participates[i] = TRUE;
4230 #else
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232       if (stored_player[i].active)
4233         tape.player_participates[i] = TRUE;
4234 #endif
4235   }
4236
4237 #if DEBUG_INIT_PLAYER
4238   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4239 #endif
4240
4241   if (BorderElement == EL_EMPTY)
4242   {
4243     SBX_Left = 0;
4244     SBX_Right = lev_fieldx - SCR_FIELDX;
4245     SBY_Upper = 0;
4246     SBY_Lower = lev_fieldy - SCR_FIELDY;
4247   }
4248   else
4249   {
4250     SBX_Left = -1;
4251     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4252     SBY_Upper = -1;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4254   }
4255
4256   if (full_lev_fieldx <= SCR_FIELDX)
4257     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4258   if (full_lev_fieldy <= SCR_FIELDY)
4259     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4260
4261   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4262     SBX_Left--;
4263   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4264     SBY_Upper--;
4265
4266   // if local player not found, look for custom element that might create
4267   // the player (make some assumptions about the right custom element)
4268   if (!local_player->present)
4269   {
4270     int start_x = 0, start_y = 0;
4271     int found_rating = 0;
4272     int found_element = EL_UNDEFINED;
4273     int player_nr = local_player->index_nr;
4274
4275     SCAN_PLAYFIELD(x, y)
4276     {
4277       int element = Tile[x][y];
4278       int content;
4279       int xx, yy;
4280       boolean is_player;
4281
4282       if (level.use_start_element[player_nr] &&
4283           level.start_element[player_nr] == element &&
4284           found_rating < 4)
4285       {
4286         start_x = x;
4287         start_y = y;
4288
4289         found_rating = 4;
4290         found_element = element;
4291       }
4292
4293       if (!IS_CUSTOM_ELEMENT(element))
4294         continue;
4295
4296       if (CAN_CHANGE(element))
4297       {
4298         for (i = 0; i < element_info[element].num_change_pages; i++)
4299         {
4300           // check for player created from custom element as single target
4301           content = element_info[element].change_page[i].target_element;
4302           is_player = ELEM_IS_PLAYER(content);
4303
4304           if (is_player && (found_rating < 3 ||
4305                             (found_rating == 3 && element < found_element)))
4306           {
4307             start_x = x;
4308             start_y = y;
4309
4310             found_rating = 3;
4311             found_element = element;
4312           }
4313         }
4314       }
4315
4316       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4317       {
4318         // check for player created from custom element as explosion content
4319         content = element_info[element].content.e[xx][yy];
4320         is_player = ELEM_IS_PLAYER(content);
4321
4322         if (is_player && (found_rating < 2 ||
4323                           (found_rating == 2 && element < found_element)))
4324         {
4325           start_x = x + xx - 1;
4326           start_y = y + yy - 1;
4327
4328           found_rating = 2;
4329           found_element = element;
4330         }
4331
4332         if (!CAN_CHANGE(element))
4333           continue;
4334
4335         for (i = 0; i < element_info[element].num_change_pages; i++)
4336         {
4337           // check for player created from custom element as extended target
4338           content =
4339             element_info[element].change_page[i].target_content.e[xx][yy];
4340
4341           is_player = ELEM_IS_PLAYER(content);
4342
4343           if (is_player && (found_rating < 1 ||
4344                             (found_rating == 1 && element < found_element)))
4345           {
4346             start_x = x + xx - 1;
4347             start_y = y + yy - 1;
4348
4349             found_rating = 1;
4350             found_element = element;
4351           }
4352         }
4353       }
4354     }
4355
4356     scroll_x = SCROLL_POSITION_X(start_x);
4357     scroll_y = SCROLL_POSITION_Y(start_y);
4358   }
4359   else
4360   {
4361     scroll_x = SCROLL_POSITION_X(local_player->jx);
4362     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4363   }
4364
4365   // !!! FIX THIS (START) !!!
4366   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4367   {
4368     InitGameEngine_EM();
4369   }
4370   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4371   {
4372     InitGameEngine_SP();
4373   }
4374   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4375   {
4376     InitGameEngine_MM();
4377   }
4378   else
4379   {
4380     DrawLevel(REDRAW_FIELD);
4381     DrawAllPlayers();
4382
4383     // after drawing the level, correct some elements
4384     if (game.timegate_time_left == 0)
4385       CloseAllOpenTimegates();
4386   }
4387
4388   // blit playfield from scroll buffer to normal back buffer for fading in
4389   BlitScreenToBitmap(backbuffer);
4390   // !!! FIX THIS (END) !!!
4391
4392   DrawMaskedBorder(fade_mask);
4393
4394   FadeIn(fade_mask);
4395
4396 #if 1
4397   // full screen redraw is required at this point in the following cases:
4398   // - special editor door undrawn when game was started from level editor
4399   // - drawing area (playfield) was changed and has to be removed completely
4400   redraw_mask = REDRAW_ALL;
4401   BackToFront();
4402 #endif
4403
4404   if (!game.restart_level)
4405   {
4406     // copy default game door content to main double buffer
4407
4408     // !!! CHECK AGAIN !!!
4409     SetPanelBackground();
4410     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4411     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4412   }
4413
4414   SetPanelBackground();
4415   SetDrawBackgroundMask(REDRAW_DOOR_1);
4416
4417   UpdateAndDisplayGameControlValues();
4418
4419   if (!game.restart_level)
4420   {
4421     UnmapGameButtons();
4422     UnmapTapeButtons();
4423
4424     FreeGameButtons();
4425     CreateGameButtons();
4426
4427     MapGameButtons();
4428     MapTapeButtons();
4429
4430     // copy actual game door content to door double buffer for OpenDoor()
4431     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4432
4433     OpenDoor(DOOR_OPEN_ALL);
4434
4435     KeyboardAutoRepeatOffUnlessAutoplay();
4436
4437 #if DEBUG_INIT_PLAYER
4438     DebugPrintPlayerStatus("Player status (final)");
4439 #endif
4440   }
4441
4442   UnmapAllGadgets();
4443
4444   MapGameButtons();
4445   MapTapeButtons();
4446
4447   if (!game.restart_level && !tape.playing)
4448   {
4449     LevelStats_incPlayed(level_nr);
4450
4451     SaveLevelSetup_SeriesInfo();
4452   }
4453
4454   game.restart_level = FALSE;
4455   game.restart_game_message = NULL;
4456
4457   game.request_active = FALSE;
4458   game.request_active_or_moving = FALSE;
4459
4460   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4461     InitGameActions_MM();
4462
4463   SaveEngineSnapshotToListInitial();
4464
4465   if (!game.restart_level)
4466   {
4467     PlaySound(SND_GAME_STARTING);
4468
4469     if (setup.sound_music)
4470       PlayLevelMusic();
4471   }
4472 }
4473
4474 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4475                         int actual_player_x, int actual_player_y)
4476 {
4477   // this is used for non-R'n'D game engines to update certain engine values
4478
4479   // needed to determine if sounds are played within the visible screen area
4480   scroll_x = actual_scroll_x;
4481   scroll_y = actual_scroll_y;
4482
4483   // needed to get player position for "follow finger" playing input method
4484   local_player->jx = actual_player_x;
4485   local_player->jy = actual_player_y;
4486 }
4487
4488 void InitMovDir(int x, int y)
4489 {
4490   int i, element = Tile[x][y];
4491   static int xy[4][2] =
4492   {
4493     {  0, +1 },
4494     { +1,  0 },
4495     {  0, -1 },
4496     { -1,  0 }
4497   };
4498   static int direction[3][4] =
4499   {
4500     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4501     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4502     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4503   };
4504
4505   switch (element)
4506   {
4507     case EL_BUG_RIGHT:
4508     case EL_BUG_UP:
4509     case EL_BUG_LEFT:
4510     case EL_BUG_DOWN:
4511       Tile[x][y] = EL_BUG;
4512       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4513       break;
4514
4515     case EL_SPACESHIP_RIGHT:
4516     case EL_SPACESHIP_UP:
4517     case EL_SPACESHIP_LEFT:
4518     case EL_SPACESHIP_DOWN:
4519       Tile[x][y] = EL_SPACESHIP;
4520       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4521       break;
4522
4523     case EL_BD_BUTTERFLY_RIGHT:
4524     case EL_BD_BUTTERFLY_UP:
4525     case EL_BD_BUTTERFLY_LEFT:
4526     case EL_BD_BUTTERFLY_DOWN:
4527       Tile[x][y] = EL_BD_BUTTERFLY;
4528       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4529       break;
4530
4531     case EL_BD_FIREFLY_RIGHT:
4532     case EL_BD_FIREFLY_UP:
4533     case EL_BD_FIREFLY_LEFT:
4534     case EL_BD_FIREFLY_DOWN:
4535       Tile[x][y] = EL_BD_FIREFLY;
4536       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4537       break;
4538
4539     case EL_PACMAN_RIGHT:
4540     case EL_PACMAN_UP:
4541     case EL_PACMAN_LEFT:
4542     case EL_PACMAN_DOWN:
4543       Tile[x][y] = EL_PACMAN;
4544       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4545       break;
4546
4547     case EL_YAMYAM_LEFT:
4548     case EL_YAMYAM_RIGHT:
4549     case EL_YAMYAM_UP:
4550     case EL_YAMYAM_DOWN:
4551       Tile[x][y] = EL_YAMYAM;
4552       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4553       break;
4554
4555     case EL_SP_SNIKSNAK:
4556       MovDir[x][y] = MV_UP;
4557       break;
4558
4559     case EL_SP_ELECTRON:
4560       MovDir[x][y] = MV_LEFT;
4561       break;
4562
4563     case EL_MOLE_LEFT:
4564     case EL_MOLE_RIGHT:
4565     case EL_MOLE_UP:
4566     case EL_MOLE_DOWN:
4567       Tile[x][y] = EL_MOLE;
4568       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4569       break;
4570
4571     case EL_SPRING_LEFT:
4572     case EL_SPRING_RIGHT:
4573       Tile[x][y] = EL_SPRING;
4574       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4575       break;
4576
4577     default:
4578       if (IS_CUSTOM_ELEMENT(element))
4579       {
4580         struct ElementInfo *ei = &element_info[element];
4581         int move_direction_initial = ei->move_direction_initial;
4582         int move_pattern = ei->move_pattern;
4583
4584         if (move_direction_initial == MV_START_PREVIOUS)
4585         {
4586           if (MovDir[x][y] != MV_NONE)
4587             return;
4588
4589           move_direction_initial = MV_START_AUTOMATIC;
4590         }
4591
4592         if (move_direction_initial == MV_START_RANDOM)
4593           MovDir[x][y] = 1 << RND(4);
4594         else if (move_direction_initial & MV_ANY_DIRECTION)
4595           MovDir[x][y] = move_direction_initial;
4596         else if (move_pattern == MV_ALL_DIRECTIONS ||
4597                  move_pattern == MV_TURNING_LEFT ||
4598                  move_pattern == MV_TURNING_RIGHT ||
4599                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4600                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4601                  move_pattern == MV_TURNING_RANDOM)
4602           MovDir[x][y] = 1 << RND(4);
4603         else if (move_pattern == MV_HORIZONTAL)
4604           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4605         else if (move_pattern == MV_VERTICAL)
4606           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4607         else if (move_pattern & MV_ANY_DIRECTION)
4608           MovDir[x][y] = element_info[element].move_pattern;
4609         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4610                  move_pattern == MV_ALONG_RIGHT_SIDE)
4611         {
4612           // use random direction as default start direction
4613           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4614             MovDir[x][y] = 1 << RND(4);
4615
4616           for (i = 0; i < NUM_DIRECTIONS; i++)
4617           {
4618             int x1 = x + xy[i][0];
4619             int y1 = y + xy[i][1];
4620
4621             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4622             {
4623               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4624                 MovDir[x][y] = direction[0][i];
4625               else
4626                 MovDir[x][y] = direction[1][i];
4627
4628               break;
4629             }
4630           }
4631         }                
4632       }
4633       else
4634       {
4635         MovDir[x][y] = 1 << RND(4);
4636
4637         if (element != EL_BUG &&
4638             element != EL_SPACESHIP &&
4639             element != EL_BD_BUTTERFLY &&
4640             element != EL_BD_FIREFLY)
4641           break;
4642
4643         for (i = 0; i < NUM_DIRECTIONS; i++)
4644         {
4645           int x1 = x + xy[i][0];
4646           int y1 = y + xy[i][1];
4647
4648           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4649           {
4650             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4651             {
4652               MovDir[x][y] = direction[0][i];
4653               break;
4654             }
4655             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4656                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4657             {
4658               MovDir[x][y] = direction[1][i];
4659               break;
4660             }
4661           }
4662         }
4663       }
4664       break;
4665   }
4666
4667   GfxDir[x][y] = MovDir[x][y];
4668 }
4669
4670 void InitAmoebaNr(int x, int y)
4671 {
4672   int i;
4673   int group_nr = AmoebaNeighbourNr(x, y);
4674
4675   if (group_nr == 0)
4676   {
4677     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4678     {
4679       if (AmoebaCnt[i] == 0)
4680       {
4681         group_nr = i;
4682         break;
4683       }
4684     }
4685   }
4686
4687   AmoebaNr[x][y] = group_nr;
4688   AmoebaCnt[group_nr]++;
4689   AmoebaCnt2[group_nr]++;
4690 }
4691
4692 static void LevelSolved(void)
4693 {
4694   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4695       game.players_still_needed > 0)
4696     return;
4697
4698   game.LevelSolved = TRUE;
4699   game.GameOver = TRUE;
4700
4701   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4702                       game_em.lev->score :
4703                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4704                       game_mm.score :
4705                       game.score);
4706   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4707                        MM_HEALTH(game_mm.laser_overload_value) :
4708                        game.health);
4709
4710   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4711   game.LevelSolved_CountingScore = game.score_final;
4712   game.LevelSolved_CountingHealth = game.health_final;
4713 }
4714
4715 void GameWon(void)
4716 {
4717   static int time_count_steps;
4718   static int time, time_final;
4719   static float score, score_final; // needed for time score < 10 for 10 seconds
4720   static int health, health_final;
4721   static int game_over_delay_1 = 0;
4722   static int game_over_delay_2 = 0;
4723   static int game_over_delay_3 = 0;
4724   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4725   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4726
4727   if (!game.LevelSolved_GameWon)
4728   {
4729     int i;
4730
4731     // do not start end game actions before the player stops moving (to exit)
4732     if (local_player->active && local_player->MovPos)
4733       return;
4734
4735     game.LevelSolved_GameWon = TRUE;
4736     game.LevelSolved_SaveTape = tape.recording;
4737     game.LevelSolved_SaveScore = !tape.playing;
4738
4739     if (!tape.playing)
4740     {
4741       LevelStats_incSolved(level_nr);
4742
4743       SaveLevelSetup_SeriesInfo();
4744     }
4745
4746     if (tape.auto_play)         // tape might already be stopped here
4747       tape.auto_play_level_solved = TRUE;
4748
4749     TapeStop();
4750
4751     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4752     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4753     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4754
4755     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4756     score = score_final = game.score_final;
4757     health = health_final = game.health_final;
4758
4759     if (time_score > 0)
4760     {
4761       int time_frames = 0;
4762
4763       if (TimeLeft > 0)
4764       {
4765         time_final = 0;
4766         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4767       }
4768       else if (game.no_time_limit && TimePlayed < 999)
4769       {
4770         time_final = 999;
4771         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4772       }
4773
4774       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4775
4776       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4777
4778       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4779       {
4780         health_final = 0;
4781         score_final += health * time_score;
4782       }
4783
4784       game.score_final = score_final;
4785       game.health_final = health_final;
4786     }
4787
4788     if (level_editor_test_game || !setup.count_score_after_game)
4789     {
4790       time = time_final;
4791       score = score_final;
4792
4793       game.LevelSolved_CountingTime = time;
4794       game.LevelSolved_CountingScore = score;
4795
4796       game_panel_controls[GAME_PANEL_TIME].value = time;
4797       game_panel_controls[GAME_PANEL_SCORE].value = score;
4798
4799       DisplayGameControlValues();
4800     }
4801
4802     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4803     {
4804       // check if last player has left the level
4805       if (game.exit_x >= 0 &&
4806           game.exit_y >= 0)
4807       {
4808         int x = game.exit_x;
4809         int y = game.exit_y;
4810         int element = Tile[x][y];
4811
4812         // close exit door after last player
4813         if ((game.all_players_gone &&
4814              (element == EL_EXIT_OPEN ||
4815               element == EL_SP_EXIT_OPEN ||
4816               element == EL_STEEL_EXIT_OPEN)) ||
4817             element == EL_EM_EXIT_OPEN ||
4818             element == EL_EM_STEEL_EXIT_OPEN)
4819         {
4820
4821           Tile[x][y] =
4822             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4823              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4824              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4825              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4826              EL_EM_STEEL_EXIT_CLOSING);
4827
4828           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4829         }
4830
4831         // player disappears
4832         DrawLevelField(x, y);
4833       }
4834
4835       for (i = 0; i < MAX_PLAYERS; i++)
4836       {
4837         struct PlayerInfo *player = &stored_player[i];
4838
4839         if (player->present)
4840         {
4841           RemovePlayer(player);
4842
4843           // player disappears
4844           DrawLevelField(player->jx, player->jy);
4845         }
4846       }
4847     }
4848
4849     PlaySound(SND_GAME_WINNING);
4850   }
4851
4852   if (setup.count_score_after_game)
4853   {
4854     if (time != time_final)
4855     {
4856       if (game_over_delay_1 > 0)
4857       {
4858         game_over_delay_1--;
4859
4860         return;
4861       }
4862
4863       int time_to_go = ABS(time_final - time);
4864       int time_count_dir = (time < time_final ? +1 : -1);
4865
4866       if (time_to_go < time_count_steps)
4867         time_count_steps = 1;
4868
4869       time  += time_count_steps * time_count_dir;
4870       score += time_count_steps * time_score;
4871
4872       // set final score to correct rounding differences after counting score
4873       if (time == time_final)
4874         score = score_final;
4875
4876       game.LevelSolved_CountingTime = time;
4877       game.LevelSolved_CountingScore = score;
4878
4879       game_panel_controls[GAME_PANEL_TIME].value = time;
4880       game_panel_controls[GAME_PANEL_SCORE].value = score;
4881
4882       DisplayGameControlValues();
4883
4884       if (time == time_final)
4885         StopSound(SND_GAME_LEVELTIME_BONUS);
4886       else if (setup.sound_loops)
4887         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4888       else
4889         PlaySound(SND_GAME_LEVELTIME_BONUS);
4890
4891       return;
4892     }
4893
4894     if (health != health_final)
4895     {
4896       if (game_over_delay_2 > 0)
4897       {
4898         game_over_delay_2--;
4899
4900         return;
4901       }
4902
4903       int health_count_dir = (health < health_final ? +1 : -1);
4904
4905       health += health_count_dir;
4906       score  += time_score;
4907
4908       game.LevelSolved_CountingHealth = health;
4909       game.LevelSolved_CountingScore = score;
4910
4911       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4912       game_panel_controls[GAME_PANEL_SCORE].value = score;
4913
4914       DisplayGameControlValues();
4915
4916       if (health == health_final)
4917         StopSound(SND_GAME_LEVELTIME_BONUS);
4918       else if (setup.sound_loops)
4919         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4920       else
4921         PlaySound(SND_GAME_LEVELTIME_BONUS);
4922
4923       return;
4924     }
4925   }
4926
4927   game.panel.active = FALSE;
4928
4929   if (game_over_delay_3 > 0)
4930   {
4931     game_over_delay_3--;
4932
4933     return;
4934   }
4935
4936   GameEnd();
4937 }
4938
4939 void GameEnd(void)
4940 {
4941   // used instead of "level_nr" (needed for network games)
4942   int last_level_nr = levelset.level_nr;
4943   int hi_pos;
4944
4945   game.LevelSolved_GameEnd = TRUE;
4946
4947   if (game.LevelSolved_SaveTape)
4948   {
4949     // make sure that request dialog to save tape does not open door again
4950     if (!global.use_envelope_request)
4951       CloseDoor(DOOR_CLOSE_1);
4952
4953     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4954   }
4955
4956   // if no tape is to be saved, close both doors simultaneously
4957   CloseDoor(DOOR_CLOSE_ALL);
4958
4959   if (level_editor_test_game)
4960   {
4961     SetGameStatus(GAME_MODE_MAIN);
4962
4963     DrawMainMenu();
4964
4965     return;
4966   }
4967
4968   if (!game.LevelSolved_SaveScore)
4969   {
4970     SetGameStatus(GAME_MODE_MAIN);
4971
4972     DrawMainMenu();
4973
4974     return;
4975   }
4976
4977   if (level_nr == leveldir_current->handicap_level)
4978   {
4979     leveldir_current->handicap_level++;
4980
4981     SaveLevelSetup_SeriesInfo();
4982   }
4983
4984   if (setup.increment_levels &&
4985       level_nr < leveldir_current->last_level &&
4986       !network_playing)
4987   {
4988     level_nr++;         // advance to next level
4989     TapeErase();        // start with empty tape
4990
4991     if (setup.auto_play_next_level)
4992     {
4993       LoadLevel(level_nr);
4994
4995       SaveLevelSetup_SeriesInfo();
4996     }
4997   }
4998
4999   hi_pos = NewHiScore(last_level_nr);
5000
5001   if (hi_pos >= 0 && setup.show_scores_after_game)
5002   {
5003     SetGameStatus(GAME_MODE_SCORES);
5004
5005     DrawHallOfFame(last_level_nr, hi_pos);
5006   }
5007   else if (setup.auto_play_next_level && setup.increment_levels &&
5008            last_level_nr < leveldir_current->last_level &&
5009            !network_playing)
5010   {
5011     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5012   }
5013   else
5014   {
5015     SetGameStatus(GAME_MODE_MAIN);
5016
5017     DrawMainMenu();
5018   }
5019 }
5020
5021 int NewHiScore(int level_nr)
5022 {
5023   int k, l;
5024   int position = -1;
5025   boolean one_score_entry_per_name = !program.many_scores_per_name;
5026
5027   LoadScore(level_nr);
5028
5029   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5030       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5031     return -1;
5032
5033   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5034   {
5035     if (game.score_final > highscore[k].Score)
5036     {
5037       // player has made it to the hall of fame
5038
5039       if (k < MAX_SCORE_ENTRIES - 1)
5040       {
5041         int m = MAX_SCORE_ENTRIES - 1;
5042
5043         if (one_score_entry_per_name)
5044         {
5045           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5046             if (strEqual(setup.player_name, highscore[l].Name))
5047               m = l;
5048
5049           if (m == k)   // player's new highscore overwrites his old one
5050             goto put_into_list;
5051         }
5052
5053         for (l = m; l > k; l--)
5054         {
5055           strcpy(highscore[l].Name, highscore[l - 1].Name);
5056           highscore[l].Score = highscore[l - 1].Score;
5057         }
5058       }
5059
5060       put_into_list:
5061
5062       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5063       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5064       highscore[k].Score = game.score_final;
5065       position = k;
5066
5067       break;
5068     }
5069     else if (one_score_entry_per_name &&
5070              !strncmp(setup.player_name, highscore[k].Name,
5071                       MAX_PLAYER_NAME_LEN))
5072       break;    // player already there with a higher score
5073   }
5074
5075   if (position >= 0) 
5076     SaveScore(level_nr);
5077
5078   return position;
5079 }
5080
5081 static int getElementMoveStepsizeExt(int x, int y, int direction)
5082 {
5083   int element = Tile[x][y];
5084   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5085   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5086   int horiz_move = (dx != 0);
5087   int sign = (horiz_move ? dx : dy);
5088   int step = sign * element_info[element].move_stepsize;
5089
5090   // special values for move stepsize for spring and things on conveyor belt
5091   if (horiz_move)
5092   {
5093     if (CAN_FALL(element) &&
5094         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5095       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5096     else if (element == EL_SPRING)
5097       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5098   }
5099
5100   return step;
5101 }
5102
5103 static int getElementMoveStepsize(int x, int y)
5104 {
5105   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5106 }
5107
5108 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5109 {
5110   if (player->GfxAction != action || player->GfxDir != dir)
5111   {
5112     player->GfxAction = action;
5113     player->GfxDir = dir;
5114     player->Frame = 0;
5115     player->StepFrame = 0;
5116   }
5117 }
5118
5119 static void ResetGfxFrame(int x, int y)
5120 {
5121   // profiling showed that "autotest" spends 10~20% of its time in this function
5122   if (DrawingDeactivatedField())
5123     return;
5124
5125   int element = Tile[x][y];
5126   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5127
5128   if (graphic_info[graphic].anim_global_sync)
5129     GfxFrame[x][y] = FrameCounter;
5130   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5131     GfxFrame[x][y] = CustomValue[x][y];
5132   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5133     GfxFrame[x][y] = element_info[element].collect_score;
5134   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5135     GfxFrame[x][y] = ChangeDelay[x][y];
5136 }
5137
5138 static void ResetGfxAnimation(int x, int y)
5139 {
5140   GfxAction[x][y] = ACTION_DEFAULT;
5141   GfxDir[x][y] = MovDir[x][y];
5142   GfxFrame[x][y] = 0;
5143
5144   ResetGfxFrame(x, y);
5145 }
5146
5147 static void ResetRandomAnimationValue(int x, int y)
5148 {
5149   GfxRandom[x][y] = INIT_GFX_RANDOM();
5150 }
5151
5152 static void InitMovingField(int x, int y, int direction)
5153 {
5154   int element = Tile[x][y];
5155   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5156   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5157   int newx = x + dx;
5158   int newy = y + dy;
5159   boolean is_moving_before, is_moving_after;
5160
5161   // check if element was/is moving or being moved before/after mode change
5162   is_moving_before = (WasJustMoving[x][y] != 0);
5163   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5164
5165   // reset animation only for moving elements which change direction of moving
5166   // or which just started or stopped moving
5167   // (else CEs with property "can move" / "not moving" are reset each frame)
5168   if (is_moving_before != is_moving_after ||
5169       direction != MovDir[x][y])
5170     ResetGfxAnimation(x, y);
5171
5172   MovDir[x][y] = direction;
5173   GfxDir[x][y] = direction;
5174
5175   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5176                      direction == MV_DOWN && CAN_FALL(element) ?
5177                      ACTION_FALLING : ACTION_MOVING);
5178
5179   // this is needed for CEs with property "can move" / "not moving"
5180
5181   if (is_moving_after)
5182   {
5183     if (Tile[newx][newy] == EL_EMPTY)
5184       Tile[newx][newy] = EL_BLOCKED;
5185
5186     MovDir[newx][newy] = MovDir[x][y];
5187
5188     CustomValue[newx][newy] = CustomValue[x][y];
5189
5190     GfxFrame[newx][newy] = GfxFrame[x][y];
5191     GfxRandom[newx][newy] = GfxRandom[x][y];
5192     GfxAction[newx][newy] = GfxAction[x][y];
5193     GfxDir[newx][newy] = GfxDir[x][y];
5194   }
5195 }
5196
5197 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5198 {
5199   int direction = MovDir[x][y];
5200   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5201   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5202
5203   *goes_to_x = newx;
5204   *goes_to_y = newy;
5205 }
5206
5207 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5208 {
5209   int oldx = x, oldy = y;
5210   int direction = MovDir[x][y];
5211
5212   if (direction == MV_LEFT)
5213     oldx++;
5214   else if (direction == MV_RIGHT)
5215     oldx--;
5216   else if (direction == MV_UP)
5217     oldy++;
5218   else if (direction == MV_DOWN)
5219     oldy--;
5220
5221   *comes_from_x = oldx;
5222   *comes_from_y = oldy;
5223 }
5224
5225 static int MovingOrBlocked2Element(int x, int y)
5226 {
5227   int element = Tile[x][y];
5228
5229   if (element == EL_BLOCKED)
5230   {
5231     int oldx, oldy;
5232
5233     Blocked2Moving(x, y, &oldx, &oldy);
5234     return Tile[oldx][oldy];
5235   }
5236   else
5237     return element;
5238 }
5239
5240 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5241 {
5242   // like MovingOrBlocked2Element(), but if element is moving
5243   // and (x,y) is the field the moving element is just leaving,
5244   // return EL_BLOCKED instead of the element value
5245   int element = Tile[x][y];
5246
5247   if (IS_MOVING(x, y))
5248   {
5249     if (element == EL_BLOCKED)
5250     {
5251       int oldx, oldy;
5252
5253       Blocked2Moving(x, y, &oldx, &oldy);
5254       return Tile[oldx][oldy];
5255     }
5256     else
5257       return EL_BLOCKED;
5258   }
5259   else
5260     return element;
5261 }
5262
5263 static void RemoveField(int x, int y)
5264 {
5265   Tile[x][y] = EL_EMPTY;
5266
5267   MovPos[x][y] = 0;
5268   MovDir[x][y] = 0;
5269   MovDelay[x][y] = 0;
5270
5271   CustomValue[x][y] = 0;
5272
5273   AmoebaNr[x][y] = 0;
5274   ChangeDelay[x][y] = 0;
5275   ChangePage[x][y] = -1;
5276   Pushed[x][y] = FALSE;
5277
5278   GfxElement[x][y] = EL_UNDEFINED;
5279   GfxAction[x][y] = ACTION_DEFAULT;
5280   GfxDir[x][y] = MV_NONE;
5281 }
5282
5283 static void RemoveMovingField(int x, int y)
5284 {
5285   int oldx = x, oldy = y, newx = x, newy = y;
5286   int element = Tile[x][y];
5287   int next_element = EL_UNDEFINED;
5288
5289   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5290     return;
5291
5292   if (IS_MOVING(x, y))
5293   {
5294     Moving2Blocked(x, y, &newx, &newy);
5295
5296     if (Tile[newx][newy] != EL_BLOCKED)
5297     {
5298       // element is moving, but target field is not free (blocked), but
5299       // already occupied by something different (example: acid pool);
5300       // in this case, only remove the moving field, but not the target
5301
5302       RemoveField(oldx, oldy);
5303
5304       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5305
5306       TEST_DrawLevelField(oldx, oldy);
5307
5308       return;
5309     }
5310   }
5311   else if (element == EL_BLOCKED)
5312   {
5313     Blocked2Moving(x, y, &oldx, &oldy);
5314     if (!IS_MOVING(oldx, oldy))
5315       return;
5316   }
5317
5318   if (element == EL_BLOCKED &&
5319       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5320        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5321        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5322        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5323        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5324        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5325     next_element = get_next_element(Tile[oldx][oldy]);
5326
5327   RemoveField(oldx, oldy);
5328   RemoveField(newx, newy);
5329
5330   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5331
5332   if (next_element != EL_UNDEFINED)
5333     Tile[oldx][oldy] = next_element;
5334
5335   TEST_DrawLevelField(oldx, oldy);
5336   TEST_DrawLevelField(newx, newy);
5337 }
5338
5339 void DrawDynamite(int x, int y)
5340 {
5341   int sx = SCREENX(x), sy = SCREENY(y);
5342   int graphic = el2img(Tile[x][y]);
5343   int frame;
5344
5345   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5346     return;
5347
5348   if (IS_WALKABLE_INSIDE(Back[x][y]))
5349     return;
5350
5351   if (Back[x][y])
5352     DrawLevelElement(x, y, Back[x][y]);
5353   else if (Store[x][y])
5354     DrawLevelElement(x, y, Store[x][y]);
5355   else if (game.use_masked_elements)
5356     DrawLevelElement(x, y, EL_EMPTY);
5357
5358   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5359
5360   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5361     DrawGraphicThruMask(sx, sy, graphic, frame);
5362   else
5363     DrawGraphic(sx, sy, graphic, frame);
5364 }
5365
5366 static void CheckDynamite(int x, int y)
5367 {
5368   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5369   {
5370     MovDelay[x][y]--;
5371
5372     if (MovDelay[x][y] != 0)
5373     {
5374       DrawDynamite(x, y);
5375       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5376
5377       return;
5378     }
5379   }
5380
5381   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5382
5383   Bang(x, y);
5384 }
5385
5386 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5387 {
5388   boolean num_checked_players = 0;
5389   int i;
5390
5391   for (i = 0; i < MAX_PLAYERS; i++)
5392   {
5393     if (stored_player[i].active)
5394     {
5395       int sx = stored_player[i].jx;
5396       int sy = stored_player[i].jy;
5397
5398       if (num_checked_players == 0)
5399       {
5400         *sx1 = *sx2 = sx;
5401         *sy1 = *sy2 = sy;
5402       }
5403       else
5404       {
5405         *sx1 = MIN(*sx1, sx);
5406         *sy1 = MIN(*sy1, sy);
5407         *sx2 = MAX(*sx2, sx);
5408         *sy2 = MAX(*sy2, sy);
5409       }
5410
5411       num_checked_players++;
5412     }
5413   }
5414 }
5415
5416 static boolean checkIfAllPlayersFitToScreen_RND(void)
5417 {
5418   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5419
5420   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5421
5422   return (sx2 - sx1 < SCR_FIELDX &&
5423           sy2 - sy1 < SCR_FIELDY);
5424 }
5425
5426 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5427 {
5428   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5429
5430   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5431
5432   *sx = (sx1 + sx2) / 2;
5433   *sy = (sy1 + sy2) / 2;
5434 }
5435
5436 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5437                                boolean center_screen, boolean quick_relocation)
5438 {
5439   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5440   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5441   boolean no_delay = (tape.warp_forward);
5442   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5443   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5444   int new_scroll_x, new_scroll_y;
5445
5446   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5447   {
5448     // case 1: quick relocation inside visible screen (without scrolling)
5449
5450     RedrawPlayfield();
5451
5452     return;
5453   }
5454
5455   if (!level.shifted_relocation || center_screen)
5456   {
5457     // relocation _with_ centering of screen
5458
5459     new_scroll_x = SCROLL_POSITION_X(x);
5460     new_scroll_y = SCROLL_POSITION_Y(y);
5461   }
5462   else
5463   {
5464     // relocation _without_ centering of screen
5465
5466     int center_scroll_x = SCROLL_POSITION_X(old_x);
5467     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5468     int offset_x = x + (scroll_x - center_scroll_x);
5469     int offset_y = y + (scroll_y - center_scroll_y);
5470
5471     // for new screen position, apply previous offset to center position
5472     new_scroll_x = SCROLL_POSITION_X(offset_x);
5473     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5474   }
5475
5476   if (quick_relocation)
5477   {
5478     // case 2: quick relocation (redraw without visible scrolling)
5479
5480     scroll_x = new_scroll_x;
5481     scroll_y = new_scroll_y;
5482
5483     RedrawPlayfield();
5484
5485     return;
5486   }
5487
5488   // case 3: visible relocation (with scrolling to new position)
5489
5490   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5491
5492   SetVideoFrameDelay(wait_delay_value);
5493
5494   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5495   {
5496     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5497     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5498
5499     if (dx == 0 && dy == 0)             // no scrolling needed at all
5500       break;
5501
5502     scroll_x -= dx;
5503     scroll_y -= dy;
5504
5505     // set values for horizontal/vertical screen scrolling (half tile size)
5506     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5507     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5508     int pos_x = dx * TILEX / 2;
5509     int pos_y = dy * TILEY / 2;
5510     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5511     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5512
5513     ScrollLevel(dx, dy);
5514     DrawAllPlayers();
5515
5516     // scroll in two steps of half tile size to make things smoother
5517     BlitScreenToBitmapExt_RND(window, fx, fy);
5518
5519     // scroll second step to align at full tile size
5520     BlitScreenToBitmap(window);
5521   }
5522
5523   DrawAllPlayers();
5524   BackToFront();
5525
5526   SetVideoFrameDelay(frame_delay_value_old);
5527 }
5528
5529 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5530 {
5531   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5532   int player_nr = GET_PLAYER_NR(el_player);
5533   struct PlayerInfo *player = &stored_player[player_nr];
5534   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5535   boolean no_delay = (tape.warp_forward);
5536   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5537   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5538   int old_jx = player->jx;
5539   int old_jy = player->jy;
5540   int old_element = Tile[old_jx][old_jy];
5541   int element = Tile[jx][jy];
5542   boolean player_relocated = (old_jx != jx || old_jy != jy);
5543
5544   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5545   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5546   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5547   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5548   int leave_side_horiz = move_dir_horiz;
5549   int leave_side_vert  = move_dir_vert;
5550   int enter_side = enter_side_horiz | enter_side_vert;
5551   int leave_side = leave_side_horiz | leave_side_vert;
5552
5553   if (player->buried)           // do not reanimate dead player
5554     return;
5555
5556   if (!player_relocated)        // no need to relocate the player
5557     return;
5558
5559   if (IS_PLAYER(jx, jy))        // player already placed at new position
5560   {
5561     RemoveField(jx, jy);        // temporarily remove newly placed player
5562     DrawLevelField(jx, jy);
5563   }
5564
5565   if (player->present)
5566   {
5567     while (player->MovPos)
5568     {
5569       ScrollPlayer(player, SCROLL_GO_ON);
5570       ScrollScreen(NULL, SCROLL_GO_ON);
5571
5572       AdvanceFrameAndPlayerCounters(player->index_nr);
5573
5574       DrawPlayer(player);
5575
5576       BackToFront_WithFrameDelay(wait_delay_value);
5577     }
5578
5579     DrawPlayer(player);         // needed here only to cleanup last field
5580     DrawLevelField(player->jx, player->jy);     // remove player graphic
5581
5582     player->is_moving = FALSE;
5583   }
5584
5585   if (IS_CUSTOM_ELEMENT(old_element))
5586     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5587                                CE_LEFT_BY_PLAYER,
5588                                player->index_bit, leave_side);
5589
5590   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5591                                       CE_PLAYER_LEAVES_X,
5592                                       player->index_bit, leave_side);
5593
5594   Tile[jx][jy] = el_player;
5595   InitPlayerField(jx, jy, el_player, TRUE);
5596
5597   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5598      possible that the relocation target field did not contain a player element,
5599      but a walkable element, to which the new player was relocated -- in this
5600      case, restore that (already initialized!) element on the player field */
5601   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5602   {
5603     Tile[jx][jy] = element;     // restore previously existing element
5604   }
5605
5606   // only visually relocate centered player
5607   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5608                      FALSE, level.instant_relocation);
5609
5610   TestIfPlayerTouchesBadThing(jx, jy);
5611   TestIfPlayerTouchesCustomElement(jx, jy);
5612
5613   if (IS_CUSTOM_ELEMENT(element))
5614     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5615                                player->index_bit, enter_side);
5616
5617   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5618                                       player->index_bit, enter_side);
5619
5620   if (player->is_switching)
5621   {
5622     /* ensure that relocation while still switching an element does not cause
5623        a new element to be treated as also switched directly after relocation
5624        (this is important for teleporter switches that teleport the player to
5625        a place where another teleporter switch is in the same direction, which
5626        would then incorrectly be treated as immediately switched before the
5627        direction key that caused the switch was released) */
5628
5629     player->switch_x += jx - old_jx;
5630     player->switch_y += jy - old_jy;
5631   }
5632 }
5633
5634 static void Explode(int ex, int ey, int phase, int mode)
5635 {
5636   int x, y;
5637   int last_phase;
5638   int border_element;
5639
5640   // !!! eliminate this variable !!!
5641   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5642
5643   if (game.explosions_delayed)
5644   {
5645     ExplodeField[ex][ey] = mode;
5646     return;
5647   }
5648
5649   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5650   {
5651     int center_element = Tile[ex][ey];
5652     int artwork_element, explosion_element;     // set these values later
5653
5654     // remove things displayed in background while burning dynamite
5655     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5656       Back[ex][ey] = 0;
5657
5658     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5659     {
5660       // put moving element to center field (and let it explode there)
5661       center_element = MovingOrBlocked2Element(ex, ey);
5662       RemoveMovingField(ex, ey);
5663       Tile[ex][ey] = center_element;
5664     }
5665
5666     // now "center_element" is finally determined -- set related values now
5667     artwork_element = center_element;           // for custom player artwork
5668     explosion_element = center_element;         // for custom player artwork
5669
5670     if (IS_PLAYER(ex, ey))
5671     {
5672       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5673
5674       artwork_element = stored_player[player_nr].artwork_element;
5675
5676       if (level.use_explosion_element[player_nr])
5677       {
5678         explosion_element = level.explosion_element[player_nr];
5679         artwork_element = explosion_element;
5680       }
5681     }
5682
5683     if (mode == EX_TYPE_NORMAL ||
5684         mode == EX_TYPE_CENTER ||
5685         mode == EX_TYPE_CROSS)
5686       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5687
5688     last_phase = element_info[explosion_element].explosion_delay + 1;
5689
5690     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5691     {
5692       int xx = x - ex + 1;
5693       int yy = y - ey + 1;
5694       int element;
5695
5696       if (!IN_LEV_FIELD(x, y) ||
5697           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5698           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5699         continue;
5700
5701       element = Tile[x][y];
5702
5703       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5704       {
5705         element = MovingOrBlocked2Element(x, y);
5706
5707         if (!IS_EXPLOSION_PROOF(element))
5708           RemoveMovingField(x, y);
5709       }
5710
5711       // indestructible elements can only explode in center (but not flames)
5712       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5713                                            mode == EX_TYPE_BORDER)) ||
5714           element == EL_FLAMES)
5715         continue;
5716
5717       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5718          behaviour, for example when touching a yamyam that explodes to rocks
5719          with active deadly shield, a rock is created under the player !!! */
5720       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5721 #if 0
5722       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5723           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5724            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5725 #else
5726       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5727 #endif
5728       {
5729         if (IS_ACTIVE_BOMB(element))
5730         {
5731           // re-activate things under the bomb like gate or penguin
5732           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5733           Back[x][y] = 0;
5734         }
5735
5736         continue;
5737       }
5738
5739       // save walkable background elements while explosion on same tile
5740       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5741           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5742         Back[x][y] = element;
5743
5744       // ignite explodable elements reached by other explosion
5745       if (element == EL_EXPLOSION)
5746         element = Store2[x][y];
5747
5748       if (AmoebaNr[x][y] &&
5749           (element == EL_AMOEBA_FULL ||
5750            element == EL_BD_AMOEBA ||
5751            element == EL_AMOEBA_GROWING))
5752       {
5753         AmoebaCnt[AmoebaNr[x][y]]--;
5754         AmoebaCnt2[AmoebaNr[x][y]]--;
5755       }
5756
5757       RemoveField(x, y);
5758
5759       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5760       {
5761         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5762
5763         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5764
5765         if (PLAYERINFO(ex, ey)->use_murphy)
5766           Store[x][y] = EL_EMPTY;
5767       }
5768
5769       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5770       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5771       else if (ELEM_IS_PLAYER(center_element))
5772         Store[x][y] = EL_EMPTY;
5773       else if (center_element == EL_YAMYAM)
5774         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5775       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5776         Store[x][y] = element_info[center_element].content.e[xx][yy];
5777 #if 1
5778       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5779       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5780       // otherwise) -- FIX THIS !!!
5781       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5782         Store[x][y] = element_info[element].content.e[1][1];
5783 #else
5784       else if (!CAN_EXPLODE(element))
5785         Store[x][y] = element_info[element].content.e[1][1];
5786 #endif
5787       else
5788         Store[x][y] = EL_EMPTY;
5789
5790       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5791           center_element == EL_AMOEBA_TO_DIAMOND)
5792         Store2[x][y] = element;
5793
5794       Tile[x][y] = EL_EXPLOSION;
5795       GfxElement[x][y] = artwork_element;
5796
5797       ExplodePhase[x][y] = 1;
5798       ExplodeDelay[x][y] = last_phase;
5799
5800       Stop[x][y] = TRUE;
5801     }
5802
5803     if (center_element == EL_YAMYAM)
5804       game.yamyam_content_nr =
5805         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5806
5807     return;
5808   }
5809
5810   if (Stop[ex][ey])
5811     return;
5812
5813   x = ex;
5814   y = ey;
5815
5816   if (phase == 1)
5817     GfxFrame[x][y] = 0;         // restart explosion animation
5818
5819   last_phase = ExplodeDelay[x][y];
5820
5821   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5822
5823   // this can happen if the player leaves an explosion just in time
5824   if (GfxElement[x][y] == EL_UNDEFINED)
5825     GfxElement[x][y] = EL_EMPTY;
5826
5827   border_element = Store2[x][y];
5828   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5829     border_element = StorePlayer[x][y];
5830
5831   if (phase == element_info[border_element].ignition_delay ||
5832       phase == last_phase)
5833   {
5834     boolean border_explosion = FALSE;
5835
5836     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5837         !PLAYER_EXPLOSION_PROTECTED(x, y))
5838     {
5839       KillPlayerUnlessExplosionProtected(x, y);
5840       border_explosion = TRUE;
5841     }
5842     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5843     {
5844       Tile[x][y] = Store2[x][y];
5845       Store2[x][y] = 0;
5846       Bang(x, y);
5847       border_explosion = TRUE;
5848     }
5849     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5850     {
5851       AmoebaToDiamond(x, y);
5852       Store2[x][y] = 0;
5853       border_explosion = TRUE;
5854     }
5855
5856     // if an element just explodes due to another explosion (chain-reaction),
5857     // do not immediately end the new explosion when it was the last frame of
5858     // the explosion (as it would be done in the following "if"-statement!)
5859     if (border_explosion && phase == last_phase)
5860       return;
5861   }
5862
5863   if (phase == last_phase)
5864   {
5865     int element;
5866
5867     element = Tile[x][y] = Store[x][y];
5868     Store[x][y] = Store2[x][y] = 0;
5869     GfxElement[x][y] = EL_UNDEFINED;
5870
5871     // player can escape from explosions and might therefore be still alive
5872     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5873         element <= EL_PLAYER_IS_EXPLODING_4)
5874     {
5875       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5876       int explosion_element = EL_PLAYER_1 + player_nr;
5877       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5878       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5879
5880       if (level.use_explosion_element[player_nr])
5881         explosion_element = level.explosion_element[player_nr];
5882
5883       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5884                     element_info[explosion_element].content.e[xx][yy]);
5885     }
5886
5887     // restore probably existing indestructible background element
5888     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5889       element = Tile[x][y] = Back[x][y];
5890     Back[x][y] = 0;
5891
5892     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5893     GfxDir[x][y] = MV_NONE;
5894     ChangeDelay[x][y] = 0;
5895     ChangePage[x][y] = -1;
5896
5897     CustomValue[x][y] = 0;
5898
5899     InitField_WithBug2(x, y, FALSE);
5900
5901     TEST_DrawLevelField(x, y);
5902
5903     TestIfElementTouchesCustomElement(x, y);
5904
5905     if (GFX_CRUMBLED(element))
5906       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5907
5908     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5909       StorePlayer[x][y] = 0;
5910
5911     if (ELEM_IS_PLAYER(element))
5912       RelocatePlayer(x, y, element);
5913   }
5914   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5915   {
5916     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5917     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5918
5919     if (phase == delay)
5920       TEST_DrawLevelFieldCrumbled(x, y);
5921
5922     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5923     {
5924       DrawLevelElement(x, y, Back[x][y]);
5925       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5926     }
5927     else if (IS_WALKABLE_UNDER(Back[x][y]))
5928     {
5929       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5930       DrawLevelElementThruMask(x, y, Back[x][y]);
5931     }
5932     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5933       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934   }
5935 }
5936
5937 static void DynaExplode(int ex, int ey)
5938 {
5939   int i, j;
5940   int dynabomb_element = Tile[ex][ey];
5941   int dynabomb_size = 1;
5942   boolean dynabomb_xl = FALSE;
5943   struct PlayerInfo *player;
5944   static int xy[4][2] =
5945   {
5946     { 0, -1 },
5947     { -1, 0 },
5948     { +1, 0 },
5949     { 0, +1 }
5950   };
5951
5952   if (IS_ACTIVE_BOMB(dynabomb_element))
5953   {
5954     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5955     dynabomb_size = player->dynabomb_size;
5956     dynabomb_xl = player->dynabomb_xl;
5957     player->dynabombs_left++;
5958   }
5959
5960   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5961
5962   for (i = 0; i < NUM_DIRECTIONS; i++)
5963   {
5964     for (j = 1; j <= dynabomb_size; j++)
5965     {
5966       int x = ex + j * xy[i][0];
5967       int y = ey + j * xy[i][1];
5968       int element;
5969
5970       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5971         break;
5972
5973       element = Tile[x][y];
5974
5975       // do not restart explosions of fields with active bombs
5976       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5977         continue;
5978
5979       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5980
5981       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5982           !IS_DIGGABLE(element) && !dynabomb_xl)
5983         break;
5984     }
5985   }
5986 }
5987
5988 void Bang(int x, int y)
5989 {
5990   int element = MovingOrBlocked2Element(x, y);
5991   int explosion_type = EX_TYPE_NORMAL;
5992
5993   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5994   {
5995     struct PlayerInfo *player = PLAYERINFO(x, y);
5996
5997     element = Tile[x][y] = player->initial_element;
5998
5999     if (level.use_explosion_element[player->index_nr])
6000     {
6001       int explosion_element = level.explosion_element[player->index_nr];
6002
6003       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6004         explosion_type = EX_TYPE_CROSS;
6005       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6006         explosion_type = EX_TYPE_CENTER;
6007     }
6008   }
6009
6010   switch (element)
6011   {
6012     case EL_BUG:
6013     case EL_SPACESHIP:
6014     case EL_BD_BUTTERFLY:
6015     case EL_BD_FIREFLY:
6016     case EL_YAMYAM:
6017     case EL_DARK_YAMYAM:
6018     case EL_ROBOT:
6019     case EL_PACMAN:
6020     case EL_MOLE:
6021       RaiseScoreElement(element);
6022       break;
6023
6024     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6025     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6026     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6027     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6028     case EL_DYNABOMB_INCREASE_NUMBER:
6029     case EL_DYNABOMB_INCREASE_SIZE:
6030     case EL_DYNABOMB_INCREASE_POWER:
6031       explosion_type = EX_TYPE_DYNA;
6032       break;
6033
6034     case EL_DC_LANDMINE:
6035       explosion_type = EX_TYPE_CENTER;
6036       break;
6037
6038     case EL_PENGUIN:
6039     case EL_LAMP:
6040     case EL_LAMP_ACTIVE:
6041     case EL_AMOEBA_TO_DIAMOND:
6042       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6043         explosion_type = EX_TYPE_CENTER;
6044       break;
6045
6046     default:
6047       if (element_info[element].explosion_type == EXPLODES_CROSS)
6048         explosion_type = EX_TYPE_CROSS;
6049       else if (element_info[element].explosion_type == EXPLODES_1X1)
6050         explosion_type = EX_TYPE_CENTER;
6051       break;
6052   }
6053
6054   if (explosion_type == EX_TYPE_DYNA)
6055     DynaExplode(x, y);
6056   else
6057     Explode(x, y, EX_PHASE_START, explosion_type);
6058
6059   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6060 }
6061
6062 static void SplashAcid(int x, int y)
6063 {
6064   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6065       (!IN_LEV_FIELD(x - 1, y - 2) ||
6066        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6067     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6068
6069   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6070       (!IN_LEV_FIELD(x + 1, y - 2) ||
6071        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6072     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6073
6074   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6075 }
6076
6077 static void InitBeltMovement(void)
6078 {
6079   static int belt_base_element[4] =
6080   {
6081     EL_CONVEYOR_BELT_1_LEFT,
6082     EL_CONVEYOR_BELT_2_LEFT,
6083     EL_CONVEYOR_BELT_3_LEFT,
6084     EL_CONVEYOR_BELT_4_LEFT
6085   };
6086   static int belt_base_active_element[4] =
6087   {
6088     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6089     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6090     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6091     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6092   };
6093
6094   int x, y, i, j;
6095
6096   // set frame order for belt animation graphic according to belt direction
6097   for (i = 0; i < NUM_BELTS; i++)
6098   {
6099     int belt_nr = i;
6100
6101     for (j = 0; j < NUM_BELT_PARTS; j++)
6102     {
6103       int element = belt_base_active_element[belt_nr] + j;
6104       int graphic_1 = el2img(element);
6105       int graphic_2 = el2panelimg(element);
6106
6107       if (game.belt_dir[i] == MV_LEFT)
6108       {
6109         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6110         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6111       }
6112       else
6113       {
6114         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6115         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6116       }
6117     }
6118   }
6119
6120   SCAN_PLAYFIELD(x, y)
6121   {
6122     int element = Tile[x][y];
6123
6124     for (i = 0; i < NUM_BELTS; i++)
6125     {
6126       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6127       {
6128         int e_belt_nr = getBeltNrFromBeltElement(element);
6129         int belt_nr = i;
6130
6131         if (e_belt_nr == belt_nr)
6132         {
6133           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6134
6135           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6136         }
6137       }
6138     }
6139   }
6140 }
6141
6142 static void ToggleBeltSwitch(int x, int y)
6143 {
6144   static int belt_base_element[4] =
6145   {
6146     EL_CONVEYOR_BELT_1_LEFT,
6147     EL_CONVEYOR_BELT_2_LEFT,
6148     EL_CONVEYOR_BELT_3_LEFT,
6149     EL_CONVEYOR_BELT_4_LEFT
6150   };
6151   static int belt_base_active_element[4] =
6152   {
6153     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6154     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6155     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6156     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6157   };
6158   static int belt_base_switch_element[4] =
6159   {
6160     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6161     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6162     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6163     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6164   };
6165   static int belt_move_dir[4] =
6166   {
6167     MV_LEFT,
6168     MV_NONE,
6169     MV_RIGHT,
6170     MV_NONE,
6171   };
6172
6173   int element = Tile[x][y];
6174   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6175   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6176   int belt_dir = belt_move_dir[belt_dir_nr];
6177   int xx, yy, i;
6178
6179   if (!IS_BELT_SWITCH(element))
6180     return;
6181
6182   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6183   game.belt_dir[belt_nr] = belt_dir;
6184
6185   if (belt_dir_nr == 3)
6186     belt_dir_nr = 1;
6187
6188   // set frame order for belt animation graphic according to belt direction
6189   for (i = 0; i < NUM_BELT_PARTS; i++)
6190   {
6191     int element = belt_base_active_element[belt_nr] + i;
6192     int graphic_1 = el2img(element);
6193     int graphic_2 = el2panelimg(element);
6194
6195     if (belt_dir == MV_LEFT)
6196     {
6197       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6198       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6199     }
6200     else
6201     {
6202       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6203       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6204     }
6205   }
6206
6207   SCAN_PLAYFIELD(xx, yy)
6208   {
6209     int element = Tile[xx][yy];
6210
6211     if (IS_BELT_SWITCH(element))
6212     {
6213       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6214
6215       if (e_belt_nr == belt_nr)
6216       {
6217         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6218         TEST_DrawLevelField(xx, yy);
6219       }
6220     }
6221     else if (IS_BELT(element) && belt_dir != MV_NONE)
6222     {
6223       int e_belt_nr = getBeltNrFromBeltElement(element);
6224
6225       if (e_belt_nr == belt_nr)
6226       {
6227         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6228
6229         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6230         TEST_DrawLevelField(xx, yy);
6231       }
6232     }
6233     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6234     {
6235       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6236
6237       if (e_belt_nr == belt_nr)
6238       {
6239         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6240
6241         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6242         TEST_DrawLevelField(xx, yy);
6243       }
6244     }
6245   }
6246 }
6247
6248 static void ToggleSwitchgateSwitch(int x, int y)
6249 {
6250   int xx, yy;
6251
6252   game.switchgate_pos = !game.switchgate_pos;
6253
6254   SCAN_PLAYFIELD(xx, yy)
6255   {
6256     int element = Tile[xx][yy];
6257
6258     if (element == EL_SWITCHGATE_SWITCH_UP)
6259     {
6260       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6261       TEST_DrawLevelField(xx, yy);
6262     }
6263     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6264     {
6265       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6266       TEST_DrawLevelField(xx, yy);
6267     }
6268     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6269     {
6270       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6271       TEST_DrawLevelField(xx, yy);
6272     }
6273     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6274     {
6275       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6276       TEST_DrawLevelField(xx, yy);
6277     }
6278     else if (element == EL_SWITCHGATE_OPEN ||
6279              element == EL_SWITCHGATE_OPENING)
6280     {
6281       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6282
6283       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6284     }
6285     else if (element == EL_SWITCHGATE_CLOSED ||
6286              element == EL_SWITCHGATE_CLOSING)
6287     {
6288       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6289
6290       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6291     }
6292   }
6293 }
6294
6295 static int getInvisibleActiveFromInvisibleElement(int element)
6296 {
6297   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6298           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6299           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6300           element);
6301 }
6302
6303 static int getInvisibleFromInvisibleActiveElement(int element)
6304 {
6305   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6306           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6307           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6308           element);
6309 }
6310
6311 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6312 {
6313   int x, y;
6314
6315   SCAN_PLAYFIELD(x, y)
6316   {
6317     int element = Tile[x][y];
6318
6319     if (element == EL_LIGHT_SWITCH &&
6320         game.light_time_left > 0)
6321     {
6322       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6323       TEST_DrawLevelField(x, y);
6324     }
6325     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6326              game.light_time_left == 0)
6327     {
6328       Tile[x][y] = EL_LIGHT_SWITCH;
6329       TEST_DrawLevelField(x, y);
6330     }
6331     else if (element == EL_EMC_DRIPPER &&
6332              game.light_time_left > 0)
6333     {
6334       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6335       TEST_DrawLevelField(x, y);
6336     }
6337     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6338              game.light_time_left == 0)
6339     {
6340       Tile[x][y] = EL_EMC_DRIPPER;
6341       TEST_DrawLevelField(x, y);
6342     }
6343     else if (element == EL_INVISIBLE_STEELWALL ||
6344              element == EL_INVISIBLE_WALL ||
6345              element == EL_INVISIBLE_SAND)
6346     {
6347       if (game.light_time_left > 0)
6348         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6349
6350       TEST_DrawLevelField(x, y);
6351
6352       // uncrumble neighbour fields, if needed
6353       if (element == EL_INVISIBLE_SAND)
6354         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6355     }
6356     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6357              element == EL_INVISIBLE_WALL_ACTIVE ||
6358              element == EL_INVISIBLE_SAND_ACTIVE)
6359     {
6360       if (game.light_time_left == 0)
6361         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6362
6363       TEST_DrawLevelField(x, y);
6364
6365       // re-crumble neighbour fields, if needed
6366       if (element == EL_INVISIBLE_SAND)
6367         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6368     }
6369   }
6370 }
6371
6372 static void RedrawAllInvisibleElementsForLenses(void)
6373 {
6374   int x, y;
6375
6376   SCAN_PLAYFIELD(x, y)
6377   {
6378     int element = Tile[x][y];
6379
6380     if (element == EL_EMC_DRIPPER &&
6381         game.lenses_time_left > 0)
6382     {
6383       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6384       TEST_DrawLevelField(x, y);
6385     }
6386     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6387              game.lenses_time_left == 0)
6388     {
6389       Tile[x][y] = EL_EMC_DRIPPER;
6390       TEST_DrawLevelField(x, y);
6391     }
6392     else if (element == EL_INVISIBLE_STEELWALL ||
6393              element == EL_INVISIBLE_WALL ||
6394              element == EL_INVISIBLE_SAND)
6395     {
6396       if (game.lenses_time_left > 0)
6397         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6398
6399       TEST_DrawLevelField(x, y);
6400
6401       // uncrumble neighbour fields, if needed
6402       if (element == EL_INVISIBLE_SAND)
6403         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6404     }
6405     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6406              element == EL_INVISIBLE_WALL_ACTIVE ||
6407              element == EL_INVISIBLE_SAND_ACTIVE)
6408     {
6409       if (game.lenses_time_left == 0)
6410         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6411
6412       TEST_DrawLevelField(x, y);
6413
6414       // re-crumble neighbour fields, if needed
6415       if (element == EL_INVISIBLE_SAND)
6416         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6417     }
6418   }
6419 }
6420
6421 static void RedrawAllInvisibleElementsForMagnifier(void)
6422 {
6423   int x, y;
6424
6425   SCAN_PLAYFIELD(x, y)
6426   {
6427     int element = Tile[x][y];
6428
6429     if (element == EL_EMC_FAKE_GRASS &&
6430         game.magnify_time_left > 0)
6431     {
6432       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6433       TEST_DrawLevelField(x, y);
6434     }
6435     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6436              game.magnify_time_left == 0)
6437     {
6438       Tile[x][y] = EL_EMC_FAKE_GRASS;
6439       TEST_DrawLevelField(x, y);
6440     }
6441     else if (IS_GATE_GRAY(element) &&
6442              game.magnify_time_left > 0)
6443     {
6444       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6445                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6446                     IS_EM_GATE_GRAY(element) ?
6447                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6448                     IS_EMC_GATE_GRAY(element) ?
6449                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6450                     IS_DC_GATE_GRAY(element) ?
6451                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6452                     element);
6453       TEST_DrawLevelField(x, y);
6454     }
6455     else if (IS_GATE_GRAY_ACTIVE(element) &&
6456              game.magnify_time_left == 0)
6457     {
6458       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6459                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6460                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6461                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6462                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6463                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6464                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6465                     EL_DC_GATE_WHITE_GRAY :
6466                     element);
6467       TEST_DrawLevelField(x, y);
6468     }
6469   }
6470 }
6471
6472 static void ToggleLightSwitch(int x, int y)
6473 {
6474   int element = Tile[x][y];
6475
6476   game.light_time_left =
6477     (element == EL_LIGHT_SWITCH ?
6478      level.time_light * FRAMES_PER_SECOND : 0);
6479
6480   RedrawAllLightSwitchesAndInvisibleElements();
6481 }
6482
6483 static void ActivateTimegateSwitch(int x, int y)
6484 {
6485   int xx, yy;
6486
6487   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6488
6489   SCAN_PLAYFIELD(xx, yy)
6490   {
6491     int element = Tile[xx][yy];
6492
6493     if (element == EL_TIMEGATE_CLOSED ||
6494         element == EL_TIMEGATE_CLOSING)
6495     {
6496       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6497       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6498     }
6499
6500     /*
6501     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6502     {
6503       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6504       TEST_DrawLevelField(xx, yy);
6505     }
6506     */
6507
6508   }
6509
6510   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6511                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6512 }
6513
6514 static void Impact(int x, int y)
6515 {
6516   boolean last_line = (y == lev_fieldy - 1);
6517   boolean object_hit = FALSE;
6518   boolean impact = (last_line || object_hit);
6519   int element = Tile[x][y];
6520   int smashed = EL_STEELWALL;
6521
6522   if (!last_line)       // check if element below was hit
6523   {
6524     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6525       return;
6526
6527     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6528                                          MovDir[x][y + 1] != MV_DOWN ||
6529                                          MovPos[x][y + 1] <= TILEY / 2));
6530
6531     // do not smash moving elements that left the smashed field in time
6532     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6533         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6534       object_hit = FALSE;
6535
6536 #if USE_QUICKSAND_IMPACT_BUGFIX
6537     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6538     {
6539       RemoveMovingField(x, y + 1);
6540       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6541       Tile[x][y + 2] = EL_ROCK;
6542       TEST_DrawLevelField(x, y + 2);
6543
6544       object_hit = TRUE;
6545     }
6546
6547     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6548     {
6549       RemoveMovingField(x, y + 1);
6550       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6551       Tile[x][y + 2] = EL_ROCK;
6552       TEST_DrawLevelField(x, y + 2);
6553
6554       object_hit = TRUE;
6555     }
6556 #endif
6557
6558     if (object_hit)
6559       smashed = MovingOrBlocked2Element(x, y + 1);
6560
6561     impact = (last_line || object_hit);
6562   }
6563
6564   if (!last_line && smashed == EL_ACID) // element falls into acid
6565   {
6566     SplashAcid(x, y + 1);
6567     return;
6568   }
6569
6570   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6571   // only reset graphic animation if graphic really changes after impact
6572   if (impact &&
6573       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6574   {
6575     ResetGfxAnimation(x, y);
6576     TEST_DrawLevelField(x, y);
6577   }
6578
6579   if (impact && CAN_EXPLODE_IMPACT(element))
6580   {
6581     Bang(x, y);
6582     return;
6583   }
6584   else if (impact && element == EL_PEARL &&
6585            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6586   {
6587     ResetGfxAnimation(x, y);
6588
6589     Tile[x][y] = EL_PEARL_BREAKING;
6590     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6591     return;
6592   }
6593   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6594   {
6595     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6596
6597     return;
6598   }
6599
6600   if (impact && element == EL_AMOEBA_DROP)
6601   {
6602     if (object_hit && IS_PLAYER(x, y + 1))
6603       KillPlayerUnlessEnemyProtected(x, y + 1);
6604     else if (object_hit && smashed == EL_PENGUIN)
6605       Bang(x, y + 1);
6606     else
6607     {
6608       Tile[x][y] = EL_AMOEBA_GROWING;
6609       Store[x][y] = EL_AMOEBA_WET;
6610
6611       ResetRandomAnimationValue(x, y);
6612     }
6613     return;
6614   }
6615
6616   if (object_hit)               // check which object was hit
6617   {
6618     if ((CAN_PASS_MAGIC_WALL(element) && 
6619          (smashed == EL_MAGIC_WALL ||
6620           smashed == EL_BD_MAGIC_WALL)) ||
6621         (CAN_PASS_DC_MAGIC_WALL(element) &&
6622          smashed == EL_DC_MAGIC_WALL))
6623     {
6624       int xx, yy;
6625       int activated_magic_wall =
6626         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6627          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6628          EL_DC_MAGIC_WALL_ACTIVE);
6629
6630       // activate magic wall / mill
6631       SCAN_PLAYFIELD(xx, yy)
6632       {
6633         if (Tile[xx][yy] == smashed)
6634           Tile[xx][yy] = activated_magic_wall;
6635       }
6636
6637       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6638       game.magic_wall_active = TRUE;
6639
6640       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6641                             SND_MAGIC_WALL_ACTIVATING :
6642                             smashed == EL_BD_MAGIC_WALL ?
6643                             SND_BD_MAGIC_WALL_ACTIVATING :
6644                             SND_DC_MAGIC_WALL_ACTIVATING));
6645     }
6646
6647     if (IS_PLAYER(x, y + 1))
6648     {
6649       if (CAN_SMASH_PLAYER(element))
6650       {
6651         KillPlayerUnlessEnemyProtected(x, y + 1);
6652         return;
6653       }
6654     }
6655     else if (smashed == EL_PENGUIN)
6656     {
6657       if (CAN_SMASH_PLAYER(element))
6658       {
6659         Bang(x, y + 1);
6660         return;
6661       }
6662     }
6663     else if (element == EL_BD_DIAMOND)
6664     {
6665       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6666       {
6667         Bang(x, y + 1);
6668         return;
6669       }
6670     }
6671     else if (((element == EL_SP_INFOTRON ||
6672                element == EL_SP_ZONK) &&
6673               (smashed == EL_SP_SNIKSNAK ||
6674                smashed == EL_SP_ELECTRON ||
6675                smashed == EL_SP_DISK_ORANGE)) ||
6676              (element == EL_SP_INFOTRON &&
6677               smashed == EL_SP_DISK_YELLOW))
6678     {
6679       Bang(x, y + 1);
6680       return;
6681     }
6682     else if (CAN_SMASH_EVERYTHING(element))
6683     {
6684       if (IS_CLASSIC_ENEMY(smashed) ||
6685           CAN_EXPLODE_SMASHED(smashed))
6686       {
6687         Bang(x, y + 1);
6688         return;
6689       }
6690       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6691       {
6692         if (smashed == EL_LAMP ||
6693             smashed == EL_LAMP_ACTIVE)
6694         {
6695           Bang(x, y + 1);
6696           return;
6697         }
6698         else if (smashed == EL_NUT)
6699         {
6700           Tile[x][y + 1] = EL_NUT_BREAKING;
6701           PlayLevelSound(x, y, SND_NUT_BREAKING);
6702           RaiseScoreElement(EL_NUT);
6703           return;
6704         }
6705         else if (smashed == EL_PEARL)
6706         {
6707           ResetGfxAnimation(x, y);
6708
6709           Tile[x][y + 1] = EL_PEARL_BREAKING;
6710           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6711           return;
6712         }
6713         else if (smashed == EL_DIAMOND)
6714         {
6715           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6716           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6717           return;
6718         }
6719         else if (IS_BELT_SWITCH(smashed))
6720         {
6721           ToggleBeltSwitch(x, y + 1);
6722         }
6723         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6724                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6725                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6726                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6727         {
6728           ToggleSwitchgateSwitch(x, y + 1);
6729         }
6730         else if (smashed == EL_LIGHT_SWITCH ||
6731                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6732         {
6733           ToggleLightSwitch(x, y + 1);
6734         }
6735         else
6736         {
6737           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6738
6739           CheckElementChangeBySide(x, y + 1, smashed, element,
6740                                    CE_SWITCHED, CH_SIDE_TOP);
6741           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6742                                             CH_SIDE_TOP);
6743         }
6744       }
6745       else
6746       {
6747         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6748       }
6749     }
6750   }
6751
6752   // play sound of magic wall / mill
6753   if (!last_line &&
6754       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6755        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6756        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6757   {
6758     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6759       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6760     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6761       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6762     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6763       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6764
6765     return;
6766   }
6767
6768   // play sound of object that hits the ground
6769   if (last_line || object_hit)
6770     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6771 }
6772
6773 static void TurnRoundExt(int x, int y)
6774 {
6775   static struct
6776   {
6777     int dx, dy;
6778   } move_xy[] =
6779   {
6780     {  0,  0 },
6781     { -1,  0 },
6782     { +1,  0 },
6783     {  0,  0 },
6784     {  0, -1 },
6785     {  0,  0 }, { 0, 0 }, { 0, 0 },
6786     {  0, +1 }
6787   };
6788   static struct
6789   {
6790     int left, right, back;
6791   } turn[] =
6792   {
6793     { 0,        0,              0        },
6794     { MV_DOWN,  MV_UP,          MV_RIGHT },
6795     { MV_UP,    MV_DOWN,        MV_LEFT  },
6796     { 0,        0,              0        },
6797     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6798     { 0,        0,              0        },
6799     { 0,        0,              0        },
6800     { 0,        0,              0        },
6801     { MV_RIGHT, MV_LEFT,        MV_UP    }
6802   };
6803
6804   int element = Tile[x][y];
6805   int move_pattern = element_info[element].move_pattern;
6806
6807   int old_move_dir = MovDir[x][y];
6808   int left_dir  = turn[old_move_dir].left;
6809   int right_dir = turn[old_move_dir].right;
6810   int back_dir  = turn[old_move_dir].back;
6811
6812   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6813   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6814   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6815   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6816
6817   int left_x  = x + left_dx,  left_y  = y + left_dy;
6818   int right_x = x + right_dx, right_y = y + right_dy;
6819   int move_x  = x + move_dx,  move_y  = y + move_dy;
6820
6821   int xx, yy;
6822
6823   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6824   {
6825     TestIfBadThingTouchesOtherBadThing(x, y);
6826
6827     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6828       MovDir[x][y] = right_dir;
6829     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6830       MovDir[x][y] = left_dir;
6831
6832     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6833       MovDelay[x][y] = 9;
6834     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6835       MovDelay[x][y] = 1;
6836   }
6837   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6838   {
6839     TestIfBadThingTouchesOtherBadThing(x, y);
6840
6841     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6842       MovDir[x][y] = left_dir;
6843     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6844       MovDir[x][y] = right_dir;
6845
6846     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6847       MovDelay[x][y] = 9;
6848     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6849       MovDelay[x][y] = 1;
6850   }
6851   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6852   {
6853     TestIfBadThingTouchesOtherBadThing(x, y);
6854
6855     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6856       MovDir[x][y] = left_dir;
6857     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6858       MovDir[x][y] = right_dir;
6859
6860     if (MovDir[x][y] != old_move_dir)
6861       MovDelay[x][y] = 9;
6862   }
6863   else if (element == EL_YAMYAM)
6864   {
6865     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6866     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6867
6868     if (can_turn_left && can_turn_right)
6869       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6870     else if (can_turn_left)
6871       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6872     else if (can_turn_right)
6873       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6874     else
6875       MovDir[x][y] = back_dir;
6876
6877     MovDelay[x][y] = 16 + 16 * RND(3);
6878   }
6879   else if (element == EL_DARK_YAMYAM)
6880   {
6881     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6882                                                          left_x, left_y);
6883     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6884                                                          right_x, right_y);
6885
6886     if (can_turn_left && can_turn_right)
6887       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6888     else if (can_turn_left)
6889       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6890     else if (can_turn_right)
6891       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6892     else
6893       MovDir[x][y] = back_dir;
6894
6895     MovDelay[x][y] = 16 + 16 * RND(3);
6896   }
6897   else if (element == EL_PACMAN)
6898   {
6899     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6900     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6901
6902     if (can_turn_left && can_turn_right)
6903       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6904     else if (can_turn_left)
6905       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6906     else if (can_turn_right)
6907       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6908     else
6909       MovDir[x][y] = back_dir;
6910
6911     MovDelay[x][y] = 6 + RND(40);
6912   }
6913   else if (element == EL_PIG)
6914   {
6915     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6916     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6917     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6918     boolean should_turn_left, should_turn_right, should_move_on;
6919     int rnd_value = 24;
6920     int rnd = RND(rnd_value);
6921
6922     should_turn_left = (can_turn_left &&
6923                         (!can_move_on ||
6924                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6925                                                    y + back_dy + left_dy)));
6926     should_turn_right = (can_turn_right &&
6927                          (!can_move_on ||
6928                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6929                                                     y + back_dy + right_dy)));
6930     should_move_on = (can_move_on &&
6931                       (!can_turn_left ||
6932                        !can_turn_right ||
6933                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6934                                                  y + move_dy + left_dy) ||
6935                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6936                                                  y + move_dy + right_dy)));
6937
6938     if (should_turn_left || should_turn_right || should_move_on)
6939     {
6940       if (should_turn_left && should_turn_right && should_move_on)
6941         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6942                         rnd < 2 * rnd_value / 3 ? right_dir :
6943                         old_move_dir);
6944       else if (should_turn_left && should_turn_right)
6945         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6946       else if (should_turn_left && should_move_on)
6947         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6948       else if (should_turn_right && should_move_on)
6949         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6950       else if (should_turn_left)
6951         MovDir[x][y] = left_dir;
6952       else if (should_turn_right)
6953         MovDir[x][y] = right_dir;
6954       else if (should_move_on)
6955         MovDir[x][y] = old_move_dir;
6956     }
6957     else if (can_move_on && rnd > rnd_value / 8)
6958       MovDir[x][y] = old_move_dir;
6959     else if (can_turn_left && can_turn_right)
6960       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6961     else if (can_turn_left && rnd > rnd_value / 8)
6962       MovDir[x][y] = left_dir;
6963     else if (can_turn_right && rnd > rnd_value/8)
6964       MovDir[x][y] = right_dir;
6965     else
6966       MovDir[x][y] = back_dir;
6967
6968     xx = x + move_xy[MovDir[x][y]].dx;
6969     yy = y + move_xy[MovDir[x][y]].dy;
6970
6971     if (!IN_LEV_FIELD(xx, yy) ||
6972         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6973       MovDir[x][y] = old_move_dir;
6974
6975     MovDelay[x][y] = 0;
6976   }
6977   else if (element == EL_DRAGON)
6978   {
6979     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6980     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6981     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6982     int rnd_value = 24;
6983     int rnd = RND(rnd_value);
6984
6985     if (can_move_on && rnd > rnd_value / 8)
6986       MovDir[x][y] = old_move_dir;
6987     else if (can_turn_left && can_turn_right)
6988       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6989     else if (can_turn_left && rnd > rnd_value / 8)
6990       MovDir[x][y] = left_dir;
6991     else if (can_turn_right && rnd > rnd_value / 8)
6992       MovDir[x][y] = right_dir;
6993     else
6994       MovDir[x][y] = back_dir;
6995
6996     xx = x + move_xy[MovDir[x][y]].dx;
6997     yy = y + move_xy[MovDir[x][y]].dy;
6998
6999     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7000       MovDir[x][y] = old_move_dir;
7001
7002     MovDelay[x][y] = 0;
7003   }
7004   else if (element == EL_MOLE)
7005   {
7006     boolean can_move_on =
7007       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7008                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7009                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7010     if (!can_move_on)
7011     {
7012       boolean can_turn_left =
7013         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7014                               IS_AMOEBOID(Tile[left_x][left_y])));
7015
7016       boolean can_turn_right =
7017         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7018                               IS_AMOEBOID(Tile[right_x][right_y])));
7019
7020       if (can_turn_left && can_turn_right)
7021         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7022       else if (can_turn_left)
7023         MovDir[x][y] = left_dir;
7024       else
7025         MovDir[x][y] = right_dir;
7026     }
7027
7028     if (MovDir[x][y] != old_move_dir)
7029       MovDelay[x][y] = 9;
7030   }
7031   else if (element == EL_BALLOON)
7032   {
7033     MovDir[x][y] = game.wind_direction;
7034     MovDelay[x][y] = 0;
7035   }
7036   else if (element == EL_SPRING)
7037   {
7038     if (MovDir[x][y] & MV_HORIZONTAL)
7039     {
7040       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7041           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7042       {
7043         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7044         ResetGfxAnimation(move_x, move_y);
7045         TEST_DrawLevelField(move_x, move_y);
7046
7047         MovDir[x][y] = back_dir;
7048       }
7049       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7050                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7051         MovDir[x][y] = MV_NONE;
7052     }
7053
7054     MovDelay[x][y] = 0;
7055   }
7056   else if (element == EL_ROBOT ||
7057            element == EL_SATELLITE ||
7058            element == EL_PENGUIN ||
7059            element == EL_EMC_ANDROID)
7060   {
7061     int attr_x = -1, attr_y = -1;
7062
7063     if (game.all_players_gone)
7064     {
7065       attr_x = game.exit_x;
7066       attr_y = game.exit_y;
7067     }
7068     else
7069     {
7070       int i;
7071
7072       for (i = 0; i < MAX_PLAYERS; i++)
7073       {
7074         struct PlayerInfo *player = &stored_player[i];
7075         int jx = player->jx, jy = player->jy;
7076
7077         if (!player->active)
7078           continue;
7079
7080         if (attr_x == -1 ||
7081             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7082         {
7083           attr_x = jx;
7084           attr_y = jy;
7085         }
7086       }
7087     }
7088
7089     if (element == EL_ROBOT &&
7090         game.robot_wheel_x >= 0 &&
7091         game.robot_wheel_y >= 0 &&
7092         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7093          game.engine_version < VERSION_IDENT(3,1,0,0)))
7094     {
7095       attr_x = game.robot_wheel_x;
7096       attr_y = game.robot_wheel_y;
7097     }
7098
7099     if (element == EL_PENGUIN)
7100     {
7101       int i;
7102       static int xy[4][2] =
7103       {
7104         { 0, -1 },
7105         { -1, 0 },
7106         { +1, 0 },
7107         { 0, +1 }
7108       };
7109
7110       for (i = 0; i < NUM_DIRECTIONS; i++)
7111       {
7112         int ex = x + xy[i][0];
7113         int ey = y + xy[i][1];
7114
7115         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7116                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7117                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7118                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7119         {
7120           attr_x = ex;
7121           attr_y = ey;
7122           break;
7123         }
7124       }
7125     }
7126
7127     MovDir[x][y] = MV_NONE;
7128     if (attr_x < x)
7129       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7130     else if (attr_x > x)
7131       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7132     if (attr_y < y)
7133       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7134     else if (attr_y > y)
7135       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7136
7137     if (element == EL_ROBOT)
7138     {
7139       int newx, newy;
7140
7141       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7142         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7143       Moving2Blocked(x, y, &newx, &newy);
7144
7145       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7146         MovDelay[x][y] = 8 + 8 * !RND(3);
7147       else
7148         MovDelay[x][y] = 16;
7149     }
7150     else if (element == EL_PENGUIN)
7151     {
7152       int newx, newy;
7153
7154       MovDelay[x][y] = 1;
7155
7156       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7157       {
7158         boolean first_horiz = RND(2);
7159         int new_move_dir = MovDir[x][y];
7160
7161         MovDir[x][y] =
7162           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7163         Moving2Blocked(x, y, &newx, &newy);
7164
7165         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7166           return;
7167
7168         MovDir[x][y] =
7169           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7170         Moving2Blocked(x, y, &newx, &newy);
7171
7172         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7173           return;
7174
7175         MovDir[x][y] = old_move_dir;
7176         return;
7177       }
7178     }
7179     else if (element == EL_SATELLITE)
7180     {
7181       int newx, newy;
7182
7183       MovDelay[x][y] = 1;
7184
7185       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7186       {
7187         boolean first_horiz = RND(2);
7188         int new_move_dir = MovDir[x][y];
7189
7190         MovDir[x][y] =
7191           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7192         Moving2Blocked(x, y, &newx, &newy);
7193
7194         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7195           return;
7196
7197         MovDir[x][y] =
7198           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7199         Moving2Blocked(x, y, &newx, &newy);
7200
7201         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7202           return;
7203
7204         MovDir[x][y] = old_move_dir;
7205         return;
7206       }
7207     }
7208     else if (element == EL_EMC_ANDROID)
7209     {
7210       static int check_pos[16] =
7211       {
7212         -1,             //  0 => (invalid)
7213         7,              //  1 => MV_LEFT
7214         3,              //  2 => MV_RIGHT
7215         -1,             //  3 => (invalid)
7216         1,              //  4 =>            MV_UP
7217         0,              //  5 => MV_LEFT  | MV_UP
7218         2,              //  6 => MV_RIGHT | MV_UP
7219         -1,             //  7 => (invalid)
7220         5,              //  8 =>            MV_DOWN
7221         6,              //  9 => MV_LEFT  | MV_DOWN
7222         4,              // 10 => MV_RIGHT | MV_DOWN
7223         -1,             // 11 => (invalid)
7224         -1,             // 12 => (invalid)
7225         -1,             // 13 => (invalid)
7226         -1,             // 14 => (invalid)
7227         -1,             // 15 => (invalid)
7228       };
7229       static struct
7230       {
7231         int dx, dy;
7232         int dir;
7233       } check_xy[8] =
7234       {
7235         { -1, -1,       MV_LEFT  | MV_UP   },
7236         {  0, -1,                  MV_UP   },
7237         { +1, -1,       MV_RIGHT | MV_UP   },
7238         { +1,  0,       MV_RIGHT           },
7239         { +1, +1,       MV_RIGHT | MV_DOWN },
7240         {  0, +1,                  MV_DOWN },
7241         { -1, +1,       MV_LEFT  | MV_DOWN },
7242         { -1,  0,       MV_LEFT            },
7243       };
7244       int start_pos, check_order;
7245       boolean can_clone = FALSE;
7246       int i;
7247
7248       // check if there is any free field around current position
7249       for (i = 0; i < 8; i++)
7250       {
7251         int newx = x + check_xy[i].dx;
7252         int newy = y + check_xy[i].dy;
7253
7254         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7255         {
7256           can_clone = TRUE;
7257
7258           break;
7259         }
7260       }
7261
7262       if (can_clone)            // randomly find an element to clone
7263       {
7264         can_clone = FALSE;
7265
7266         start_pos = check_pos[RND(8)];
7267         check_order = (RND(2) ? -1 : +1);
7268
7269         for (i = 0; i < 8; i++)
7270         {
7271           int pos_raw = start_pos + i * check_order;
7272           int pos = (pos_raw + 8) % 8;
7273           int newx = x + check_xy[pos].dx;
7274           int newy = y + check_xy[pos].dy;
7275
7276           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7277           {
7278             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7279             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7280
7281             Store[x][y] = Tile[newx][newy];
7282
7283             can_clone = TRUE;
7284
7285             break;
7286           }
7287         }
7288       }
7289
7290       if (can_clone)            // randomly find a direction to move
7291       {
7292         can_clone = FALSE;
7293
7294         start_pos = check_pos[RND(8)];
7295         check_order = (RND(2) ? -1 : +1);
7296
7297         for (i = 0; i < 8; i++)
7298         {
7299           int pos_raw = start_pos + i * check_order;
7300           int pos = (pos_raw + 8) % 8;
7301           int newx = x + check_xy[pos].dx;
7302           int newy = y + check_xy[pos].dy;
7303           int new_move_dir = check_xy[pos].dir;
7304
7305           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7306           {
7307             MovDir[x][y] = new_move_dir;
7308             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7309
7310             can_clone = TRUE;
7311
7312             break;
7313           }
7314         }
7315       }
7316
7317       if (can_clone)            // cloning and moving successful
7318         return;
7319
7320       // cannot clone -- try to move towards player
7321
7322       start_pos = check_pos[MovDir[x][y] & 0x0f];
7323       check_order = (RND(2) ? -1 : +1);
7324
7325       for (i = 0; i < 3; i++)
7326       {
7327         // first check start_pos, then previous/next or (next/previous) pos
7328         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7329         int pos = (pos_raw + 8) % 8;
7330         int newx = x + check_xy[pos].dx;
7331         int newy = y + check_xy[pos].dy;
7332         int new_move_dir = check_xy[pos].dir;
7333
7334         if (IS_PLAYER(newx, newy))
7335           break;
7336
7337         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7338         {
7339           MovDir[x][y] = new_move_dir;
7340           MovDelay[x][y] = level.android_move_time * 8 + 1;
7341
7342           break;
7343         }
7344       }
7345     }
7346   }
7347   else if (move_pattern == MV_TURNING_LEFT ||
7348            move_pattern == MV_TURNING_RIGHT ||
7349            move_pattern == MV_TURNING_LEFT_RIGHT ||
7350            move_pattern == MV_TURNING_RIGHT_LEFT ||
7351            move_pattern == MV_TURNING_RANDOM ||
7352            move_pattern == MV_ALL_DIRECTIONS)
7353   {
7354     boolean can_turn_left =
7355       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7356     boolean can_turn_right =
7357       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7358
7359     if (element_info[element].move_stepsize == 0)       // "not moving"
7360       return;
7361
7362     if (move_pattern == MV_TURNING_LEFT)
7363       MovDir[x][y] = left_dir;
7364     else if (move_pattern == MV_TURNING_RIGHT)
7365       MovDir[x][y] = right_dir;
7366     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7367       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7368     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7369       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7370     else if (move_pattern == MV_TURNING_RANDOM)
7371       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7372                       can_turn_right && !can_turn_left ? right_dir :
7373                       RND(2) ? left_dir : right_dir);
7374     else if (can_turn_left && can_turn_right)
7375       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7376     else if (can_turn_left)
7377       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7378     else if (can_turn_right)
7379       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7380     else
7381       MovDir[x][y] = back_dir;
7382
7383     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7384   }
7385   else if (move_pattern == MV_HORIZONTAL ||
7386            move_pattern == MV_VERTICAL)
7387   {
7388     if (move_pattern & old_move_dir)
7389       MovDir[x][y] = back_dir;
7390     else if (move_pattern == MV_HORIZONTAL)
7391       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7392     else if (move_pattern == MV_VERTICAL)
7393       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7394
7395     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7396   }
7397   else if (move_pattern & MV_ANY_DIRECTION)
7398   {
7399     MovDir[x][y] = move_pattern;
7400     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7401   }
7402   else if (move_pattern & MV_WIND_DIRECTION)
7403   {
7404     MovDir[x][y] = game.wind_direction;
7405     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7406   }
7407   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7408   {
7409     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7410       MovDir[x][y] = left_dir;
7411     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7412       MovDir[x][y] = right_dir;
7413
7414     if (MovDir[x][y] != old_move_dir)
7415       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7416   }
7417   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7418   {
7419     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7420       MovDir[x][y] = right_dir;
7421     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7422       MovDir[x][y] = left_dir;
7423
7424     if (MovDir[x][y] != old_move_dir)
7425       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7426   }
7427   else if (move_pattern == MV_TOWARDS_PLAYER ||
7428            move_pattern == MV_AWAY_FROM_PLAYER)
7429   {
7430     int attr_x = -1, attr_y = -1;
7431     int newx, newy;
7432     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7433
7434     if (game.all_players_gone)
7435     {
7436       attr_x = game.exit_x;
7437       attr_y = game.exit_y;
7438     }
7439     else
7440     {
7441       int i;
7442
7443       for (i = 0; i < MAX_PLAYERS; i++)
7444       {
7445         struct PlayerInfo *player = &stored_player[i];
7446         int jx = player->jx, jy = player->jy;
7447
7448         if (!player->active)
7449           continue;
7450
7451         if (attr_x == -1 ||
7452             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7453         {
7454           attr_x = jx;
7455           attr_y = jy;
7456         }
7457       }
7458     }
7459
7460     MovDir[x][y] = MV_NONE;
7461     if (attr_x < x)
7462       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7463     else if (attr_x > x)
7464       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7465     if (attr_y < y)
7466       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7467     else if (attr_y > y)
7468       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7469
7470     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7471
7472     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7473     {
7474       boolean first_horiz = RND(2);
7475       int new_move_dir = MovDir[x][y];
7476
7477       if (element_info[element].move_stepsize == 0)     // "not moving"
7478       {
7479         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7480         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7481
7482         return;
7483       }
7484
7485       MovDir[x][y] =
7486         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7487       Moving2Blocked(x, y, &newx, &newy);
7488
7489       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7490         return;
7491
7492       MovDir[x][y] =
7493         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7494       Moving2Blocked(x, y, &newx, &newy);
7495
7496       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7497         return;
7498
7499       MovDir[x][y] = old_move_dir;
7500     }
7501   }
7502   else if (move_pattern == MV_WHEN_PUSHED ||
7503            move_pattern == MV_WHEN_DROPPED)
7504   {
7505     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7506       MovDir[x][y] = MV_NONE;
7507
7508     MovDelay[x][y] = 0;
7509   }
7510   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7511   {
7512     static int test_xy[7][2] =
7513     {
7514       { 0, -1 },
7515       { -1, 0 },
7516       { +1, 0 },
7517       { 0, +1 },
7518       { 0, -1 },
7519       { -1, 0 },
7520       { +1, 0 },
7521     };
7522     static int test_dir[7] =
7523     {
7524       MV_UP,
7525       MV_LEFT,
7526       MV_RIGHT,
7527       MV_DOWN,
7528       MV_UP,
7529       MV_LEFT,
7530       MV_RIGHT,
7531     };
7532     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7533     int move_preference = -1000000;     // start with very low preference
7534     int new_move_dir = MV_NONE;
7535     int start_test = RND(4);
7536     int i;
7537
7538     for (i = 0; i < NUM_DIRECTIONS; i++)
7539     {
7540       int move_dir = test_dir[start_test + i];
7541       int move_dir_preference;
7542
7543       xx = x + test_xy[start_test + i][0];
7544       yy = y + test_xy[start_test + i][1];
7545
7546       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7547           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7548       {
7549         new_move_dir = move_dir;
7550
7551         break;
7552       }
7553
7554       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7555         continue;
7556
7557       move_dir_preference = -1 * RunnerVisit[xx][yy];
7558       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7559         move_dir_preference = PlayerVisit[xx][yy];
7560
7561       if (move_dir_preference > move_preference)
7562       {
7563         // prefer field that has not been visited for the longest time
7564         move_preference = move_dir_preference;
7565         new_move_dir = move_dir;
7566       }
7567       else if (move_dir_preference == move_preference &&
7568                move_dir == old_move_dir)
7569       {
7570         // prefer last direction when all directions are preferred equally
7571         move_preference = move_dir_preference;
7572         new_move_dir = move_dir;
7573       }
7574     }
7575
7576     MovDir[x][y] = new_move_dir;
7577     if (old_move_dir != new_move_dir)
7578       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7579   }
7580 }
7581
7582 static void TurnRound(int x, int y)
7583 {
7584   int direction = MovDir[x][y];
7585
7586   TurnRoundExt(x, y);
7587
7588   GfxDir[x][y] = MovDir[x][y];
7589
7590   if (direction != MovDir[x][y])
7591     GfxFrame[x][y] = 0;
7592
7593   if (MovDelay[x][y])
7594     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7595
7596   ResetGfxFrame(x, y);
7597 }
7598
7599 static boolean JustBeingPushed(int x, int y)
7600 {
7601   int i;
7602
7603   for (i = 0; i < MAX_PLAYERS; i++)
7604   {
7605     struct PlayerInfo *player = &stored_player[i];
7606
7607     if (player->active && player->is_pushing && player->MovPos)
7608     {
7609       int next_jx = player->jx + (player->jx - player->last_jx);
7610       int next_jy = player->jy + (player->jy - player->last_jy);
7611
7612       if (x == next_jx && y == next_jy)
7613         return TRUE;
7614     }
7615   }
7616
7617   return FALSE;
7618 }
7619
7620 static void StartMoving(int x, int y)
7621 {
7622   boolean started_moving = FALSE;       // some elements can fall _and_ move
7623   int element = Tile[x][y];
7624
7625   if (Stop[x][y])
7626     return;
7627
7628   if (MovDelay[x][y] == 0)
7629     GfxAction[x][y] = ACTION_DEFAULT;
7630
7631   if (CAN_FALL(element) && y < lev_fieldy - 1)
7632   {
7633     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7634         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7635       if (JustBeingPushed(x, y))
7636         return;
7637
7638     if (element == EL_QUICKSAND_FULL)
7639     {
7640       if (IS_FREE(x, y + 1))
7641       {
7642         InitMovingField(x, y, MV_DOWN);
7643         started_moving = TRUE;
7644
7645         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7646 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7647         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7648           Store[x][y] = EL_ROCK;
7649 #else
7650         Store[x][y] = EL_ROCK;
7651 #endif
7652
7653         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7654       }
7655       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7656       {
7657         if (!MovDelay[x][y])
7658         {
7659           MovDelay[x][y] = TILEY + 1;
7660
7661           ResetGfxAnimation(x, y);
7662           ResetGfxAnimation(x, y + 1);
7663         }
7664
7665         if (MovDelay[x][y])
7666         {
7667           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7668           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7669
7670           MovDelay[x][y]--;
7671           if (MovDelay[x][y])
7672             return;
7673         }
7674
7675         Tile[x][y] = EL_QUICKSAND_EMPTY;
7676         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7677         Store[x][y + 1] = Store[x][y];
7678         Store[x][y] = 0;
7679
7680         PlayLevelSoundAction(x, y, ACTION_FILLING);
7681       }
7682       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7683       {
7684         if (!MovDelay[x][y])
7685         {
7686           MovDelay[x][y] = TILEY + 1;
7687
7688           ResetGfxAnimation(x, y);
7689           ResetGfxAnimation(x, y + 1);
7690         }
7691
7692         if (MovDelay[x][y])
7693         {
7694           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7695           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7696
7697           MovDelay[x][y]--;
7698           if (MovDelay[x][y])
7699             return;
7700         }
7701
7702         Tile[x][y] = EL_QUICKSAND_EMPTY;
7703         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7704         Store[x][y + 1] = Store[x][y];
7705         Store[x][y] = 0;
7706
7707         PlayLevelSoundAction(x, y, ACTION_FILLING);
7708       }
7709     }
7710     else if (element == EL_QUICKSAND_FAST_FULL)
7711     {
7712       if (IS_FREE(x, y + 1))
7713       {
7714         InitMovingField(x, y, MV_DOWN);
7715         started_moving = TRUE;
7716
7717         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7718 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7719         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7720           Store[x][y] = EL_ROCK;
7721 #else
7722         Store[x][y] = EL_ROCK;
7723 #endif
7724
7725         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7726       }
7727       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7728       {
7729         if (!MovDelay[x][y])
7730         {
7731           MovDelay[x][y] = TILEY + 1;
7732
7733           ResetGfxAnimation(x, y);
7734           ResetGfxAnimation(x, y + 1);
7735         }
7736
7737         if (MovDelay[x][y])
7738         {
7739           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7740           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7741
7742           MovDelay[x][y]--;
7743           if (MovDelay[x][y])
7744             return;
7745         }
7746
7747         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7748         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7749         Store[x][y + 1] = Store[x][y];
7750         Store[x][y] = 0;
7751
7752         PlayLevelSoundAction(x, y, ACTION_FILLING);
7753       }
7754       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7755       {
7756         if (!MovDelay[x][y])
7757         {
7758           MovDelay[x][y] = TILEY + 1;
7759
7760           ResetGfxAnimation(x, y);
7761           ResetGfxAnimation(x, y + 1);
7762         }
7763
7764         if (MovDelay[x][y])
7765         {
7766           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7767           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7768
7769           MovDelay[x][y]--;
7770           if (MovDelay[x][y])
7771             return;
7772         }
7773
7774         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7775         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7776         Store[x][y + 1] = Store[x][y];
7777         Store[x][y] = 0;
7778
7779         PlayLevelSoundAction(x, y, ACTION_FILLING);
7780       }
7781     }
7782     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7783              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7784     {
7785       InitMovingField(x, y, MV_DOWN);
7786       started_moving = TRUE;
7787
7788       Tile[x][y] = EL_QUICKSAND_FILLING;
7789       Store[x][y] = element;
7790
7791       PlayLevelSoundAction(x, y, ACTION_FILLING);
7792     }
7793     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7794              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7795     {
7796       InitMovingField(x, y, MV_DOWN);
7797       started_moving = TRUE;
7798
7799       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7800       Store[x][y] = element;
7801
7802       PlayLevelSoundAction(x, y, ACTION_FILLING);
7803     }
7804     else if (element == EL_MAGIC_WALL_FULL)
7805     {
7806       if (IS_FREE(x, y + 1))
7807       {
7808         InitMovingField(x, y, MV_DOWN);
7809         started_moving = TRUE;
7810
7811         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7812         Store[x][y] = EL_CHANGED(Store[x][y]);
7813       }
7814       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7815       {
7816         if (!MovDelay[x][y])
7817           MovDelay[x][y] = TILEY / 4 + 1;
7818
7819         if (MovDelay[x][y])
7820         {
7821           MovDelay[x][y]--;
7822           if (MovDelay[x][y])
7823             return;
7824         }
7825
7826         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7827         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7828         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7829         Store[x][y] = 0;
7830       }
7831     }
7832     else if (element == EL_BD_MAGIC_WALL_FULL)
7833     {
7834       if (IS_FREE(x, y + 1))
7835       {
7836         InitMovingField(x, y, MV_DOWN);
7837         started_moving = TRUE;
7838
7839         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7840         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7841       }
7842       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7843       {
7844         if (!MovDelay[x][y])
7845           MovDelay[x][y] = TILEY / 4 + 1;
7846
7847         if (MovDelay[x][y])
7848         {
7849           MovDelay[x][y]--;
7850           if (MovDelay[x][y])
7851             return;
7852         }
7853
7854         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7855         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7856         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7857         Store[x][y] = 0;
7858       }
7859     }
7860     else if (element == EL_DC_MAGIC_WALL_FULL)
7861     {
7862       if (IS_FREE(x, y + 1))
7863       {
7864         InitMovingField(x, y, MV_DOWN);
7865         started_moving = TRUE;
7866
7867         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7868         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7869       }
7870       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7871       {
7872         if (!MovDelay[x][y])
7873           MovDelay[x][y] = TILEY / 4 + 1;
7874
7875         if (MovDelay[x][y])
7876         {
7877           MovDelay[x][y]--;
7878           if (MovDelay[x][y])
7879             return;
7880         }
7881
7882         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7883         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7884         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7885         Store[x][y] = 0;
7886       }
7887     }
7888     else if ((CAN_PASS_MAGIC_WALL(element) &&
7889               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7890                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7891              (CAN_PASS_DC_MAGIC_WALL(element) &&
7892               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7893
7894     {
7895       InitMovingField(x, y, MV_DOWN);
7896       started_moving = TRUE;
7897
7898       Tile[x][y] =
7899         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7900          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7901          EL_DC_MAGIC_WALL_FILLING);
7902       Store[x][y] = element;
7903     }
7904     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7905     {
7906       SplashAcid(x, y + 1);
7907
7908       InitMovingField(x, y, MV_DOWN);
7909       started_moving = TRUE;
7910
7911       Store[x][y] = EL_ACID;
7912     }
7913     else if (
7914              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7915               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7916              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7917               CAN_FALL(element) && WasJustFalling[x][y] &&
7918               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7919
7920              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7921               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7922               (Tile[x][y + 1] == EL_BLOCKED)))
7923     {
7924       /* this is needed for a special case not covered by calling "Impact()"
7925          from "ContinueMoving()": if an element moves to a tile directly below
7926          another element which was just falling on that tile (which was empty
7927          in the previous frame), the falling element above would just stop
7928          instead of smashing the element below (in previous version, the above
7929          element was just checked for "moving" instead of "falling", resulting
7930          in incorrect smashes caused by horizontal movement of the above
7931          element; also, the case of the player being the element to smash was
7932          simply not covered here... :-/ ) */
7933
7934       CheckCollision[x][y] = 0;
7935       CheckImpact[x][y] = 0;
7936
7937       Impact(x, y);
7938     }
7939     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7940     {
7941       if (MovDir[x][y] == MV_NONE)
7942       {
7943         InitMovingField(x, y, MV_DOWN);
7944         started_moving = TRUE;
7945       }
7946     }
7947     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7948     {
7949       if (WasJustFalling[x][y]) // prevent animation from being restarted
7950         MovDir[x][y] = MV_DOWN;
7951
7952       InitMovingField(x, y, MV_DOWN);
7953       started_moving = TRUE;
7954     }
7955     else if (element == EL_AMOEBA_DROP)
7956     {
7957       Tile[x][y] = EL_AMOEBA_GROWING;
7958       Store[x][y] = EL_AMOEBA_WET;
7959     }
7960     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7961               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7962              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7963              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7964     {
7965       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7966                                 (IS_FREE(x - 1, y + 1) ||
7967                                  Tile[x - 1][y + 1] == EL_ACID));
7968       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7969                                 (IS_FREE(x + 1, y + 1) ||
7970                                  Tile[x + 1][y + 1] == EL_ACID));
7971       boolean can_fall_any  = (can_fall_left || can_fall_right);
7972       boolean can_fall_both = (can_fall_left && can_fall_right);
7973       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7974
7975       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7976       {
7977         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7978           can_fall_right = FALSE;
7979         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7980           can_fall_left = FALSE;
7981         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7984           can_fall_left = FALSE;
7985
7986         can_fall_any  = (can_fall_left || can_fall_right);
7987         can_fall_both = FALSE;
7988       }
7989
7990       if (can_fall_both)
7991       {
7992         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7993           can_fall_right = FALSE;       // slip down on left side
7994         else
7995           can_fall_left = !(can_fall_right = RND(2));
7996
7997         can_fall_both = FALSE;
7998       }
7999
8000       if (can_fall_any)
8001       {
8002         // if not determined otherwise, prefer left side for slipping down
8003         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8004         started_moving = TRUE;
8005       }
8006     }
8007     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8008     {
8009       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8010       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8011       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8012       int belt_dir = game.belt_dir[belt_nr];
8013
8014       if ((belt_dir == MV_LEFT  && left_is_free) ||
8015           (belt_dir == MV_RIGHT && right_is_free))
8016       {
8017         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8018
8019         InitMovingField(x, y, belt_dir);
8020         started_moving = TRUE;
8021
8022         Pushed[x][y] = TRUE;
8023         Pushed[nextx][y] = TRUE;
8024
8025         GfxAction[x][y] = ACTION_DEFAULT;
8026       }
8027       else
8028       {
8029         MovDir[x][y] = 0;       // if element was moving, stop it
8030       }
8031     }
8032   }
8033
8034   // not "else if" because of elements that can fall and move (EL_SPRING)
8035   if (CAN_MOVE(element) && !started_moving)
8036   {
8037     int move_pattern = element_info[element].move_pattern;
8038     int newx, newy;
8039
8040     Moving2Blocked(x, y, &newx, &newy);
8041
8042     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8043       return;
8044
8045     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8046         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8047     {
8048       WasJustMoving[x][y] = 0;
8049       CheckCollision[x][y] = 0;
8050
8051       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8052
8053       if (Tile[x][y] != element)        // element has changed
8054         return;
8055     }
8056
8057     if (!MovDelay[x][y])        // start new movement phase
8058     {
8059       // all objects that can change their move direction after each step
8060       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8061
8062       if (element != EL_YAMYAM &&
8063           element != EL_DARK_YAMYAM &&
8064           element != EL_PACMAN &&
8065           !(move_pattern & MV_ANY_DIRECTION) &&
8066           move_pattern != MV_TURNING_LEFT &&
8067           move_pattern != MV_TURNING_RIGHT &&
8068           move_pattern != MV_TURNING_LEFT_RIGHT &&
8069           move_pattern != MV_TURNING_RIGHT_LEFT &&
8070           move_pattern != MV_TURNING_RANDOM)
8071       {
8072         TurnRound(x, y);
8073
8074         if (MovDelay[x][y] && (element == EL_BUG ||
8075                                element == EL_SPACESHIP ||
8076                                element == EL_SP_SNIKSNAK ||
8077                                element == EL_SP_ELECTRON ||
8078                                element == EL_MOLE))
8079           TEST_DrawLevelField(x, y);
8080       }
8081     }
8082
8083     if (MovDelay[x][y])         // wait some time before next movement
8084     {
8085       MovDelay[x][y]--;
8086
8087       if (element == EL_ROBOT ||
8088           element == EL_YAMYAM ||
8089           element == EL_DARK_YAMYAM)
8090       {
8091         DrawLevelElementAnimationIfNeeded(x, y, element);
8092         PlayLevelSoundAction(x, y, ACTION_WAITING);
8093       }
8094       else if (element == EL_SP_ELECTRON)
8095         DrawLevelElementAnimationIfNeeded(x, y, element);
8096       else if (element == EL_DRAGON)
8097       {
8098         int i;
8099         int dir = MovDir[x][y];
8100         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8101         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8102         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8103                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8104                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8105                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8106         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8107
8108         GfxAction[x][y] = ACTION_ATTACKING;
8109
8110         if (IS_PLAYER(x, y))
8111           DrawPlayerField(x, y);
8112         else
8113           TEST_DrawLevelField(x, y);
8114
8115         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8116
8117         for (i = 1; i <= 3; i++)
8118         {
8119           int xx = x + i * dx;
8120           int yy = y + i * dy;
8121           int sx = SCREENX(xx);
8122           int sy = SCREENY(yy);
8123           int flame_graphic = graphic + (i - 1);
8124
8125           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8126             break;
8127
8128           if (MovDelay[x][y])
8129           {
8130             int flamed = MovingOrBlocked2Element(xx, yy);
8131
8132             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8133               Bang(xx, yy);
8134             else
8135               RemoveMovingField(xx, yy);
8136
8137             ChangeDelay[xx][yy] = 0;
8138
8139             Tile[xx][yy] = EL_FLAMES;
8140
8141             if (IN_SCR_FIELD(sx, sy))
8142             {
8143               TEST_DrawLevelFieldCrumbled(xx, yy);
8144               DrawGraphic(sx, sy, flame_graphic, frame);
8145             }
8146           }
8147           else
8148           {
8149             if (Tile[xx][yy] == EL_FLAMES)
8150               Tile[xx][yy] = EL_EMPTY;
8151             TEST_DrawLevelField(xx, yy);
8152           }
8153         }
8154       }
8155
8156       if (MovDelay[x][y])       // element still has to wait some time
8157       {
8158         PlayLevelSoundAction(x, y, ACTION_WAITING);
8159
8160         return;
8161       }
8162     }
8163
8164     // now make next step
8165
8166     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8167
8168     if (DONT_COLLIDE_WITH(element) &&
8169         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8170         !PLAYER_ENEMY_PROTECTED(newx, newy))
8171     {
8172       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8173
8174       return;
8175     }
8176
8177     else if (CAN_MOVE_INTO_ACID(element) &&
8178              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8179              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8180              (MovDir[x][y] == MV_DOWN ||
8181               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8182     {
8183       SplashAcid(newx, newy);
8184       Store[x][y] = EL_ACID;
8185     }
8186     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8187     {
8188       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8189           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8190           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8191           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8192       {
8193         RemoveField(x, y);
8194         TEST_DrawLevelField(x, y);
8195
8196         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8197         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8198           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8199
8200         game.friends_still_needed--;
8201         if (!game.friends_still_needed &&
8202             !game.GameOver &&
8203             game.all_players_gone)
8204           LevelSolved();
8205
8206         return;
8207       }
8208       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8209       {
8210         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8211           TEST_DrawLevelField(newx, newy);
8212         else
8213           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8214       }
8215       else if (!IS_FREE(newx, newy))
8216       {
8217         GfxAction[x][y] = ACTION_WAITING;
8218
8219         if (IS_PLAYER(x, y))
8220           DrawPlayerField(x, y);
8221         else
8222           TEST_DrawLevelField(x, y);
8223
8224         return;
8225       }
8226     }
8227     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8228     {
8229       if (IS_FOOD_PIG(Tile[newx][newy]))
8230       {
8231         if (IS_MOVING(newx, newy))
8232           RemoveMovingField(newx, newy);
8233         else
8234         {
8235           Tile[newx][newy] = EL_EMPTY;
8236           TEST_DrawLevelField(newx, newy);
8237         }
8238
8239         PlayLevelSound(x, y, SND_PIG_DIGGING);
8240       }
8241       else if (!IS_FREE(newx, newy))
8242       {
8243         if (IS_PLAYER(x, y))
8244           DrawPlayerField(x, y);
8245         else
8246           TEST_DrawLevelField(x, y);
8247
8248         return;
8249       }
8250     }
8251     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8252     {
8253       if (Store[x][y] != EL_EMPTY)
8254       {
8255         boolean can_clone = FALSE;
8256         int xx, yy;
8257
8258         // check if element to clone is still there
8259         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8260         {
8261           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8262           {
8263             can_clone = TRUE;
8264
8265             break;
8266           }
8267         }
8268
8269         // cannot clone or target field not free anymore -- do not clone
8270         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8271           Store[x][y] = EL_EMPTY;
8272       }
8273
8274       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8275       {
8276         if (IS_MV_DIAGONAL(MovDir[x][y]))
8277         {
8278           int diagonal_move_dir = MovDir[x][y];
8279           int stored = Store[x][y];
8280           int change_delay = 8;
8281           int graphic;
8282
8283           // android is moving diagonally
8284
8285           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8286
8287           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8288           GfxElement[x][y] = EL_EMC_ANDROID;
8289           GfxAction[x][y] = ACTION_SHRINKING;
8290           GfxDir[x][y] = diagonal_move_dir;
8291           ChangeDelay[x][y] = change_delay;
8292
8293           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8294                                    GfxDir[x][y]);
8295
8296           DrawLevelGraphicAnimation(x, y, graphic);
8297           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8298
8299           if (Tile[newx][newy] == EL_ACID)
8300           {
8301             SplashAcid(newx, newy);
8302
8303             return;
8304           }
8305
8306           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8307
8308           Store[newx][newy] = EL_EMC_ANDROID;
8309           GfxElement[newx][newy] = EL_EMC_ANDROID;
8310           GfxAction[newx][newy] = ACTION_GROWING;
8311           GfxDir[newx][newy] = diagonal_move_dir;
8312           ChangeDelay[newx][newy] = change_delay;
8313
8314           graphic = el_act_dir2img(GfxElement[newx][newy],
8315                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8316
8317           DrawLevelGraphicAnimation(newx, newy, graphic);
8318           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8319
8320           return;
8321         }
8322         else
8323         {
8324           Tile[newx][newy] = EL_EMPTY;
8325           TEST_DrawLevelField(newx, newy);
8326
8327           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8328         }
8329       }
8330       else if (!IS_FREE(newx, newy))
8331       {
8332         return;
8333       }
8334     }
8335     else if (IS_CUSTOM_ELEMENT(element) &&
8336              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8337     {
8338       if (!DigFieldByCE(newx, newy, element))
8339         return;
8340
8341       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8342       {
8343         RunnerVisit[x][y] = FrameCounter;
8344         PlayerVisit[x][y] /= 8;         // expire player visit path
8345       }
8346     }
8347     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8348     {
8349       if (!IS_FREE(newx, newy))
8350       {
8351         if (IS_PLAYER(x, y))
8352           DrawPlayerField(x, y);
8353         else
8354           TEST_DrawLevelField(x, y);
8355
8356         return;
8357       }
8358       else
8359       {
8360         boolean wanna_flame = !RND(10);
8361         int dx = newx - x, dy = newy - y;
8362         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8363         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8364         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8365                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8366         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8367                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8368
8369         if ((wanna_flame ||
8370              IS_CLASSIC_ENEMY(element1) ||
8371              IS_CLASSIC_ENEMY(element2)) &&
8372             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8373             element1 != EL_FLAMES && element2 != EL_FLAMES)
8374         {
8375           ResetGfxAnimation(x, y);
8376           GfxAction[x][y] = ACTION_ATTACKING;
8377
8378           if (IS_PLAYER(x, y))
8379             DrawPlayerField(x, y);
8380           else
8381             TEST_DrawLevelField(x, y);
8382
8383           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8384
8385           MovDelay[x][y] = 50;
8386
8387           Tile[newx][newy] = EL_FLAMES;
8388           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8389             Tile[newx1][newy1] = EL_FLAMES;
8390           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8391             Tile[newx2][newy2] = EL_FLAMES;
8392
8393           return;
8394         }
8395       }
8396     }
8397     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8398              Tile[newx][newy] == EL_DIAMOND)
8399     {
8400       if (IS_MOVING(newx, newy))
8401         RemoveMovingField(newx, newy);
8402       else
8403       {
8404         Tile[newx][newy] = EL_EMPTY;
8405         TEST_DrawLevelField(newx, newy);
8406       }
8407
8408       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8409     }
8410     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8411              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8412     {
8413       if (AmoebaNr[newx][newy])
8414       {
8415         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8416         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8417             Tile[newx][newy] == EL_BD_AMOEBA)
8418           AmoebaCnt[AmoebaNr[newx][newy]]--;
8419       }
8420
8421       if (IS_MOVING(newx, newy))
8422       {
8423         RemoveMovingField(newx, newy);
8424       }
8425       else
8426       {
8427         Tile[newx][newy] = EL_EMPTY;
8428         TEST_DrawLevelField(newx, newy);
8429       }
8430
8431       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8432     }
8433     else if ((element == EL_PACMAN || element == EL_MOLE)
8434              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8435     {
8436       if (AmoebaNr[newx][newy])
8437       {
8438         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8439         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8440             Tile[newx][newy] == EL_BD_AMOEBA)
8441           AmoebaCnt[AmoebaNr[newx][newy]]--;
8442       }
8443
8444       if (element == EL_MOLE)
8445       {
8446         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8447         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8448
8449         ResetGfxAnimation(x, y);
8450         GfxAction[x][y] = ACTION_DIGGING;
8451         TEST_DrawLevelField(x, y);
8452
8453         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8454
8455         return;                         // wait for shrinking amoeba
8456       }
8457       else      // element == EL_PACMAN
8458       {
8459         Tile[newx][newy] = EL_EMPTY;
8460         TEST_DrawLevelField(newx, newy);
8461         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8462       }
8463     }
8464     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8465              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8466               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8467     {
8468       // wait for shrinking amoeba to completely disappear
8469       return;
8470     }
8471     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8472     {
8473       // object was running against a wall
8474
8475       TurnRound(x, y);
8476
8477       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8478         DrawLevelElementAnimation(x, y, element);
8479
8480       if (DONT_TOUCH(element))
8481         TestIfBadThingTouchesPlayer(x, y);
8482
8483       return;
8484     }
8485
8486     InitMovingField(x, y, MovDir[x][y]);
8487
8488     PlayLevelSoundAction(x, y, ACTION_MOVING);
8489   }
8490
8491   if (MovDir[x][y])
8492     ContinueMoving(x, y);
8493 }
8494
8495 void ContinueMoving(int x, int y)
8496 {
8497   int element = Tile[x][y];
8498   struct ElementInfo *ei = &element_info[element];
8499   int direction = MovDir[x][y];
8500   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8501   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8502   int newx = x + dx, newy = y + dy;
8503   int stored = Store[x][y];
8504   int stored_new = Store[newx][newy];
8505   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8506   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8507   boolean last_line = (newy == lev_fieldy - 1);
8508
8509   MovPos[x][y] += getElementMoveStepsize(x, y);
8510
8511   if (pushed_by_player) // special case: moving object pushed by player
8512     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8513
8514   if (ABS(MovPos[x][y]) < TILEX)
8515   {
8516     TEST_DrawLevelField(x, y);
8517
8518     return;     // element is still moving
8519   }
8520
8521   // element reached destination field
8522
8523   Tile[x][y] = EL_EMPTY;
8524   Tile[newx][newy] = element;
8525   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8526
8527   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8528   {
8529     element = Tile[newx][newy] = EL_ACID;
8530   }
8531   else if (element == EL_MOLE)
8532   {
8533     Tile[x][y] = EL_SAND;
8534
8535     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8536   }
8537   else if (element == EL_QUICKSAND_FILLING)
8538   {
8539     element = Tile[newx][newy] = get_next_element(element);
8540     Store[newx][newy] = Store[x][y];
8541   }
8542   else if (element == EL_QUICKSAND_EMPTYING)
8543   {
8544     Tile[x][y] = get_next_element(element);
8545     element = Tile[newx][newy] = Store[x][y];
8546   }
8547   else if (element == EL_QUICKSAND_FAST_FILLING)
8548   {
8549     element = Tile[newx][newy] = get_next_element(element);
8550     Store[newx][newy] = Store[x][y];
8551   }
8552   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8553   {
8554     Tile[x][y] = get_next_element(element);
8555     element = Tile[newx][newy] = Store[x][y];
8556   }
8557   else if (element == EL_MAGIC_WALL_FILLING)
8558   {
8559     element = Tile[newx][newy] = get_next_element(element);
8560     if (!game.magic_wall_active)
8561       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8562     Store[newx][newy] = Store[x][y];
8563   }
8564   else if (element == EL_MAGIC_WALL_EMPTYING)
8565   {
8566     Tile[x][y] = get_next_element(element);
8567     if (!game.magic_wall_active)
8568       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8569     element = Tile[newx][newy] = Store[x][y];
8570
8571     InitField(newx, newy, FALSE);
8572   }
8573   else if (element == EL_BD_MAGIC_WALL_FILLING)
8574   {
8575     element = Tile[newx][newy] = get_next_element(element);
8576     if (!game.magic_wall_active)
8577       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8578     Store[newx][newy] = Store[x][y];
8579   }
8580   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8581   {
8582     Tile[x][y] = get_next_element(element);
8583     if (!game.magic_wall_active)
8584       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8585     element = Tile[newx][newy] = Store[x][y];
8586
8587     InitField(newx, newy, FALSE);
8588   }
8589   else if (element == EL_DC_MAGIC_WALL_FILLING)
8590   {
8591     element = Tile[newx][newy] = get_next_element(element);
8592     if (!game.magic_wall_active)
8593       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8594     Store[newx][newy] = Store[x][y];
8595   }
8596   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8597   {
8598     Tile[x][y] = get_next_element(element);
8599     if (!game.magic_wall_active)
8600       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8601     element = Tile[newx][newy] = Store[x][y];
8602
8603     InitField(newx, newy, FALSE);
8604   }
8605   else if (element == EL_AMOEBA_DROPPING)
8606   {
8607     Tile[x][y] = get_next_element(element);
8608     element = Tile[newx][newy] = Store[x][y];
8609   }
8610   else if (element == EL_SOKOBAN_OBJECT)
8611   {
8612     if (Back[x][y])
8613       Tile[x][y] = Back[x][y];
8614
8615     if (Back[newx][newy])
8616       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8617
8618     Back[x][y] = Back[newx][newy] = 0;
8619   }
8620
8621   Store[x][y] = EL_EMPTY;
8622   MovPos[x][y] = 0;
8623   MovDir[x][y] = 0;
8624   MovDelay[x][y] = 0;
8625
8626   MovDelay[newx][newy] = 0;
8627
8628   if (CAN_CHANGE_OR_HAS_ACTION(element))
8629   {
8630     // copy element change control values to new field
8631     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8632     ChangePage[newx][newy]  = ChangePage[x][y];
8633     ChangeCount[newx][newy] = ChangeCount[x][y];
8634     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8635   }
8636
8637   CustomValue[newx][newy] = CustomValue[x][y];
8638
8639   ChangeDelay[x][y] = 0;
8640   ChangePage[x][y] = -1;
8641   ChangeCount[x][y] = 0;
8642   ChangeEvent[x][y] = -1;
8643
8644   CustomValue[x][y] = 0;
8645
8646   // copy animation control values to new field
8647   GfxFrame[newx][newy]  = GfxFrame[x][y];
8648   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8649   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8650   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8651
8652   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8653
8654   // some elements can leave other elements behind after moving
8655   if (ei->move_leave_element != EL_EMPTY &&
8656       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8657       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8658   {
8659     int move_leave_element = ei->move_leave_element;
8660
8661     // this makes it possible to leave the removed element again
8662     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8663       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8664
8665     Tile[x][y] = move_leave_element;
8666
8667     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8668       MovDir[x][y] = direction;
8669
8670     InitField(x, y, FALSE);
8671
8672     if (GFX_CRUMBLED(Tile[x][y]))
8673       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8674
8675     if (ELEM_IS_PLAYER(move_leave_element))
8676       RelocatePlayer(x, y, move_leave_element);
8677   }
8678
8679   // do this after checking for left-behind element
8680   ResetGfxAnimation(x, y);      // reset animation values for old field
8681
8682   if (!CAN_MOVE(element) ||
8683       (CAN_FALL(element) && direction == MV_DOWN &&
8684        (element == EL_SPRING ||
8685         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8686         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8687     GfxDir[x][y] = MovDir[newx][newy] = 0;
8688
8689   TEST_DrawLevelField(x, y);
8690   TEST_DrawLevelField(newx, newy);
8691
8692   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8693
8694   // prevent pushed element from moving on in pushed direction
8695   if (pushed_by_player && CAN_MOVE(element) &&
8696       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8697       !(element_info[element].move_pattern & direction))
8698     TurnRound(newx, newy);
8699
8700   // prevent elements on conveyor belt from moving on in last direction
8701   if (pushed_by_conveyor && CAN_FALL(element) &&
8702       direction & MV_HORIZONTAL)
8703     MovDir[newx][newy] = 0;
8704
8705   if (!pushed_by_player)
8706   {
8707     int nextx = newx + dx, nexty = newy + dy;
8708     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8709
8710     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8711
8712     if (CAN_FALL(element) && direction == MV_DOWN)
8713       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8714
8715     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8716       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8717
8718     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8719       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8720   }
8721
8722   if (DONT_TOUCH(element))      // object may be nasty to player or others
8723   {
8724     TestIfBadThingTouchesPlayer(newx, newy);
8725     TestIfBadThingTouchesFriend(newx, newy);
8726
8727     if (!IS_CUSTOM_ELEMENT(element))
8728       TestIfBadThingTouchesOtherBadThing(newx, newy);
8729   }
8730   else if (element == EL_PENGUIN)
8731     TestIfFriendTouchesBadThing(newx, newy);
8732
8733   if (DONT_GET_HIT_BY(element))
8734   {
8735     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8736   }
8737
8738   // give the player one last chance (one more frame) to move away
8739   if (CAN_FALL(element) && direction == MV_DOWN &&
8740       (last_line || (!IS_FREE(x, newy + 1) &&
8741                      (!IS_PLAYER(x, newy + 1) ||
8742                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8743     Impact(x, newy);
8744
8745   if (pushed_by_player && !game.use_change_when_pushing_bug)
8746   {
8747     int push_side = MV_DIR_OPPOSITE(direction);
8748     struct PlayerInfo *player = PLAYERINFO(x, y);
8749
8750     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8751                                player->index_bit, push_side);
8752     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8753                                         player->index_bit, push_side);
8754   }
8755
8756   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8757     MovDelay[newx][newy] = 1;
8758
8759   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8760
8761   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8762   TestIfElementHitsCustomElement(newx, newy, direction);
8763   TestIfPlayerTouchesCustomElement(newx, newy);
8764   TestIfElementTouchesCustomElement(newx, newy);
8765
8766   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8767       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8768     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8769                              MV_DIR_OPPOSITE(direction));
8770 }
8771
8772 int AmoebaNeighbourNr(int ax, int ay)
8773 {
8774   int i;
8775   int element = Tile[ax][ay];
8776   int group_nr = 0;
8777   static int xy[4][2] =
8778   {
8779     { 0, -1 },
8780     { -1, 0 },
8781     { +1, 0 },
8782     { 0, +1 }
8783   };
8784
8785   for (i = 0; i < NUM_DIRECTIONS; i++)
8786   {
8787     int x = ax + xy[i][0];
8788     int y = ay + xy[i][1];
8789
8790     if (!IN_LEV_FIELD(x, y))
8791       continue;
8792
8793     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8794       group_nr = AmoebaNr[x][y];
8795   }
8796
8797   return group_nr;
8798 }
8799
8800 static void AmoebaMerge(int ax, int ay)
8801 {
8802   int i, x, y, xx, yy;
8803   int new_group_nr = AmoebaNr[ax][ay];
8804   static int xy[4][2] =
8805   {
8806     { 0, -1 },
8807     { -1, 0 },
8808     { +1, 0 },
8809     { 0, +1 }
8810   };
8811
8812   if (new_group_nr == 0)
8813     return;
8814
8815   for (i = 0; i < NUM_DIRECTIONS; i++)
8816   {
8817     x = ax + xy[i][0];
8818     y = ay + xy[i][1];
8819
8820     if (!IN_LEV_FIELD(x, y))
8821       continue;
8822
8823     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8824          Tile[x][y] == EL_BD_AMOEBA ||
8825          Tile[x][y] == EL_AMOEBA_DEAD) &&
8826         AmoebaNr[x][y] != new_group_nr)
8827     {
8828       int old_group_nr = AmoebaNr[x][y];
8829
8830       if (old_group_nr == 0)
8831         return;
8832
8833       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8834       AmoebaCnt[old_group_nr] = 0;
8835       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8836       AmoebaCnt2[old_group_nr] = 0;
8837
8838       SCAN_PLAYFIELD(xx, yy)
8839       {
8840         if (AmoebaNr[xx][yy] == old_group_nr)
8841           AmoebaNr[xx][yy] = new_group_nr;
8842       }
8843     }
8844   }
8845 }
8846
8847 void AmoebaToDiamond(int ax, int ay)
8848 {
8849   int i, x, y;
8850
8851   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8852   {
8853     int group_nr = AmoebaNr[ax][ay];
8854
8855 #ifdef DEBUG
8856     if (group_nr == 0)
8857     {
8858       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8859       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8860
8861       return;
8862     }
8863 #endif
8864
8865     SCAN_PLAYFIELD(x, y)
8866     {
8867       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8868       {
8869         AmoebaNr[x][y] = 0;
8870         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8871       }
8872     }
8873
8874     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8875                             SND_AMOEBA_TURNING_TO_GEM :
8876                             SND_AMOEBA_TURNING_TO_ROCK));
8877     Bang(ax, ay);
8878   }
8879   else
8880   {
8881     static int xy[4][2] =
8882     {
8883       { 0, -1 },
8884       { -1, 0 },
8885       { +1, 0 },
8886       { 0, +1 }
8887     };
8888
8889     for (i = 0; i < NUM_DIRECTIONS; i++)
8890     {
8891       x = ax + xy[i][0];
8892       y = ay + xy[i][1];
8893
8894       if (!IN_LEV_FIELD(x, y))
8895         continue;
8896
8897       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8898       {
8899         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8900                               SND_AMOEBA_TURNING_TO_GEM :
8901                               SND_AMOEBA_TURNING_TO_ROCK));
8902         Bang(x, y);
8903       }
8904     }
8905   }
8906 }
8907
8908 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8909 {
8910   int x, y;
8911   int group_nr = AmoebaNr[ax][ay];
8912   boolean done = FALSE;
8913
8914 #ifdef DEBUG
8915   if (group_nr == 0)
8916   {
8917     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8918     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8919
8920     return;
8921   }
8922 #endif
8923
8924   SCAN_PLAYFIELD(x, y)
8925   {
8926     if (AmoebaNr[x][y] == group_nr &&
8927         (Tile[x][y] == EL_AMOEBA_DEAD ||
8928          Tile[x][y] == EL_BD_AMOEBA ||
8929          Tile[x][y] == EL_AMOEBA_GROWING))
8930     {
8931       AmoebaNr[x][y] = 0;
8932       Tile[x][y] = new_element;
8933       InitField(x, y, FALSE);
8934       TEST_DrawLevelField(x, y);
8935       done = TRUE;
8936     }
8937   }
8938
8939   if (done)
8940     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8941                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8942                             SND_BD_AMOEBA_TURNING_TO_GEM));
8943 }
8944
8945 static void AmoebaGrowing(int x, int y)
8946 {
8947   static unsigned int sound_delay = 0;
8948   static unsigned int sound_delay_value = 0;
8949
8950   if (!MovDelay[x][y])          // start new growing cycle
8951   {
8952     MovDelay[x][y] = 7;
8953
8954     if (DelayReached(&sound_delay, sound_delay_value))
8955     {
8956       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8957       sound_delay_value = 30;
8958     }
8959   }
8960
8961   if (MovDelay[x][y])           // wait some time before growing bigger
8962   {
8963     MovDelay[x][y]--;
8964     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8965     {
8966       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8967                                            6 - MovDelay[x][y]);
8968
8969       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8970     }
8971
8972     if (!MovDelay[x][y])
8973     {
8974       Tile[x][y] = Store[x][y];
8975       Store[x][y] = 0;
8976       TEST_DrawLevelField(x, y);
8977     }
8978   }
8979 }
8980
8981 static void AmoebaShrinking(int x, int y)
8982 {
8983   static unsigned int sound_delay = 0;
8984   static unsigned int sound_delay_value = 0;
8985
8986   if (!MovDelay[x][y])          // start new shrinking cycle
8987   {
8988     MovDelay[x][y] = 7;
8989
8990     if (DelayReached(&sound_delay, sound_delay_value))
8991       sound_delay_value = 30;
8992   }
8993
8994   if (MovDelay[x][y])           // wait some time before shrinking
8995   {
8996     MovDelay[x][y]--;
8997     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8998     {
8999       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9000                                            6 - MovDelay[x][y]);
9001
9002       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9003     }
9004
9005     if (!MovDelay[x][y])
9006     {
9007       Tile[x][y] = EL_EMPTY;
9008       TEST_DrawLevelField(x, y);
9009
9010       // don't let mole enter this field in this cycle;
9011       // (give priority to objects falling to this field from above)
9012       Stop[x][y] = TRUE;
9013     }
9014   }
9015 }
9016
9017 static void AmoebaReproduce(int ax, int ay)
9018 {
9019   int i;
9020   int element = Tile[ax][ay];
9021   int graphic = el2img(element);
9022   int newax = ax, neway = ay;
9023   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9024   static int xy[4][2] =
9025   {
9026     { 0, -1 },
9027     { -1, 0 },
9028     { +1, 0 },
9029     { 0, +1 }
9030   };
9031
9032   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9033   {
9034     Tile[ax][ay] = EL_AMOEBA_DEAD;
9035     TEST_DrawLevelField(ax, ay);
9036     return;
9037   }
9038
9039   if (IS_ANIMATED(graphic))
9040     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9041
9042   if (!MovDelay[ax][ay])        // start making new amoeba field
9043     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9044
9045   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9046   {
9047     MovDelay[ax][ay]--;
9048     if (MovDelay[ax][ay])
9049       return;
9050   }
9051
9052   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9053   {
9054     int start = RND(4);
9055     int x = ax + xy[start][0];
9056     int y = ay + xy[start][1];
9057
9058     if (!IN_LEV_FIELD(x, y))
9059       return;
9060
9061     if (IS_FREE(x, y) ||
9062         CAN_GROW_INTO(Tile[x][y]) ||
9063         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9064         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9065     {
9066       newax = x;
9067       neway = y;
9068     }
9069
9070     if (newax == ax && neway == ay)
9071       return;
9072   }
9073   else                          // normal or "filled" (BD style) amoeba
9074   {
9075     int start = RND(4);
9076     boolean waiting_for_player = FALSE;
9077
9078     for (i = 0; i < NUM_DIRECTIONS; i++)
9079     {
9080       int j = (start + i) % 4;
9081       int x = ax + xy[j][0];
9082       int y = ay + xy[j][1];
9083
9084       if (!IN_LEV_FIELD(x, y))
9085         continue;
9086
9087       if (IS_FREE(x, y) ||
9088           CAN_GROW_INTO(Tile[x][y]) ||
9089           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9090           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9091       {
9092         newax = x;
9093         neway = y;
9094         break;
9095       }
9096       else if (IS_PLAYER(x, y))
9097         waiting_for_player = TRUE;
9098     }
9099
9100     if (newax == ax && neway == ay)             // amoeba cannot grow
9101     {
9102       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9103       {
9104         Tile[ax][ay] = EL_AMOEBA_DEAD;
9105         TEST_DrawLevelField(ax, ay);
9106         AmoebaCnt[AmoebaNr[ax][ay]]--;
9107
9108         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9109         {
9110           if (element == EL_AMOEBA_FULL)
9111             AmoebaToDiamond(ax, ay);
9112           else if (element == EL_BD_AMOEBA)
9113             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9114         }
9115       }
9116       return;
9117     }
9118     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9119     {
9120       // amoeba gets larger by growing in some direction
9121
9122       int new_group_nr = AmoebaNr[ax][ay];
9123
9124 #ifdef DEBUG
9125   if (new_group_nr == 0)
9126   {
9127     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9128           newax, neway);
9129     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9130
9131     return;
9132   }
9133 #endif
9134
9135       AmoebaNr[newax][neway] = new_group_nr;
9136       AmoebaCnt[new_group_nr]++;
9137       AmoebaCnt2[new_group_nr]++;
9138
9139       // if amoeba touches other amoeba(s) after growing, unify them
9140       AmoebaMerge(newax, neway);
9141
9142       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9143       {
9144         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9145         return;
9146       }
9147     }
9148   }
9149
9150   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9151       (neway == lev_fieldy - 1 && newax != ax))
9152   {
9153     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9154     Store[newax][neway] = element;
9155   }
9156   else if (neway == ay || element == EL_EMC_DRIPPER)
9157   {
9158     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9159
9160     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9161   }
9162   else
9163   {
9164     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9165     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9166     Store[ax][ay] = EL_AMOEBA_DROP;
9167     ContinueMoving(ax, ay);
9168     return;
9169   }
9170
9171   TEST_DrawLevelField(newax, neway);
9172 }
9173
9174 static void Life(int ax, int ay)
9175 {
9176   int x1, y1, x2, y2;
9177   int life_time = 40;
9178   int element = Tile[ax][ay];
9179   int graphic = el2img(element);
9180   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9181                          level.biomaze);
9182   boolean changed = FALSE;
9183
9184   if (IS_ANIMATED(graphic))
9185     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9186
9187   if (Stop[ax][ay])
9188     return;
9189
9190   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9191     MovDelay[ax][ay] = life_time;
9192
9193   if (MovDelay[ax][ay])         // wait some time before next cycle
9194   {
9195     MovDelay[ax][ay]--;
9196     if (MovDelay[ax][ay])
9197       return;
9198   }
9199
9200   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9201   {
9202     int xx = ax+x1, yy = ay+y1;
9203     int old_element = Tile[xx][yy];
9204     int num_neighbours = 0;
9205
9206     if (!IN_LEV_FIELD(xx, yy))
9207       continue;
9208
9209     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9210     {
9211       int x = xx+x2, y = yy+y2;
9212
9213       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9214         continue;
9215
9216       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9217       boolean is_neighbour = FALSE;
9218
9219       if (level.use_life_bugs)
9220         is_neighbour =
9221           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9222            (IS_FREE(x, y)                             &&  Stop[x][y]));
9223       else
9224         is_neighbour =
9225           (Last[x][y] == element || is_player_cell);
9226
9227       if (is_neighbour)
9228         num_neighbours++;
9229     }
9230
9231     boolean is_free = FALSE;
9232
9233     if (level.use_life_bugs)
9234       is_free = (IS_FREE(xx, yy));
9235     else
9236       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9237
9238     if (xx == ax && yy == ay)           // field in the middle
9239     {
9240       if (num_neighbours < life_parameter[0] ||
9241           num_neighbours > life_parameter[1])
9242       {
9243         Tile[xx][yy] = EL_EMPTY;
9244         if (Tile[xx][yy] != old_element)
9245           TEST_DrawLevelField(xx, yy);
9246         Stop[xx][yy] = TRUE;
9247         changed = TRUE;
9248       }
9249     }
9250     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9251     {                                   // free border field
9252       if (num_neighbours >= life_parameter[2] &&
9253           num_neighbours <= life_parameter[3])
9254       {
9255         Tile[xx][yy] = element;
9256         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9257         if (Tile[xx][yy] != old_element)
9258           TEST_DrawLevelField(xx, yy);
9259         Stop[xx][yy] = TRUE;
9260         changed = TRUE;
9261       }
9262     }
9263   }
9264
9265   if (changed)
9266     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9267                    SND_GAME_OF_LIFE_GROWING);
9268 }
9269
9270 static void InitRobotWheel(int x, int y)
9271 {
9272   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9273 }
9274
9275 static void RunRobotWheel(int x, int y)
9276 {
9277   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9278 }
9279
9280 static void StopRobotWheel(int x, int y)
9281 {
9282   if (game.robot_wheel_x == x &&
9283       game.robot_wheel_y == y)
9284   {
9285     game.robot_wheel_x = -1;
9286     game.robot_wheel_y = -1;
9287     game.robot_wheel_active = FALSE;
9288   }
9289 }
9290
9291 static void InitTimegateWheel(int x, int y)
9292 {
9293   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9294 }
9295
9296 static void RunTimegateWheel(int x, int y)
9297 {
9298   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9299 }
9300
9301 static void InitMagicBallDelay(int x, int y)
9302 {
9303   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9304 }
9305
9306 static void ActivateMagicBall(int bx, int by)
9307 {
9308   int x, y;
9309
9310   if (level.ball_random)
9311   {
9312     int pos_border = RND(8);    // select one of the eight border elements
9313     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9314     int xx = pos_content % 3;
9315     int yy = pos_content / 3;
9316
9317     x = bx - 1 + xx;
9318     y = by - 1 + yy;
9319
9320     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9321       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9322   }
9323   else
9324   {
9325     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9326     {
9327       int xx = x - bx + 1;
9328       int yy = y - by + 1;
9329
9330       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9331         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9332     }
9333   }
9334
9335   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9336 }
9337
9338 static void CheckExit(int x, int y)
9339 {
9340   if (game.gems_still_needed > 0 ||
9341       game.sokoban_fields_still_needed > 0 ||
9342       game.sokoban_objects_still_needed > 0 ||
9343       game.lights_still_needed > 0)
9344   {
9345     int element = Tile[x][y];
9346     int graphic = el2img(element);
9347
9348     if (IS_ANIMATED(graphic))
9349       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9350
9351     return;
9352   }
9353
9354   // do not re-open exit door closed after last player
9355   if (game.all_players_gone)
9356     return;
9357
9358   Tile[x][y] = EL_EXIT_OPENING;
9359
9360   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9361 }
9362
9363 static void CheckExitEM(int x, int y)
9364 {
9365   if (game.gems_still_needed > 0 ||
9366       game.sokoban_fields_still_needed > 0 ||
9367       game.sokoban_objects_still_needed > 0 ||
9368       game.lights_still_needed > 0)
9369   {
9370     int element = Tile[x][y];
9371     int graphic = el2img(element);
9372
9373     if (IS_ANIMATED(graphic))
9374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9375
9376     return;
9377   }
9378
9379   // do not re-open exit door closed after last player
9380   if (game.all_players_gone)
9381     return;
9382
9383   Tile[x][y] = EL_EM_EXIT_OPENING;
9384
9385   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9386 }
9387
9388 static void CheckExitSteel(int x, int y)
9389 {
9390   if (game.gems_still_needed > 0 ||
9391       game.sokoban_fields_still_needed > 0 ||
9392       game.sokoban_objects_still_needed > 0 ||
9393       game.lights_still_needed > 0)
9394   {
9395     int element = Tile[x][y];
9396     int graphic = el2img(element);
9397
9398     if (IS_ANIMATED(graphic))
9399       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9400
9401     return;
9402   }
9403
9404   // do not re-open exit door closed after last player
9405   if (game.all_players_gone)
9406     return;
9407
9408   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9409
9410   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9411 }
9412
9413 static void CheckExitSteelEM(int x, int y)
9414 {
9415   if (game.gems_still_needed > 0 ||
9416       game.sokoban_fields_still_needed > 0 ||
9417       game.sokoban_objects_still_needed > 0 ||
9418       game.lights_still_needed > 0)
9419   {
9420     int element = Tile[x][y];
9421     int graphic = el2img(element);
9422
9423     if (IS_ANIMATED(graphic))
9424       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9425
9426     return;
9427   }
9428
9429   // do not re-open exit door closed after last player
9430   if (game.all_players_gone)
9431     return;
9432
9433   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9434
9435   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9436 }
9437
9438 static void CheckExitSP(int x, int y)
9439 {
9440   if (game.gems_still_needed > 0)
9441   {
9442     int element = Tile[x][y];
9443     int graphic = el2img(element);
9444
9445     if (IS_ANIMATED(graphic))
9446       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9447
9448     return;
9449   }
9450
9451   // do not re-open exit door closed after last player
9452   if (game.all_players_gone)
9453     return;
9454
9455   Tile[x][y] = EL_SP_EXIT_OPENING;
9456
9457   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9458 }
9459
9460 static void CloseAllOpenTimegates(void)
9461 {
9462   int x, y;
9463
9464   SCAN_PLAYFIELD(x, y)
9465   {
9466     int element = Tile[x][y];
9467
9468     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9469     {
9470       Tile[x][y] = EL_TIMEGATE_CLOSING;
9471
9472       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9473     }
9474   }
9475 }
9476
9477 static void DrawTwinkleOnField(int x, int y)
9478 {
9479   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9480     return;
9481
9482   if (Tile[x][y] == EL_BD_DIAMOND)
9483     return;
9484
9485   if (MovDelay[x][y] == 0)      // next animation frame
9486     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9487
9488   if (MovDelay[x][y] != 0)      // wait some time before next frame
9489   {
9490     MovDelay[x][y]--;
9491
9492     DrawLevelElementAnimation(x, y, Tile[x][y]);
9493
9494     if (MovDelay[x][y] != 0)
9495     {
9496       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9497                                            10 - MovDelay[x][y]);
9498
9499       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9500     }
9501   }
9502 }
9503
9504 static void MauerWaechst(int x, int y)
9505 {
9506   int delay = 6;
9507
9508   if (!MovDelay[x][y])          // next animation frame
9509     MovDelay[x][y] = 3 * delay;
9510
9511   if (MovDelay[x][y])           // wait some time before next frame
9512   {
9513     MovDelay[x][y]--;
9514
9515     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9516     {
9517       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9518       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9519
9520       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9521     }
9522
9523     if (!MovDelay[x][y])
9524     {
9525       if (MovDir[x][y] == MV_LEFT)
9526       {
9527         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9528           TEST_DrawLevelField(x - 1, y);
9529       }
9530       else if (MovDir[x][y] == MV_RIGHT)
9531       {
9532         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9533           TEST_DrawLevelField(x + 1, y);
9534       }
9535       else if (MovDir[x][y] == MV_UP)
9536       {
9537         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9538           TEST_DrawLevelField(x, y - 1);
9539       }
9540       else
9541       {
9542         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9543           TEST_DrawLevelField(x, y + 1);
9544       }
9545
9546       Tile[x][y] = Store[x][y];
9547       Store[x][y] = 0;
9548       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9549       TEST_DrawLevelField(x, y);
9550     }
9551   }
9552 }
9553
9554 static void MauerAbleger(int ax, int ay)
9555 {
9556   int element = Tile[ax][ay];
9557   int graphic = el2img(element);
9558   boolean oben_frei = FALSE, unten_frei = FALSE;
9559   boolean links_frei = FALSE, rechts_frei = FALSE;
9560   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9561   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9562   boolean new_wall = FALSE;
9563
9564   if (IS_ANIMATED(graphic))
9565     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9566
9567   if (!MovDelay[ax][ay])        // start building new wall
9568     MovDelay[ax][ay] = 6;
9569
9570   if (MovDelay[ax][ay])         // wait some time before building new wall
9571   {
9572     MovDelay[ax][ay]--;
9573     if (MovDelay[ax][ay])
9574       return;
9575   }
9576
9577   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9578     oben_frei = TRUE;
9579   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9580     unten_frei = TRUE;
9581   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9582     links_frei = TRUE;
9583   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9584     rechts_frei = TRUE;
9585
9586   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9587       element == EL_EXPANDABLE_WALL_ANY)
9588   {
9589     if (oben_frei)
9590     {
9591       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9592       Store[ax][ay-1] = element;
9593       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9594       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9595         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9596                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9597       new_wall = TRUE;
9598     }
9599     if (unten_frei)
9600     {
9601       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9602       Store[ax][ay+1] = element;
9603       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9604       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9605         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9606                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9607       new_wall = TRUE;
9608     }
9609   }
9610
9611   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9612       element == EL_EXPANDABLE_WALL_ANY ||
9613       element == EL_EXPANDABLE_WALL ||
9614       element == EL_BD_EXPANDABLE_WALL)
9615   {
9616     if (links_frei)
9617     {
9618       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9619       Store[ax-1][ay] = element;
9620       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9621       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9622         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9623                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9624       new_wall = TRUE;
9625     }
9626
9627     if (rechts_frei)
9628     {
9629       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9630       Store[ax+1][ay] = element;
9631       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9632       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9633         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9634                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9635       new_wall = TRUE;
9636     }
9637   }
9638
9639   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9640     TEST_DrawLevelField(ax, ay);
9641
9642   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9643     oben_massiv = TRUE;
9644   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9645     unten_massiv = TRUE;
9646   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9647     links_massiv = TRUE;
9648   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9649     rechts_massiv = TRUE;
9650
9651   if (((oben_massiv && unten_massiv) ||
9652        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9653        element == EL_EXPANDABLE_WALL) &&
9654       ((links_massiv && rechts_massiv) ||
9655        element == EL_EXPANDABLE_WALL_VERTICAL))
9656     Tile[ax][ay] = EL_WALL;
9657
9658   if (new_wall)
9659     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9660 }
9661
9662 static void MauerAblegerStahl(int ax, int ay)
9663 {
9664   int element = Tile[ax][ay];
9665   int graphic = el2img(element);
9666   boolean oben_frei = FALSE, unten_frei = FALSE;
9667   boolean links_frei = FALSE, rechts_frei = FALSE;
9668   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9669   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9670   boolean new_wall = FALSE;
9671
9672   if (IS_ANIMATED(graphic))
9673     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9674
9675   if (!MovDelay[ax][ay])        // start building new wall
9676     MovDelay[ax][ay] = 6;
9677
9678   if (MovDelay[ax][ay])         // wait some time before building new wall
9679   {
9680     MovDelay[ax][ay]--;
9681     if (MovDelay[ax][ay])
9682       return;
9683   }
9684
9685   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9686     oben_frei = TRUE;
9687   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9688     unten_frei = TRUE;
9689   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9690     links_frei = TRUE;
9691   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9692     rechts_frei = TRUE;
9693
9694   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9695       element == EL_EXPANDABLE_STEELWALL_ANY)
9696   {
9697     if (oben_frei)
9698     {
9699       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9700       Store[ax][ay-1] = element;
9701       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9702       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9703         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9704                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9705       new_wall = TRUE;
9706     }
9707     if (unten_frei)
9708     {
9709       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9710       Store[ax][ay+1] = element;
9711       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9712       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9713         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9714                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9715       new_wall = TRUE;
9716     }
9717   }
9718
9719   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9720       element == EL_EXPANDABLE_STEELWALL_ANY)
9721   {
9722     if (links_frei)
9723     {
9724       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9725       Store[ax-1][ay] = element;
9726       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9727       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9728         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9729                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9730       new_wall = TRUE;
9731     }
9732
9733     if (rechts_frei)
9734     {
9735       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9736       Store[ax+1][ay] = element;
9737       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9738       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9739         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9740                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9741       new_wall = TRUE;
9742     }
9743   }
9744
9745   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9746     oben_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9748     unten_massiv = TRUE;
9749   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9750     links_massiv = TRUE;
9751   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9752     rechts_massiv = TRUE;
9753
9754   if (((oben_massiv && unten_massiv) ||
9755        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9756       ((links_massiv && rechts_massiv) ||
9757        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9758     Tile[ax][ay] = EL_STEELWALL;
9759
9760   if (new_wall)
9761     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9762 }
9763
9764 static void CheckForDragon(int x, int y)
9765 {
9766   int i, j;
9767   boolean dragon_found = FALSE;
9768   static int xy[4][2] =
9769   {
9770     { 0, -1 },
9771     { -1, 0 },
9772     { +1, 0 },
9773     { 0, +1 }
9774   };
9775
9776   for (i = 0; i < NUM_DIRECTIONS; i++)
9777   {
9778     for (j = 0; j < 4; j++)
9779     {
9780       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9781
9782       if (IN_LEV_FIELD(xx, yy) &&
9783           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9784       {
9785         if (Tile[xx][yy] == EL_DRAGON)
9786           dragon_found = TRUE;
9787       }
9788       else
9789         break;
9790     }
9791   }
9792
9793   if (!dragon_found)
9794   {
9795     for (i = 0; i < NUM_DIRECTIONS; i++)
9796     {
9797       for (j = 0; j < 3; j++)
9798       {
9799         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9800   
9801         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9802         {
9803           Tile[xx][yy] = EL_EMPTY;
9804           TEST_DrawLevelField(xx, yy);
9805         }
9806         else
9807           break;
9808       }
9809     }
9810   }
9811 }
9812
9813 static void InitBuggyBase(int x, int y)
9814 {
9815   int element = Tile[x][y];
9816   int activating_delay = FRAMES_PER_SECOND / 4;
9817
9818   ChangeDelay[x][y] =
9819     (element == EL_SP_BUGGY_BASE ?
9820      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9821      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9822      activating_delay :
9823      element == EL_SP_BUGGY_BASE_ACTIVE ?
9824      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9825 }
9826
9827 static void WarnBuggyBase(int x, int y)
9828 {
9829   int i;
9830   static int xy[4][2] =
9831   {
9832     { 0, -1 },
9833     { -1, 0 },
9834     { +1, 0 },
9835     { 0, +1 }
9836   };
9837
9838   for (i = 0; i < NUM_DIRECTIONS; i++)
9839   {
9840     int xx = x + xy[i][0];
9841     int yy = y + xy[i][1];
9842
9843     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9844     {
9845       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9846
9847       break;
9848     }
9849   }
9850 }
9851
9852 static void InitTrap(int x, int y)
9853 {
9854   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9855 }
9856
9857 static void ActivateTrap(int x, int y)
9858 {
9859   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9860 }
9861
9862 static void ChangeActiveTrap(int x, int y)
9863 {
9864   int graphic = IMG_TRAP_ACTIVE;
9865
9866   // if new animation frame was drawn, correct crumbled sand border
9867   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9868     TEST_DrawLevelFieldCrumbled(x, y);
9869 }
9870
9871 static int getSpecialActionElement(int element, int number, int base_element)
9872 {
9873   return (element != EL_EMPTY ? element :
9874           number != -1 ? base_element + number - 1 :
9875           EL_EMPTY);
9876 }
9877
9878 static int getModifiedActionNumber(int value_old, int operator, int operand,
9879                                    int value_min, int value_max)
9880 {
9881   int value_new = (operator == CA_MODE_SET      ? operand :
9882                    operator == CA_MODE_ADD      ? value_old + operand :
9883                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9884                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9885                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9886                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9887                    value_old);
9888
9889   return (value_new < value_min ? value_min :
9890           value_new > value_max ? value_max :
9891           value_new);
9892 }
9893
9894 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9895 {
9896   struct ElementInfo *ei = &element_info[element];
9897   struct ElementChangeInfo *change = &ei->change_page[page];
9898   int target_element = change->target_element;
9899   int action_type = change->action_type;
9900   int action_mode = change->action_mode;
9901   int action_arg = change->action_arg;
9902   int action_element = change->action_element;
9903   int i;
9904
9905   if (!change->has_action)
9906     return;
9907
9908   // ---------- determine action paramater values -----------------------------
9909
9910   int level_time_value =
9911     (level.time > 0 ? TimeLeft :
9912      TimePlayed);
9913
9914   int action_arg_element_raw =
9915     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9916      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9917      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9918      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9919      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9920      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9921      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9922      EL_EMPTY);
9923   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9924
9925   int action_arg_direction =
9926     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9927      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9928      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9929      change->actual_trigger_side :
9930      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9931      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9932      MV_NONE);
9933
9934   int action_arg_number_min =
9935     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9936      CA_ARG_MIN);
9937
9938   int action_arg_number_max =
9939     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9940      action_type == CA_SET_LEVEL_GEMS ? 999 :
9941      action_type == CA_SET_LEVEL_TIME ? 9999 :
9942      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9943      action_type == CA_SET_CE_VALUE ? 9999 :
9944      action_type == CA_SET_CE_SCORE ? 9999 :
9945      CA_ARG_MAX);
9946
9947   int action_arg_number_reset =
9948     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9949      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9950      action_type == CA_SET_LEVEL_TIME ? level.time :
9951      action_type == CA_SET_LEVEL_SCORE ? 0 :
9952      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9953      action_type == CA_SET_CE_SCORE ? 0 :
9954      0);
9955
9956   int action_arg_number =
9957     (action_arg <= CA_ARG_MAX ? action_arg :
9958      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9959      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9960      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9961      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9962      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9963      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9964      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9965      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9966      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9967      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9968      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9969      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9970      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9971      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9972      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9973      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9974      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9975      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9976      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9977      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9978      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9979      -1);
9980
9981   int action_arg_number_old =
9982     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9983      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9984      action_type == CA_SET_LEVEL_SCORE ? game.score :
9985      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9986      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9987      0);
9988
9989   int action_arg_number_new =
9990     getModifiedActionNumber(action_arg_number_old,
9991                             action_mode, action_arg_number,
9992                             action_arg_number_min, action_arg_number_max);
9993
9994   int trigger_player_bits =
9995     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9996      change->actual_trigger_player_bits : change->trigger_player);
9997
9998   int action_arg_player_bits =
9999     (action_arg >= CA_ARG_PLAYER_1 &&
10000      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10001      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10002      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10003      PLAYER_BITS_ANY);
10004
10005   // ---------- execute action  -----------------------------------------------
10006
10007   switch (action_type)
10008   {
10009     case CA_NO_ACTION:
10010     {
10011       return;
10012     }
10013
10014     // ---------- level actions  ----------------------------------------------
10015
10016     case CA_RESTART_LEVEL:
10017     {
10018       game.restart_level = TRUE;
10019
10020       break;
10021     }
10022
10023     case CA_SHOW_ENVELOPE:
10024     {
10025       int element = getSpecialActionElement(action_arg_element,
10026                                             action_arg_number, EL_ENVELOPE_1);
10027
10028       if (IS_ENVELOPE(element))
10029         local_player->show_envelope = element;
10030
10031       break;
10032     }
10033
10034     case CA_SET_LEVEL_TIME:
10035     {
10036       if (level.time > 0)       // only modify limited time value
10037       {
10038         TimeLeft = action_arg_number_new;
10039
10040         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10041
10042         DisplayGameControlValues();
10043
10044         if (!TimeLeft && setup.time_limit)
10045           for (i = 0; i < MAX_PLAYERS; i++)
10046             KillPlayer(&stored_player[i]);
10047       }
10048
10049       break;
10050     }
10051
10052     case CA_SET_LEVEL_SCORE:
10053     {
10054       game.score = action_arg_number_new;
10055
10056       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10057
10058       DisplayGameControlValues();
10059
10060       break;
10061     }
10062
10063     case CA_SET_LEVEL_GEMS:
10064     {
10065       game.gems_still_needed = action_arg_number_new;
10066
10067       game.snapshot.collected_item = TRUE;
10068
10069       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10070
10071       DisplayGameControlValues();
10072
10073       break;
10074     }
10075
10076     case CA_SET_LEVEL_WIND:
10077     {
10078       game.wind_direction = action_arg_direction;
10079
10080       break;
10081     }
10082
10083     case CA_SET_LEVEL_RANDOM_SEED:
10084     {
10085       // ensure that setting a new random seed while playing is predictable
10086       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10087
10088       break;
10089     }
10090
10091     // ---------- player actions  ---------------------------------------------
10092
10093     case CA_MOVE_PLAYER:
10094     case CA_MOVE_PLAYER_NEW:
10095     {
10096       // automatically move to the next field in specified direction
10097       for (i = 0; i < MAX_PLAYERS; i++)
10098         if (trigger_player_bits & (1 << i))
10099           if (action_type == CA_MOVE_PLAYER ||
10100               stored_player[i].MovPos == 0)
10101             stored_player[i].programmed_action = action_arg_direction;
10102
10103       break;
10104     }
10105
10106     case CA_EXIT_PLAYER:
10107     {
10108       for (i = 0; i < MAX_PLAYERS; i++)
10109         if (action_arg_player_bits & (1 << i))
10110           ExitPlayer(&stored_player[i]);
10111
10112       if (game.players_still_needed == 0)
10113         LevelSolved();
10114
10115       break;
10116     }
10117
10118     case CA_KILL_PLAYER:
10119     {
10120       for (i = 0; i < MAX_PLAYERS; i++)
10121         if (action_arg_player_bits & (1 << i))
10122           KillPlayer(&stored_player[i]);
10123
10124       break;
10125     }
10126
10127     case CA_SET_PLAYER_KEYS:
10128     {
10129       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10130       int element = getSpecialActionElement(action_arg_element,
10131                                             action_arg_number, EL_KEY_1);
10132
10133       if (IS_KEY(element))
10134       {
10135         for (i = 0; i < MAX_PLAYERS; i++)
10136         {
10137           if (trigger_player_bits & (1 << i))
10138           {
10139             stored_player[i].key[KEY_NR(element)] = key_state;
10140
10141             DrawGameDoorValues();
10142           }
10143         }
10144       }
10145
10146       break;
10147     }
10148
10149     case CA_SET_PLAYER_SPEED:
10150     {
10151       for (i = 0; i < MAX_PLAYERS; i++)
10152       {
10153         if (trigger_player_bits & (1 << i))
10154         {
10155           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10156
10157           if (action_arg == CA_ARG_SPEED_FASTER &&
10158               stored_player[i].cannot_move)
10159           {
10160             action_arg_number = STEPSIZE_VERY_SLOW;
10161           }
10162           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10163                    action_arg == CA_ARG_SPEED_FASTER)
10164           {
10165             action_arg_number = 2;
10166             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10167                            CA_MODE_MULTIPLY);
10168           }
10169           else if (action_arg == CA_ARG_NUMBER_RESET)
10170           {
10171             action_arg_number = level.initial_player_stepsize[i];
10172           }
10173
10174           move_stepsize =
10175             getModifiedActionNumber(move_stepsize,
10176                                     action_mode,
10177                                     action_arg_number,
10178                                     action_arg_number_min,
10179                                     action_arg_number_max);
10180
10181           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10182         }
10183       }
10184
10185       break;
10186     }
10187
10188     case CA_SET_PLAYER_SHIELD:
10189     {
10190       for (i = 0; i < MAX_PLAYERS; i++)
10191       {
10192         if (trigger_player_bits & (1 << i))
10193         {
10194           if (action_arg == CA_ARG_SHIELD_OFF)
10195           {
10196             stored_player[i].shield_normal_time_left = 0;
10197             stored_player[i].shield_deadly_time_left = 0;
10198           }
10199           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10200           {
10201             stored_player[i].shield_normal_time_left = 999999;
10202           }
10203           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10204           {
10205             stored_player[i].shield_normal_time_left = 999999;
10206             stored_player[i].shield_deadly_time_left = 999999;
10207           }
10208         }
10209       }
10210
10211       break;
10212     }
10213
10214     case CA_SET_PLAYER_GRAVITY:
10215     {
10216       for (i = 0; i < MAX_PLAYERS; i++)
10217       {
10218         if (trigger_player_bits & (1 << i))
10219         {
10220           stored_player[i].gravity =
10221             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10222              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10223              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10224              stored_player[i].gravity);
10225         }
10226       }
10227
10228       break;
10229     }
10230
10231     case CA_SET_PLAYER_ARTWORK:
10232     {
10233       for (i = 0; i < MAX_PLAYERS; i++)
10234       {
10235         if (trigger_player_bits & (1 << i))
10236         {
10237           int artwork_element = action_arg_element;
10238
10239           if (action_arg == CA_ARG_ELEMENT_RESET)
10240             artwork_element =
10241               (level.use_artwork_element[i] ? level.artwork_element[i] :
10242                stored_player[i].element_nr);
10243
10244           if (stored_player[i].artwork_element != artwork_element)
10245             stored_player[i].Frame = 0;
10246
10247           stored_player[i].artwork_element = artwork_element;
10248
10249           SetPlayerWaiting(&stored_player[i], FALSE);
10250
10251           // set number of special actions for bored and sleeping animation
10252           stored_player[i].num_special_action_bored =
10253             get_num_special_action(artwork_element,
10254                                    ACTION_BORING_1, ACTION_BORING_LAST);
10255           stored_player[i].num_special_action_sleeping =
10256             get_num_special_action(artwork_element,
10257                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10258         }
10259       }
10260
10261       break;
10262     }
10263
10264     case CA_SET_PLAYER_INVENTORY:
10265     {
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267       {
10268         struct PlayerInfo *player = &stored_player[i];
10269         int j, k;
10270
10271         if (trigger_player_bits & (1 << i))
10272         {
10273           int inventory_element = action_arg_element;
10274
10275           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10276               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10277               action_arg == CA_ARG_ELEMENT_ACTION)
10278           {
10279             int element = inventory_element;
10280             int collect_count = element_info[element].collect_count_initial;
10281
10282             if (!IS_CUSTOM_ELEMENT(element))
10283               collect_count = 1;
10284
10285             if (collect_count == 0)
10286               player->inventory_infinite_element = element;
10287             else
10288               for (k = 0; k < collect_count; k++)
10289                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10290                   player->inventory_element[player->inventory_size++] =
10291                     element;
10292           }
10293           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10294                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10295                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10296           {
10297             if (player->inventory_infinite_element != EL_UNDEFINED &&
10298                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10299                                      action_arg_element_raw))
10300               player->inventory_infinite_element = EL_UNDEFINED;
10301
10302             for (k = 0, j = 0; j < player->inventory_size; j++)
10303             {
10304               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10305                                         action_arg_element_raw))
10306                 player->inventory_element[k++] = player->inventory_element[j];
10307             }
10308
10309             player->inventory_size = k;
10310           }
10311           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10312           {
10313             if (player->inventory_size > 0)
10314             {
10315               for (j = 0; j < player->inventory_size - 1; j++)
10316                 player->inventory_element[j] = player->inventory_element[j + 1];
10317
10318               player->inventory_size--;
10319             }
10320           }
10321           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10322           {
10323             if (player->inventory_size > 0)
10324               player->inventory_size--;
10325           }
10326           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10327           {
10328             player->inventory_infinite_element = EL_UNDEFINED;
10329             player->inventory_size = 0;
10330           }
10331           else if (action_arg == CA_ARG_INVENTORY_RESET)
10332           {
10333             player->inventory_infinite_element = EL_UNDEFINED;
10334             player->inventory_size = 0;
10335
10336             if (level.use_initial_inventory[i])
10337             {
10338               for (j = 0; j < level.initial_inventory_size[i]; j++)
10339               {
10340                 int element = level.initial_inventory_content[i][j];
10341                 int collect_count = element_info[element].collect_count_initial;
10342
10343                 if (!IS_CUSTOM_ELEMENT(element))
10344                   collect_count = 1;
10345
10346                 if (collect_count == 0)
10347                   player->inventory_infinite_element = element;
10348                 else
10349                   for (k = 0; k < collect_count; k++)
10350                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10351                       player->inventory_element[player->inventory_size++] =
10352                         element;
10353               }
10354             }
10355           }
10356         }
10357       }
10358
10359       break;
10360     }
10361
10362     // ---------- CE actions  -------------------------------------------------
10363
10364     case CA_SET_CE_VALUE:
10365     {
10366       int last_ce_value = CustomValue[x][y];
10367
10368       CustomValue[x][y] = action_arg_number_new;
10369
10370       if (CustomValue[x][y] != last_ce_value)
10371       {
10372         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10373         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10374
10375         if (CustomValue[x][y] == 0)
10376         {
10377           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10378           ChangeCount[x][y] = 0;        // allow at least one more change
10379
10380           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10381           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388     case CA_SET_CE_SCORE:
10389     {
10390       int last_ce_score = ei->collect_score;
10391
10392       ei->collect_score = action_arg_number_new;
10393
10394       if (ei->collect_score != last_ce_score)
10395       {
10396         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10397         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10398
10399         if (ei->collect_score == 0)
10400         {
10401           int xx, yy;
10402
10403           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10404           ChangeCount[x][y] = 0;        // allow at least one more change
10405
10406           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10407           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10408
10409           /*
10410             This is a very special case that seems to be a mixture between
10411             CheckElementChange() and CheckTriggeredElementChange(): while
10412             the first one only affects single elements that are triggered
10413             directly, the second one affects multiple elements in the playfield
10414             that are triggered indirectly by another element. This is a third
10415             case: Changing the CE score always affects multiple identical CEs,
10416             so every affected CE must be checked, not only the single CE for
10417             which the CE score was changed in the first place (as every instance
10418             of that CE shares the same CE score, and therefore also can change)!
10419           */
10420           SCAN_PLAYFIELD(xx, yy)
10421           {
10422             if (Tile[xx][yy] == element)
10423               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10424                                  CE_SCORE_GETS_ZERO);
10425           }
10426         }
10427       }
10428
10429       break;
10430     }
10431
10432     case CA_SET_CE_ARTWORK:
10433     {
10434       int artwork_element = action_arg_element;
10435       boolean reset_frame = FALSE;
10436       int xx, yy;
10437
10438       if (action_arg == CA_ARG_ELEMENT_RESET)
10439         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10440                            element);
10441
10442       if (ei->gfx_element != artwork_element)
10443         reset_frame = TRUE;
10444
10445       ei->gfx_element = artwork_element;
10446
10447       SCAN_PLAYFIELD(xx, yy)
10448       {
10449         if (Tile[xx][yy] == element)
10450         {
10451           if (reset_frame)
10452           {
10453             ResetGfxAnimation(xx, yy);
10454             ResetRandomAnimationValue(xx, yy);
10455           }
10456
10457           TEST_DrawLevelField(xx, yy);
10458         }
10459       }
10460
10461       break;
10462     }
10463
10464     // ---------- engine actions  ---------------------------------------------
10465
10466     case CA_SET_ENGINE_SCAN_MODE:
10467     {
10468       InitPlayfieldScanMode(action_arg);
10469
10470       break;
10471     }
10472
10473     default:
10474       break;
10475   }
10476 }
10477
10478 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10479 {
10480   int old_element = Tile[x][y];
10481   int new_element = GetElementFromGroupElement(element);
10482   int previous_move_direction = MovDir[x][y];
10483   int last_ce_value = CustomValue[x][y];
10484   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10485   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10486   boolean add_player_onto_element = (new_element_is_player &&
10487                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10488                                      IS_WALKABLE(old_element));
10489
10490   if (!add_player_onto_element)
10491   {
10492     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10493       RemoveMovingField(x, y);
10494     else
10495       RemoveField(x, y);
10496
10497     Tile[x][y] = new_element;
10498
10499     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10500       MovDir[x][y] = previous_move_direction;
10501
10502     if (element_info[new_element].use_last_ce_value)
10503       CustomValue[x][y] = last_ce_value;
10504
10505     InitField_WithBug1(x, y, FALSE);
10506
10507     new_element = Tile[x][y];   // element may have changed
10508
10509     ResetGfxAnimation(x, y);
10510     ResetRandomAnimationValue(x, y);
10511
10512     TEST_DrawLevelField(x, y);
10513
10514     if (GFX_CRUMBLED(new_element))
10515       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10516   }
10517
10518   // check if element under the player changes from accessible to unaccessible
10519   // (needed for special case of dropping element which then changes)
10520   // (must be checked after creating new element for walkable group elements)
10521   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10522       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10523   {
10524     Bang(x, y);
10525
10526     return;
10527   }
10528
10529   // "ChangeCount" not set yet to allow "entered by player" change one time
10530   if (new_element_is_player)
10531     RelocatePlayer(x, y, new_element);
10532
10533   if (is_change)
10534     ChangeCount[x][y]++;        // count number of changes in the same frame
10535
10536   TestIfBadThingTouchesPlayer(x, y);
10537   TestIfPlayerTouchesCustomElement(x, y);
10538   TestIfElementTouchesCustomElement(x, y);
10539 }
10540
10541 static void CreateField(int x, int y, int element)
10542 {
10543   CreateFieldExt(x, y, element, FALSE);
10544 }
10545
10546 static void CreateElementFromChange(int x, int y, int element)
10547 {
10548   element = GET_VALID_RUNTIME_ELEMENT(element);
10549
10550   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10551   {
10552     int old_element = Tile[x][y];
10553
10554     // prevent changed element from moving in same engine frame
10555     // unless both old and new element can either fall or move
10556     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10557         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10558       Stop[x][y] = TRUE;
10559   }
10560
10561   CreateFieldExt(x, y, element, TRUE);
10562 }
10563
10564 static boolean ChangeElement(int x, int y, int element, int page)
10565 {
10566   struct ElementInfo *ei = &element_info[element];
10567   struct ElementChangeInfo *change = &ei->change_page[page];
10568   int ce_value = CustomValue[x][y];
10569   int ce_score = ei->collect_score;
10570   int target_element;
10571   int old_element = Tile[x][y];
10572
10573   // always use default change event to prevent running into a loop
10574   if (ChangeEvent[x][y] == -1)
10575     ChangeEvent[x][y] = CE_DELAY;
10576
10577   if (ChangeEvent[x][y] == CE_DELAY)
10578   {
10579     // reset actual trigger element, trigger player and action element
10580     change->actual_trigger_element = EL_EMPTY;
10581     change->actual_trigger_player = EL_EMPTY;
10582     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10583     change->actual_trigger_side = CH_SIDE_NONE;
10584     change->actual_trigger_ce_value = 0;
10585     change->actual_trigger_ce_score = 0;
10586   }
10587
10588   // do not change elements more than a specified maximum number of changes
10589   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10590     return FALSE;
10591
10592   ChangeCount[x][y]++;          // count number of changes in the same frame
10593
10594   if (change->explode)
10595   {
10596     Bang(x, y);
10597
10598     return TRUE;
10599   }
10600
10601   if (change->use_target_content)
10602   {
10603     boolean complete_replace = TRUE;
10604     boolean can_replace[3][3];
10605     int xx, yy;
10606
10607     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10608     {
10609       boolean is_empty;
10610       boolean is_walkable;
10611       boolean is_diggable;
10612       boolean is_collectible;
10613       boolean is_removable;
10614       boolean is_destructible;
10615       int ex = x + xx - 1;
10616       int ey = y + yy - 1;
10617       int content_element = change->target_content.e[xx][yy];
10618       int e;
10619
10620       can_replace[xx][yy] = TRUE;
10621
10622       if (ex == x && ey == y)   // do not check changing element itself
10623         continue;
10624
10625       if (content_element == EL_EMPTY_SPACE)
10626       {
10627         can_replace[xx][yy] = FALSE;    // do not replace border with space
10628
10629         continue;
10630       }
10631
10632       if (!IN_LEV_FIELD(ex, ey))
10633       {
10634         can_replace[xx][yy] = FALSE;
10635         complete_replace = FALSE;
10636
10637         continue;
10638       }
10639
10640       e = Tile[ex][ey];
10641
10642       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10643         e = MovingOrBlocked2Element(ex, ey);
10644
10645       is_empty = (IS_FREE(ex, ey) ||
10646                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10647
10648       is_walkable     = (is_empty || IS_WALKABLE(e));
10649       is_diggable     = (is_empty || IS_DIGGABLE(e));
10650       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10651       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10652       is_removable    = (is_diggable || is_collectible);
10653
10654       can_replace[xx][yy] =
10655         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10656           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10657           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10658           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10659           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10660           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10661          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10662
10663       if (!can_replace[xx][yy])
10664         complete_replace = FALSE;
10665     }
10666
10667     if (!change->only_if_complete || complete_replace)
10668     {
10669       boolean something_has_changed = FALSE;
10670
10671       if (change->only_if_complete && change->use_random_replace &&
10672           RND(100) < change->random_percentage)
10673         return FALSE;
10674
10675       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10676       {
10677         int ex = x + xx - 1;
10678         int ey = y + yy - 1;
10679         int content_element;
10680
10681         if (can_replace[xx][yy] && (!change->use_random_replace ||
10682                                     RND(100) < change->random_percentage))
10683         {
10684           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10685             RemoveMovingField(ex, ey);
10686
10687           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10688
10689           content_element = change->target_content.e[xx][yy];
10690           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10691                                               ce_value, ce_score);
10692
10693           CreateElementFromChange(ex, ey, target_element);
10694
10695           something_has_changed = TRUE;
10696
10697           // for symmetry reasons, freeze newly created border elements
10698           if (ex != x || ey != y)
10699             Stop[ex][ey] = TRUE;        // no more moving in this frame
10700         }
10701       }
10702
10703       if (something_has_changed)
10704       {
10705         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10706         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10707       }
10708     }
10709   }
10710   else
10711   {
10712     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10713                                         ce_value, ce_score);
10714
10715     if (element == EL_DIAGONAL_GROWING ||
10716         element == EL_DIAGONAL_SHRINKING)
10717     {
10718       target_element = Store[x][y];
10719
10720       Store[x][y] = EL_EMPTY;
10721     }
10722
10723     CreateElementFromChange(x, y, target_element);
10724
10725     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10726     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10727   }
10728
10729   // this uses direct change before indirect change
10730   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10731
10732   return TRUE;
10733 }
10734
10735 static void HandleElementChange(int x, int y, int page)
10736 {
10737   int element = MovingOrBlocked2Element(x, y);
10738   struct ElementInfo *ei = &element_info[element];
10739   struct ElementChangeInfo *change = &ei->change_page[page];
10740   boolean handle_action_before_change = FALSE;
10741
10742 #ifdef DEBUG
10743   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10744       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10745   {
10746     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10747           x, y, element, element_info[element].token_name);
10748     Debug("game:playing:HandleElementChange", "This should never happen!");
10749   }
10750 #endif
10751
10752   // this can happen with classic bombs on walkable, changing elements
10753   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10754   {
10755     return;
10756   }
10757
10758   if (ChangeDelay[x][y] == 0)           // initialize element change
10759   {
10760     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10761
10762     if (change->can_change)
10763     {
10764       // !!! not clear why graphic animation should be reset at all here !!!
10765       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10766       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10767
10768       /*
10769         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10770
10771         When using an animation frame delay of 1 (this only happens with
10772         "sp_zonk.moving.left/right" in the classic graphics), the default
10773         (non-moving) animation shows wrong animation frames (while the
10774         moving animation, like "sp_zonk.moving.left/right", is correct,
10775         so this graphical bug never shows up with the classic graphics).
10776         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10777         be drawn instead of the correct frames 0,1,2,3. This is caused by
10778         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10779         an element change: First when the change delay ("ChangeDelay[][]")
10780         counter has reached zero after decrementing, then a second time in
10781         the next frame (after "GfxFrame[][]" was already incremented) when
10782         "ChangeDelay[][]" is reset to the initial delay value again.
10783
10784         This causes frame 0 to be drawn twice, while the last frame won't
10785         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10786
10787         As some animations may already be cleverly designed around this bug
10788         (at least the "Snake Bite" snake tail animation does this), it cannot
10789         simply be fixed here without breaking such existing animations.
10790         Unfortunately, it cannot easily be detected if a graphics set was
10791         designed "before" or "after" the bug was fixed. As a workaround,
10792         a new graphics set option "game.graphics_engine_version" was added
10793         to be able to specify the game's major release version for which the
10794         graphics set was designed, which can then be used to decide if the
10795         bugfix should be used (version 4 and above) or not (version 3 or
10796         below, or if no version was specified at all, as with old sets).
10797
10798         (The wrong/fixed animation frames can be tested with the test level set
10799         "test_gfxframe" and level "000", which contains a specially prepared
10800         custom element at level position (x/y) == (11/9) which uses the zonk
10801         animation mentioned above. Using "game.graphics_engine_version: 4"
10802         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10803         This can also be seen from the debug output for this test element.)
10804       */
10805
10806       // when a custom element is about to change (for example by change delay),
10807       // do not reset graphic animation when the custom element is moving
10808       if (game.graphics_engine_version < 4 &&
10809           !IS_MOVING(x, y))
10810       {
10811         ResetGfxAnimation(x, y);
10812         ResetRandomAnimationValue(x, y);
10813       }
10814
10815       if (change->pre_change_function)
10816         change->pre_change_function(x, y);
10817     }
10818   }
10819
10820   ChangeDelay[x][y]--;
10821
10822   if (ChangeDelay[x][y] != 0)           // continue element change
10823   {
10824     if (change->can_change)
10825     {
10826       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10827
10828       if (IS_ANIMATED(graphic))
10829         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10830
10831       if (change->change_function)
10832         change->change_function(x, y);
10833     }
10834   }
10835   else                                  // finish element change
10836   {
10837     if (ChangePage[x][y] != -1)         // remember page from delayed change
10838     {
10839       page = ChangePage[x][y];
10840       ChangePage[x][y] = -1;
10841
10842       change = &ei->change_page[page];
10843     }
10844
10845     if (IS_MOVING(x, y))                // never change a running system ;-)
10846     {
10847       ChangeDelay[x][y] = 1;            // try change after next move step
10848       ChangePage[x][y] = page;          // remember page to use for change
10849
10850       return;
10851     }
10852
10853     // special case: set new level random seed before changing element
10854     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10855       handle_action_before_change = TRUE;
10856
10857     if (change->has_action && handle_action_before_change)
10858       ExecuteCustomElementAction(x, y, element, page);
10859
10860     if (change->can_change)
10861     {
10862       if (ChangeElement(x, y, element, page))
10863       {
10864         if (change->post_change_function)
10865           change->post_change_function(x, y);
10866       }
10867     }
10868
10869     if (change->has_action && !handle_action_before_change)
10870       ExecuteCustomElementAction(x, y, element, page);
10871   }
10872 }
10873
10874 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10875                                               int trigger_element,
10876                                               int trigger_event,
10877                                               int trigger_player,
10878                                               int trigger_side,
10879                                               int trigger_page)
10880 {
10881   boolean change_done_any = FALSE;
10882   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10883   int i;
10884
10885   if (!(trigger_events[trigger_element][trigger_event]))
10886     return FALSE;
10887
10888   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10889
10890   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10891   {
10892     int element = EL_CUSTOM_START + i;
10893     boolean change_done = FALSE;
10894     int p;
10895
10896     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10897         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10898       continue;
10899
10900     for (p = 0; p < element_info[element].num_change_pages; p++)
10901     {
10902       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10903
10904       if (change->can_change_or_has_action &&
10905           change->has_event[trigger_event] &&
10906           change->trigger_side & trigger_side &&
10907           change->trigger_player & trigger_player &&
10908           change->trigger_page & trigger_page_bits &&
10909           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10910       {
10911         change->actual_trigger_element = trigger_element;
10912         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10913         change->actual_trigger_player_bits = trigger_player;
10914         change->actual_trigger_side = trigger_side;
10915         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10916         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10917
10918         if ((change->can_change && !change_done) || change->has_action)
10919         {
10920           int x, y;
10921
10922           SCAN_PLAYFIELD(x, y)
10923           {
10924             if (Tile[x][y] == element)
10925             {
10926               if (change->can_change && !change_done)
10927               {
10928                 // if element already changed in this frame, not only prevent
10929                 // another element change (checked in ChangeElement()), but
10930                 // also prevent additional element actions for this element
10931
10932                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10933                     !level.use_action_after_change_bug)
10934                   continue;
10935
10936                 ChangeDelay[x][y] = 1;
10937                 ChangeEvent[x][y] = trigger_event;
10938
10939                 HandleElementChange(x, y, p);
10940               }
10941               else if (change->has_action)
10942               {
10943                 // if element already changed in this frame, not only prevent
10944                 // another element change (checked in ChangeElement()), but
10945                 // also prevent additional element actions for this element
10946
10947                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10948                     !level.use_action_after_change_bug)
10949                   continue;
10950
10951                 ExecuteCustomElementAction(x, y, element, p);
10952                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10953               }
10954             }
10955           }
10956
10957           if (change->can_change)
10958           {
10959             change_done = TRUE;
10960             change_done_any = TRUE;
10961           }
10962         }
10963       }
10964     }
10965   }
10966
10967   RECURSION_LOOP_DETECTION_END();
10968
10969   return change_done_any;
10970 }
10971
10972 static boolean CheckElementChangeExt(int x, int y,
10973                                      int element,
10974                                      int trigger_element,
10975                                      int trigger_event,
10976                                      int trigger_player,
10977                                      int trigger_side)
10978 {
10979   boolean change_done = FALSE;
10980   int p;
10981
10982   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10983       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10984     return FALSE;
10985
10986   if (Tile[x][y] == EL_BLOCKED)
10987   {
10988     Blocked2Moving(x, y, &x, &y);
10989     element = Tile[x][y];
10990   }
10991
10992   // check if element has already changed or is about to change after moving
10993   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10994        Tile[x][y] != element) ||
10995
10996       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10997        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10998         ChangePage[x][y] != -1)))
10999     return FALSE;
11000
11001   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11002
11003   for (p = 0; p < element_info[element].num_change_pages; p++)
11004   {
11005     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11006
11007     /* check trigger element for all events where the element that is checked
11008        for changing interacts with a directly adjacent element -- this is
11009        different to element changes that affect other elements to change on the
11010        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11011     boolean check_trigger_element =
11012       (trigger_event == CE_TOUCHING_X ||
11013        trigger_event == CE_HITTING_X ||
11014        trigger_event == CE_HIT_BY_X ||
11015        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11016
11017     if (change->can_change_or_has_action &&
11018         change->has_event[trigger_event] &&
11019         change->trigger_side & trigger_side &&
11020         change->trigger_player & trigger_player &&
11021         (!check_trigger_element ||
11022          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11023     {
11024       change->actual_trigger_element = trigger_element;
11025       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11026       change->actual_trigger_player_bits = trigger_player;
11027       change->actual_trigger_side = trigger_side;
11028       change->actual_trigger_ce_value = CustomValue[x][y];
11029       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11030
11031       // special case: trigger element not at (x,y) position for some events
11032       if (check_trigger_element)
11033       {
11034         static struct
11035         {
11036           int dx, dy;
11037         } move_xy[] =
11038           {
11039             {  0,  0 },
11040             { -1,  0 },
11041             { +1,  0 },
11042             {  0,  0 },
11043             {  0, -1 },
11044             {  0,  0 }, { 0, 0 }, { 0, 0 },
11045             {  0, +1 }
11046           };
11047
11048         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11049         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11050
11051         change->actual_trigger_ce_value = CustomValue[xx][yy];
11052         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11053       }
11054
11055       if (change->can_change && !change_done)
11056       {
11057         ChangeDelay[x][y] = 1;
11058         ChangeEvent[x][y] = trigger_event;
11059
11060         HandleElementChange(x, y, p);
11061
11062         change_done = TRUE;
11063       }
11064       else if (change->has_action)
11065       {
11066         ExecuteCustomElementAction(x, y, element, p);
11067         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11068       }
11069     }
11070   }
11071
11072   RECURSION_LOOP_DETECTION_END();
11073
11074   return change_done;
11075 }
11076
11077 static void PlayPlayerSound(struct PlayerInfo *player)
11078 {
11079   int jx = player->jx, jy = player->jy;
11080   int sound_element = player->artwork_element;
11081   int last_action = player->last_action_waiting;
11082   int action = player->action_waiting;
11083
11084   if (player->is_waiting)
11085   {
11086     if (action != last_action)
11087       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11088     else
11089       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11090   }
11091   else
11092   {
11093     if (action != last_action)
11094       StopSound(element_info[sound_element].sound[last_action]);
11095
11096     if (last_action == ACTION_SLEEPING)
11097       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11098   }
11099 }
11100
11101 static void PlayAllPlayersSound(void)
11102 {
11103   int i;
11104
11105   for (i = 0; i < MAX_PLAYERS; i++)
11106     if (stored_player[i].active)
11107       PlayPlayerSound(&stored_player[i]);
11108 }
11109
11110 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11111 {
11112   boolean last_waiting = player->is_waiting;
11113   int move_dir = player->MovDir;
11114
11115   player->dir_waiting = move_dir;
11116   player->last_action_waiting = player->action_waiting;
11117
11118   if (is_waiting)
11119   {
11120     if (!last_waiting)          // not waiting -> waiting
11121     {
11122       player->is_waiting = TRUE;
11123
11124       player->frame_counter_bored =
11125         FrameCounter +
11126         game.player_boring_delay_fixed +
11127         GetSimpleRandom(game.player_boring_delay_random);
11128       player->frame_counter_sleeping =
11129         FrameCounter +
11130         game.player_sleeping_delay_fixed +
11131         GetSimpleRandom(game.player_sleeping_delay_random);
11132
11133       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11134     }
11135
11136     if (game.player_sleeping_delay_fixed +
11137         game.player_sleeping_delay_random > 0 &&
11138         player->anim_delay_counter == 0 &&
11139         player->post_delay_counter == 0 &&
11140         FrameCounter >= player->frame_counter_sleeping)
11141       player->is_sleeping = TRUE;
11142     else if (game.player_boring_delay_fixed +
11143              game.player_boring_delay_random > 0 &&
11144              FrameCounter >= player->frame_counter_bored)
11145       player->is_bored = TRUE;
11146
11147     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11148                               player->is_bored ? ACTION_BORING :
11149                               ACTION_WAITING);
11150
11151     if (player->is_sleeping && player->use_murphy)
11152     {
11153       // special case for sleeping Murphy when leaning against non-free tile
11154
11155       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11156           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11157            !IS_MOVING(player->jx - 1, player->jy)))
11158         move_dir = MV_LEFT;
11159       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11160                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11161                 !IS_MOVING(player->jx + 1, player->jy)))
11162         move_dir = MV_RIGHT;
11163       else
11164         player->is_sleeping = FALSE;
11165
11166       player->dir_waiting = move_dir;
11167     }
11168
11169     if (player->is_sleeping)
11170     {
11171       if (player->num_special_action_sleeping > 0)
11172       {
11173         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11174         {
11175           int last_special_action = player->special_action_sleeping;
11176           int num_special_action = player->num_special_action_sleeping;
11177           int special_action =
11178             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11179              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11180              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11181              last_special_action + 1 : ACTION_SLEEPING);
11182           int special_graphic =
11183             el_act_dir2img(player->artwork_element, special_action, move_dir);
11184
11185           player->anim_delay_counter =
11186             graphic_info[special_graphic].anim_delay_fixed +
11187             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11188           player->post_delay_counter =
11189             graphic_info[special_graphic].post_delay_fixed +
11190             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11191
11192           player->special_action_sleeping = special_action;
11193         }
11194
11195         if (player->anim_delay_counter > 0)
11196         {
11197           player->action_waiting = player->special_action_sleeping;
11198           player->anim_delay_counter--;
11199         }
11200         else if (player->post_delay_counter > 0)
11201         {
11202           player->post_delay_counter--;
11203         }
11204       }
11205     }
11206     else if (player->is_bored)
11207     {
11208       if (player->num_special_action_bored > 0)
11209       {
11210         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11211         {
11212           int special_action =
11213             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11214           int special_graphic =
11215             el_act_dir2img(player->artwork_element, special_action, move_dir);
11216
11217           player->anim_delay_counter =
11218             graphic_info[special_graphic].anim_delay_fixed +
11219             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11220           player->post_delay_counter =
11221             graphic_info[special_graphic].post_delay_fixed +
11222             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11223
11224           player->special_action_bored = special_action;
11225         }
11226
11227         if (player->anim_delay_counter > 0)
11228         {
11229           player->action_waiting = player->special_action_bored;
11230           player->anim_delay_counter--;
11231         }
11232         else if (player->post_delay_counter > 0)
11233         {
11234           player->post_delay_counter--;
11235         }
11236       }
11237     }
11238   }
11239   else if (last_waiting)        // waiting -> not waiting
11240   {
11241     player->is_waiting = FALSE;
11242     player->is_bored = FALSE;
11243     player->is_sleeping = FALSE;
11244
11245     player->frame_counter_bored = -1;
11246     player->frame_counter_sleeping = -1;
11247
11248     player->anim_delay_counter = 0;
11249     player->post_delay_counter = 0;
11250
11251     player->dir_waiting = player->MovDir;
11252     player->action_waiting = ACTION_DEFAULT;
11253
11254     player->special_action_bored = ACTION_DEFAULT;
11255     player->special_action_sleeping = ACTION_DEFAULT;
11256   }
11257 }
11258
11259 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11260 {
11261   if ((!player->is_moving  && player->was_moving) ||
11262       (player->MovPos == 0 && player->was_moving) ||
11263       (player->is_snapping && !player->was_snapping) ||
11264       (player->is_dropping && !player->was_dropping))
11265   {
11266     if (!CheckSaveEngineSnapshotToList())
11267       return;
11268
11269     player->was_moving = FALSE;
11270     player->was_snapping = TRUE;
11271     player->was_dropping = TRUE;
11272   }
11273   else
11274   {
11275     if (player->is_moving)
11276       player->was_moving = TRUE;
11277
11278     if (!player->is_snapping)
11279       player->was_snapping = FALSE;
11280
11281     if (!player->is_dropping)
11282       player->was_dropping = FALSE;
11283   }
11284
11285   static struct MouseActionInfo mouse_action_last = { 0 };
11286   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11287   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11288
11289   if (new_released)
11290     CheckSaveEngineSnapshotToList();
11291
11292   mouse_action_last = mouse_action;
11293 }
11294
11295 static void CheckSingleStepMode(struct PlayerInfo *player)
11296 {
11297   if (tape.single_step && tape.recording && !tape.pausing)
11298   {
11299     // as it is called "single step mode", just return to pause mode when the
11300     // player stopped moving after one tile (or never starts moving at all)
11301     // (reverse logic needed here in case single step mode used in team mode)
11302     if (player->is_moving ||
11303         player->is_pushing ||
11304         player->is_dropping_pressed ||
11305         player->effective_mouse_action.button)
11306       game.enter_single_step_mode = FALSE;
11307   }
11308
11309   CheckSaveEngineSnapshot(player);
11310 }
11311
11312 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11313 {
11314   int left      = player_action & JOY_LEFT;
11315   int right     = player_action & JOY_RIGHT;
11316   int up        = player_action & JOY_UP;
11317   int down      = player_action & JOY_DOWN;
11318   int button1   = player_action & JOY_BUTTON_1;
11319   int button2   = player_action & JOY_BUTTON_2;
11320   int dx        = (left ? -1 : right ? 1 : 0);
11321   int dy        = (up   ? -1 : down  ? 1 : 0);
11322
11323   if (!player->active || tape.pausing)
11324     return 0;
11325
11326   if (player_action)
11327   {
11328     if (button1)
11329       SnapField(player, dx, dy);
11330     else
11331     {
11332       if (button2)
11333         DropElement(player);
11334
11335       MovePlayer(player, dx, dy);
11336     }
11337
11338     CheckSingleStepMode(player);
11339
11340     SetPlayerWaiting(player, FALSE);
11341
11342     return player_action;
11343   }
11344   else
11345   {
11346     // no actions for this player (no input at player's configured device)
11347
11348     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11349     SnapField(player, 0, 0);
11350     CheckGravityMovementWhenNotMoving(player);
11351
11352     if (player->MovPos == 0)
11353       SetPlayerWaiting(player, TRUE);
11354
11355     if (player->MovPos == 0)    // needed for tape.playing
11356       player->is_moving = FALSE;
11357
11358     player->is_dropping = FALSE;
11359     player->is_dropping_pressed = FALSE;
11360     player->drop_pressed_delay = 0;
11361
11362     CheckSingleStepMode(player);
11363
11364     return 0;
11365   }
11366 }
11367
11368 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11369                                          byte *tape_action)
11370 {
11371   if (!tape.use_mouse_actions)
11372     return;
11373
11374   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11375   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11376   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11377 }
11378
11379 static void SetTapeActionFromMouseAction(byte *tape_action,
11380                                          struct MouseActionInfo *mouse_action)
11381 {
11382   if (!tape.use_mouse_actions)
11383     return;
11384
11385   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11386   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11387   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11388 }
11389
11390 static void CheckLevelSolved(void)
11391 {
11392   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11393   {
11394     if (game_em.level_solved &&
11395         !game_em.game_over)                             // game won
11396     {
11397       LevelSolved();
11398
11399       game_em.game_over = TRUE;
11400
11401       game.all_players_gone = TRUE;
11402     }
11403
11404     if (game_em.game_over)                              // game lost
11405       game.all_players_gone = TRUE;
11406   }
11407   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11408   {
11409     if (game_sp.level_solved &&
11410         !game_sp.game_over)                             // game won
11411     {
11412       LevelSolved();
11413
11414       game_sp.game_over = TRUE;
11415
11416       game.all_players_gone = TRUE;
11417     }
11418
11419     if (game_sp.game_over)                              // game lost
11420       game.all_players_gone = TRUE;
11421   }
11422   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11423   {
11424     if (game_mm.level_solved &&
11425         !game_mm.game_over)                             // game won
11426     {
11427       LevelSolved();
11428
11429       game_mm.game_over = TRUE;
11430
11431       game.all_players_gone = TRUE;
11432     }
11433
11434     if (game_mm.game_over)                              // game lost
11435       game.all_players_gone = TRUE;
11436   }
11437 }
11438
11439 static void CheckLevelTime(void)
11440 {
11441   int i;
11442
11443   if (TimeFrames >= FRAMES_PER_SECOND)
11444   {
11445     TimeFrames = 0;
11446     TapeTime++;
11447
11448     for (i = 0; i < MAX_PLAYERS; i++)
11449     {
11450       struct PlayerInfo *player = &stored_player[i];
11451
11452       if (SHIELD_ON(player))
11453       {
11454         player->shield_normal_time_left--;
11455
11456         if (player->shield_deadly_time_left > 0)
11457           player->shield_deadly_time_left--;
11458       }
11459     }
11460
11461     if (!game.LevelSolved && !level.use_step_counter)
11462     {
11463       TimePlayed++;
11464
11465       if (TimeLeft > 0)
11466       {
11467         TimeLeft--;
11468
11469         if (TimeLeft <= 10 && setup.time_limit)
11470           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11471
11472         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11473            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11474
11475         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11476
11477         if (!TimeLeft && setup.time_limit)
11478         {
11479           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11480             game_em.lev->killed_out_of_time = TRUE;
11481           else
11482             for (i = 0; i < MAX_PLAYERS; i++)
11483               KillPlayer(&stored_player[i]);
11484         }
11485       }
11486       else if (game.no_time_limit && !game.all_players_gone)
11487       {
11488         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11489       }
11490
11491       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11492     }
11493
11494     if (tape.recording || tape.playing)
11495       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11496   }
11497
11498   if (tape.recording || tape.playing)
11499     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11500
11501   UpdateAndDisplayGameControlValues();
11502 }
11503
11504 void AdvanceFrameAndPlayerCounters(int player_nr)
11505 {
11506   int i;
11507
11508   // advance frame counters (global frame counter and time frame counter)
11509   FrameCounter++;
11510   TimeFrames++;
11511
11512   // advance player counters (counters for move delay, move animation etc.)
11513   for (i = 0; i < MAX_PLAYERS; i++)
11514   {
11515     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11516     int move_delay_value = stored_player[i].move_delay_value;
11517     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11518
11519     if (!advance_player_counters)       // not all players may be affected
11520       continue;
11521
11522     if (move_frames == 0)       // less than one move per game frame
11523     {
11524       int stepsize = TILEX / move_delay_value;
11525       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11526       int count = (stored_player[i].is_moving ?
11527                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11528
11529       if (count % delay == 0)
11530         move_frames = 1;
11531     }
11532
11533     stored_player[i].Frame += move_frames;
11534
11535     if (stored_player[i].MovPos != 0)
11536       stored_player[i].StepFrame += move_frames;
11537
11538     if (stored_player[i].move_delay > 0)
11539       stored_player[i].move_delay--;
11540
11541     // due to bugs in previous versions, counter must count up, not down
11542     if (stored_player[i].push_delay != -1)
11543       stored_player[i].push_delay++;
11544
11545     if (stored_player[i].drop_delay > 0)
11546       stored_player[i].drop_delay--;
11547
11548     if (stored_player[i].is_dropping_pressed)
11549       stored_player[i].drop_pressed_delay++;
11550   }
11551 }
11552
11553 void StartGameActions(boolean init_network_game, boolean record_tape,
11554                       int random_seed)
11555 {
11556   unsigned int new_random_seed = InitRND(random_seed);
11557
11558   if (record_tape)
11559     TapeStartRecording(new_random_seed);
11560
11561   if (init_network_game)
11562   {
11563     SendToServer_LevelFile();
11564     SendToServer_StartPlaying();
11565
11566     return;
11567   }
11568
11569   InitGame();
11570 }
11571
11572 static void GameActionsExt(void)
11573 {
11574 #if 0
11575   static unsigned int game_frame_delay = 0;
11576 #endif
11577   unsigned int game_frame_delay_value;
11578   byte *recorded_player_action;
11579   byte summarized_player_action = 0;
11580   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11581   int i;
11582
11583   // detect endless loops, caused by custom element programming
11584   if (recursion_loop_detected && recursion_loop_depth == 0)
11585   {
11586     char *message = getStringCat3("Internal Error! Element ",
11587                                   EL_NAME(recursion_loop_element),
11588                                   " caused endless loop! Quit the game?");
11589
11590     Warn("element '%s' caused endless loop in game engine",
11591          EL_NAME(recursion_loop_element));
11592
11593     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11594
11595     recursion_loop_detected = FALSE;    // if game should be continued
11596
11597     free(message);
11598
11599     return;
11600   }
11601
11602   if (game.restart_level)
11603     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11604
11605   CheckLevelSolved();
11606
11607   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11608     GameWon();
11609
11610   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11611     TapeStop();
11612
11613   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11614     return;
11615
11616   game_frame_delay_value =
11617     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11618
11619   if (tape.playing && tape.warp_forward && !tape.pausing)
11620     game_frame_delay_value = 0;
11621
11622   SetVideoFrameDelay(game_frame_delay_value);
11623
11624   // (de)activate virtual buttons depending on current game status
11625   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11626   {
11627     if (game.all_players_gone)  // if no players there to be controlled anymore
11628       SetOverlayActive(FALSE);
11629     else if (!tape.playing)     // if game continues after tape stopped playing
11630       SetOverlayActive(TRUE);
11631   }
11632
11633 #if 0
11634 #if 0
11635   // ---------- main game synchronization point ----------
11636
11637   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11638
11639   Debug("game:playing:skip", "skip == %d", skip);
11640
11641 #else
11642   // ---------- main game synchronization point ----------
11643
11644   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11645 #endif
11646 #endif
11647
11648   if (network_playing && !network_player_action_received)
11649   {
11650     // try to get network player actions in time
11651
11652     // last chance to get network player actions without main loop delay
11653     HandleNetworking();
11654
11655     // game was quit by network peer
11656     if (game_status != GAME_MODE_PLAYING)
11657       return;
11658
11659     // check if network player actions still missing and game still running
11660     if (!network_player_action_received && !checkGameEnded())
11661       return;           // failed to get network player actions in time
11662
11663     // do not yet reset "network_player_action_received" (for tape.pausing)
11664   }
11665
11666   if (tape.pausing)
11667     return;
11668
11669   // at this point we know that we really continue executing the game
11670
11671   network_player_action_received = FALSE;
11672
11673   // when playing tape, read previously recorded player input from tape data
11674   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11675
11676   local_player->effective_mouse_action = local_player->mouse_action;
11677
11678   if (recorded_player_action != NULL)
11679     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11680                                  recorded_player_action);
11681
11682   // TapePlayAction() may return NULL when toggling to "pause before death"
11683   if (tape.pausing)
11684     return;
11685
11686   if (tape.set_centered_player)
11687   {
11688     game.centered_player_nr_next = tape.centered_player_nr_next;
11689     game.set_centered_player = TRUE;
11690   }
11691
11692   for (i = 0; i < MAX_PLAYERS; i++)
11693   {
11694     summarized_player_action |= stored_player[i].action;
11695
11696     if (!network_playing && (game.team_mode || tape.playing))
11697       stored_player[i].effective_action = stored_player[i].action;
11698   }
11699
11700   if (network_playing && !checkGameEnded())
11701     SendToServer_MovePlayer(summarized_player_action);
11702
11703   // summarize all actions at local players mapped input device position
11704   // (this allows using different input devices in single player mode)
11705   if (!network.enabled && !game.team_mode)
11706     stored_player[map_player_action[local_player->index_nr]].effective_action =
11707       summarized_player_action;
11708
11709   // summarize all actions at centered player in local team mode
11710   if (tape.recording &&
11711       setup.team_mode && !network.enabled &&
11712       setup.input_on_focus &&
11713       game.centered_player_nr != -1)
11714   {
11715     for (i = 0; i < MAX_PLAYERS; i++)
11716       stored_player[map_player_action[i]].effective_action =
11717         (i == game.centered_player_nr ? summarized_player_action : 0);
11718   }
11719
11720   if (recorded_player_action != NULL)
11721     for (i = 0; i < MAX_PLAYERS; i++)
11722       stored_player[i].effective_action = recorded_player_action[i];
11723
11724   for (i = 0; i < MAX_PLAYERS; i++)
11725   {
11726     tape_action[i] = stored_player[i].effective_action;
11727
11728     /* (this may happen in the RND game engine if a player was not present on
11729        the playfield on level start, but appeared later from a custom element */
11730     if (setup.team_mode &&
11731         tape.recording &&
11732         tape_action[i] &&
11733         !tape.player_participates[i])
11734       tape.player_participates[i] = TRUE;
11735   }
11736
11737   SetTapeActionFromMouseAction(tape_action,
11738                                &local_player->effective_mouse_action);
11739
11740   // only record actions from input devices, but not programmed actions
11741   if (tape.recording)
11742     TapeRecordAction(tape_action);
11743
11744   // remember if game was played (especially after tape stopped playing)
11745   if (!tape.playing && summarized_player_action)
11746     game.GamePlayed = TRUE;
11747
11748 #if USE_NEW_PLAYER_ASSIGNMENTS
11749   // !!! also map player actions in single player mode !!!
11750   // if (game.team_mode)
11751   if (1)
11752   {
11753     byte mapped_action[MAX_PLAYERS];
11754
11755 #if DEBUG_PLAYER_ACTIONS
11756     for (i = 0; i < MAX_PLAYERS; i++)
11757       DebugContinued("", "%d, ", stored_player[i].effective_action);
11758 #endif
11759
11760     for (i = 0; i < MAX_PLAYERS; i++)
11761       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11762
11763     for (i = 0; i < MAX_PLAYERS; i++)
11764       stored_player[i].effective_action = mapped_action[i];
11765
11766 #if DEBUG_PLAYER_ACTIONS
11767     DebugContinued("", "=> ");
11768     for (i = 0; i < MAX_PLAYERS; i++)
11769       DebugContinued("", "%d, ", stored_player[i].effective_action);
11770     DebugContinued("game:playing:player", "\n");
11771 #endif
11772   }
11773 #if DEBUG_PLAYER_ACTIONS
11774   else
11775   {
11776     for (i = 0; i < MAX_PLAYERS; i++)
11777       DebugContinued("", "%d, ", stored_player[i].effective_action);
11778     DebugContinued("game:playing:player", "\n");
11779   }
11780 #endif
11781 #endif
11782
11783   for (i = 0; i < MAX_PLAYERS; i++)
11784   {
11785     // allow engine snapshot in case of changed movement attempt
11786     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11787         (stored_player[i].effective_action & KEY_MOTION))
11788       game.snapshot.changed_action = TRUE;
11789
11790     // allow engine snapshot in case of snapping/dropping attempt
11791     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11792         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11793       game.snapshot.changed_action = TRUE;
11794
11795     game.snapshot.last_action[i] = stored_player[i].effective_action;
11796   }
11797
11798   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11799   {
11800     GameActions_EM_Main();
11801   }
11802   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11803   {
11804     GameActions_SP_Main();
11805   }
11806   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11807   {
11808     GameActions_MM_Main();
11809   }
11810   else
11811   {
11812     GameActions_RND_Main();
11813   }
11814
11815   BlitScreenToBitmap(backbuffer);
11816
11817   CheckLevelSolved();
11818   CheckLevelTime();
11819
11820   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11821
11822   if (global.show_frames_per_second)
11823   {
11824     static unsigned int fps_counter = 0;
11825     static int fps_frames = 0;
11826     unsigned int fps_delay_ms = Counter() - fps_counter;
11827
11828     fps_frames++;
11829
11830     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11831     {
11832       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11833
11834       fps_frames = 0;
11835       fps_counter = Counter();
11836
11837       // always draw FPS to screen after FPS value was updated
11838       redraw_mask |= REDRAW_FPS;
11839     }
11840
11841     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11842     if (GetDrawDeactivationMask() == REDRAW_NONE)
11843       redraw_mask |= REDRAW_FPS;
11844   }
11845 }
11846
11847 static void GameActions_CheckSaveEngineSnapshot(void)
11848 {
11849   if (!game.snapshot.save_snapshot)
11850     return;
11851
11852   // clear flag for saving snapshot _before_ saving snapshot
11853   game.snapshot.save_snapshot = FALSE;
11854
11855   SaveEngineSnapshotToList();
11856 }
11857
11858 void GameActions(void)
11859 {
11860   GameActionsExt();
11861
11862   GameActions_CheckSaveEngineSnapshot();
11863 }
11864
11865 void GameActions_EM_Main(void)
11866 {
11867   byte effective_action[MAX_PLAYERS];
11868   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11869   int i;
11870
11871   for (i = 0; i < MAX_PLAYERS; i++)
11872     effective_action[i] = stored_player[i].effective_action;
11873
11874   GameActions_EM(effective_action, warp_mode);
11875 }
11876
11877 void GameActions_SP_Main(void)
11878 {
11879   byte effective_action[MAX_PLAYERS];
11880   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11881   int i;
11882
11883   for (i = 0; i < MAX_PLAYERS; i++)
11884     effective_action[i] = stored_player[i].effective_action;
11885
11886   GameActions_SP(effective_action, warp_mode);
11887
11888   for (i = 0; i < MAX_PLAYERS; i++)
11889   {
11890     if (stored_player[i].force_dropping)
11891       stored_player[i].action |= KEY_BUTTON_DROP;
11892
11893     stored_player[i].force_dropping = FALSE;
11894   }
11895 }
11896
11897 void GameActions_MM_Main(void)
11898 {
11899   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11900
11901   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11902 }
11903
11904 void GameActions_RND_Main(void)
11905 {
11906   GameActions_RND();
11907 }
11908
11909 void GameActions_RND(void)
11910 {
11911   static struct MouseActionInfo mouse_action_last = { 0 };
11912   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11913   int magic_wall_x = 0, magic_wall_y = 0;
11914   int i, x, y, element, graphic, last_gfx_frame;
11915
11916   InitPlayfieldScanModeVars();
11917
11918   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11919   {
11920     SCAN_PLAYFIELD(x, y)
11921     {
11922       ChangeCount[x][y] = 0;
11923       ChangeEvent[x][y] = -1;
11924     }
11925   }
11926
11927   if (game.set_centered_player)
11928   {
11929     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11930
11931     // switching to "all players" only possible if all players fit to screen
11932     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11933     {
11934       game.centered_player_nr_next = game.centered_player_nr;
11935       game.set_centered_player = FALSE;
11936     }
11937
11938     // do not switch focus to non-existing (or non-active) player
11939     if (game.centered_player_nr_next >= 0 &&
11940         !stored_player[game.centered_player_nr_next].active)
11941     {
11942       game.centered_player_nr_next = game.centered_player_nr;
11943       game.set_centered_player = FALSE;
11944     }
11945   }
11946
11947   if (game.set_centered_player &&
11948       ScreenMovPos == 0)        // screen currently aligned at tile position
11949   {
11950     int sx, sy;
11951
11952     if (game.centered_player_nr_next == -1)
11953     {
11954       setScreenCenteredToAllPlayers(&sx, &sy);
11955     }
11956     else
11957     {
11958       sx = stored_player[game.centered_player_nr_next].jx;
11959       sy = stored_player[game.centered_player_nr_next].jy;
11960     }
11961
11962     game.centered_player_nr = game.centered_player_nr_next;
11963     game.set_centered_player = FALSE;
11964
11965     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11966     DrawGameDoorValues();
11967   }
11968
11969   // check single step mode (set flag and clear again if any player is active)
11970   game.enter_single_step_mode =
11971     (tape.single_step && tape.recording && !tape.pausing);
11972
11973   for (i = 0; i < MAX_PLAYERS; i++)
11974   {
11975     int actual_player_action = stored_player[i].effective_action;
11976
11977 #if 1
11978     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11979        - rnd_equinox_tetrachloride 048
11980        - rnd_equinox_tetrachloride_ii 096
11981        - rnd_emanuel_schmieg 002
11982        - doctor_sloan_ww 001, 020
11983     */
11984     if (stored_player[i].MovPos == 0)
11985       CheckGravityMovement(&stored_player[i]);
11986 #endif
11987
11988     // overwrite programmed action with tape action
11989     if (stored_player[i].programmed_action)
11990       actual_player_action = stored_player[i].programmed_action;
11991
11992     PlayerActions(&stored_player[i], actual_player_action);
11993
11994     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11995   }
11996
11997   // single step pause mode may already have been toggled by "ScrollPlayer()"
11998   if (game.enter_single_step_mode && !tape.pausing)
11999     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12000
12001   ScrollScreen(NULL, SCROLL_GO_ON);
12002
12003   /* for backwards compatibility, the following code emulates a fixed bug that
12004      occured when pushing elements (causing elements that just made their last
12005      pushing step to already (if possible) make their first falling step in the
12006      same game frame, which is bad); this code is also needed to use the famous
12007      "spring push bug" which is used in older levels and might be wanted to be
12008      used also in newer levels, but in this case the buggy pushing code is only
12009      affecting the "spring" element and no other elements */
12010
12011   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12012   {
12013     for (i = 0; i < MAX_PLAYERS; i++)
12014     {
12015       struct PlayerInfo *player = &stored_player[i];
12016       int x = player->jx;
12017       int y = player->jy;
12018
12019       if (player->active && player->is_pushing && player->is_moving &&
12020           IS_MOVING(x, y) &&
12021           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12022            Tile[x][y] == EL_SPRING))
12023       {
12024         ContinueMoving(x, y);
12025
12026         // continue moving after pushing (this is actually a bug)
12027         if (!IS_MOVING(x, y))
12028           Stop[x][y] = FALSE;
12029       }
12030     }
12031   }
12032
12033   SCAN_PLAYFIELD(x, y)
12034   {
12035     Last[x][y] = Tile[x][y];
12036
12037     ChangeCount[x][y] = 0;
12038     ChangeEvent[x][y] = -1;
12039
12040     // this must be handled before main playfield loop
12041     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12042     {
12043       MovDelay[x][y]--;
12044       if (MovDelay[x][y] <= 0)
12045         RemoveField(x, y);
12046     }
12047
12048     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12049     {
12050       MovDelay[x][y]--;
12051       if (MovDelay[x][y] <= 0)
12052       {
12053         int element = Store[x][y];
12054         int move_direction = MovDir[x][y];
12055         int player_index_bit = Store2[x][y];
12056
12057         Store[x][y] = 0;
12058         Store2[x][y] = 0;
12059
12060         RemoveField(x, y);
12061         TEST_DrawLevelField(x, y);
12062
12063         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12064       }
12065     }
12066
12067 #if DEBUG
12068     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12069     {
12070       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12071             x, y);
12072       Debug("game:playing:GameActions_RND", "This should never happen!");
12073
12074       ChangePage[x][y] = -1;
12075     }
12076 #endif
12077
12078     Stop[x][y] = FALSE;
12079     if (WasJustMoving[x][y] > 0)
12080       WasJustMoving[x][y]--;
12081     if (WasJustFalling[x][y] > 0)
12082       WasJustFalling[x][y]--;
12083     if (CheckCollision[x][y] > 0)
12084       CheckCollision[x][y]--;
12085     if (CheckImpact[x][y] > 0)
12086       CheckImpact[x][y]--;
12087
12088     GfxFrame[x][y]++;
12089
12090     /* reset finished pushing action (not done in ContinueMoving() to allow
12091        continuous pushing animation for elements with zero push delay) */
12092     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12093     {
12094       ResetGfxAnimation(x, y);
12095       TEST_DrawLevelField(x, y);
12096     }
12097
12098 #if DEBUG
12099     if (IS_BLOCKED(x, y))
12100     {
12101       int oldx, oldy;
12102
12103       Blocked2Moving(x, y, &oldx, &oldy);
12104       if (!IS_MOVING(oldx, oldy))
12105       {
12106         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12107         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12108         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12109         Debug("game:playing:GameActions_RND", "This should never happen!");
12110       }
12111     }
12112 #endif
12113   }
12114
12115   if (mouse_action.button)
12116   {
12117     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12118
12119     x = mouse_action.lx;
12120     y = mouse_action.ly;
12121     element = Tile[x][y];
12122
12123     if (new_button)
12124     {
12125       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12126       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12127     }
12128
12129     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12130     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12131   }
12132
12133   SCAN_PLAYFIELD(x, y)
12134   {
12135     element = Tile[x][y];
12136     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12137     last_gfx_frame = GfxFrame[x][y];
12138
12139     ResetGfxFrame(x, y);
12140
12141     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12142       DrawLevelGraphicAnimation(x, y, graphic);
12143
12144     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12145         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12146       ResetRandomAnimationValue(x, y);
12147
12148     SetRandomAnimationValue(x, y);
12149
12150     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12151
12152     if (IS_INACTIVE(element))
12153     {
12154       if (IS_ANIMATED(graphic))
12155         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12156
12157       continue;
12158     }
12159
12160     // this may take place after moving, so 'element' may have changed
12161     if (IS_CHANGING(x, y) &&
12162         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12163     {
12164       int page = element_info[element].event_page_nr[CE_DELAY];
12165
12166       HandleElementChange(x, y, page);
12167
12168       element = Tile[x][y];
12169       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12170     }
12171
12172     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12173     {
12174       StartMoving(x, y);
12175
12176       element = Tile[x][y];
12177       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12178
12179       if (IS_ANIMATED(graphic) &&
12180           !IS_MOVING(x, y) &&
12181           !Stop[x][y])
12182         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12183
12184       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12185         TEST_DrawTwinkleOnField(x, y);
12186     }
12187     else if (element == EL_ACID)
12188     {
12189       if (!Stop[x][y])
12190         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12191     }
12192     else if ((element == EL_EXIT_OPEN ||
12193               element == EL_EM_EXIT_OPEN ||
12194               element == EL_SP_EXIT_OPEN ||
12195               element == EL_STEEL_EXIT_OPEN ||
12196               element == EL_EM_STEEL_EXIT_OPEN ||
12197               element == EL_SP_TERMINAL ||
12198               element == EL_SP_TERMINAL_ACTIVE ||
12199               element == EL_EXTRA_TIME ||
12200               element == EL_SHIELD_NORMAL ||
12201               element == EL_SHIELD_DEADLY) &&
12202              IS_ANIMATED(graphic))
12203       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12204     else if (IS_MOVING(x, y))
12205       ContinueMoving(x, y);
12206     else if (IS_ACTIVE_BOMB(element))
12207       CheckDynamite(x, y);
12208     else if (element == EL_AMOEBA_GROWING)
12209       AmoebaGrowing(x, y);
12210     else if (element == EL_AMOEBA_SHRINKING)
12211       AmoebaShrinking(x, y);
12212
12213 #if !USE_NEW_AMOEBA_CODE
12214     else if (IS_AMOEBALIVE(element))
12215       AmoebaReproduce(x, y);
12216 #endif
12217
12218     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12219       Life(x, y);
12220     else if (element == EL_EXIT_CLOSED)
12221       CheckExit(x, y);
12222     else if (element == EL_EM_EXIT_CLOSED)
12223       CheckExitEM(x, y);
12224     else if (element == EL_STEEL_EXIT_CLOSED)
12225       CheckExitSteel(x, y);
12226     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12227       CheckExitSteelEM(x, y);
12228     else if (element == EL_SP_EXIT_CLOSED)
12229       CheckExitSP(x, y);
12230     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12231              element == EL_EXPANDABLE_STEELWALL_GROWING)
12232       MauerWaechst(x, y);
12233     else if (element == EL_EXPANDABLE_WALL ||
12234              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12235              element == EL_EXPANDABLE_WALL_VERTICAL ||
12236              element == EL_EXPANDABLE_WALL_ANY ||
12237              element == EL_BD_EXPANDABLE_WALL)
12238       MauerAbleger(x, y);
12239     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12240              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12241              element == EL_EXPANDABLE_STEELWALL_ANY)
12242       MauerAblegerStahl(x, y);
12243     else if (element == EL_FLAMES)
12244       CheckForDragon(x, y);
12245     else if (element == EL_EXPLOSION)
12246       ; // drawing of correct explosion animation is handled separately
12247     else if (element == EL_ELEMENT_SNAPPING ||
12248              element == EL_DIAGONAL_SHRINKING ||
12249              element == EL_DIAGONAL_GROWING)
12250     {
12251       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12252
12253       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12254     }
12255     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12256       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12257
12258     if (IS_BELT_ACTIVE(element))
12259       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12260
12261     if (game.magic_wall_active)
12262     {
12263       int jx = local_player->jx, jy = local_player->jy;
12264
12265       // play the element sound at the position nearest to the player
12266       if ((element == EL_MAGIC_WALL_FULL ||
12267            element == EL_MAGIC_WALL_ACTIVE ||
12268            element == EL_MAGIC_WALL_EMPTYING ||
12269            element == EL_BD_MAGIC_WALL_FULL ||
12270            element == EL_BD_MAGIC_WALL_ACTIVE ||
12271            element == EL_BD_MAGIC_WALL_EMPTYING ||
12272            element == EL_DC_MAGIC_WALL_FULL ||
12273            element == EL_DC_MAGIC_WALL_ACTIVE ||
12274            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12275           ABS(x - jx) + ABS(y - jy) <
12276           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12277       {
12278         magic_wall_x = x;
12279         magic_wall_y = y;
12280       }
12281     }
12282   }
12283
12284 #if USE_NEW_AMOEBA_CODE
12285   // new experimental amoeba growth stuff
12286   if (!(FrameCounter % 8))
12287   {
12288     static unsigned int random = 1684108901;
12289
12290     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12291     {
12292       x = RND(lev_fieldx);
12293       y = RND(lev_fieldy);
12294       element = Tile[x][y];
12295
12296       if (!IS_PLAYER(x,y) &&
12297           (element == EL_EMPTY ||
12298            CAN_GROW_INTO(element) ||
12299            element == EL_QUICKSAND_EMPTY ||
12300            element == EL_QUICKSAND_FAST_EMPTY ||
12301            element == EL_ACID_SPLASH_LEFT ||
12302            element == EL_ACID_SPLASH_RIGHT))
12303       {
12304         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12305             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12306             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12307             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12308           Tile[x][y] = EL_AMOEBA_DROP;
12309       }
12310
12311       random = random * 129 + 1;
12312     }
12313   }
12314 #endif
12315
12316   game.explosions_delayed = FALSE;
12317
12318   SCAN_PLAYFIELD(x, y)
12319   {
12320     element = Tile[x][y];
12321
12322     if (ExplodeField[x][y])
12323       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12324     else if (element == EL_EXPLOSION)
12325       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12326
12327     ExplodeField[x][y] = EX_TYPE_NONE;
12328   }
12329
12330   game.explosions_delayed = TRUE;
12331
12332   if (game.magic_wall_active)
12333   {
12334     if (!(game.magic_wall_time_left % 4))
12335     {
12336       int element = Tile[magic_wall_x][magic_wall_y];
12337
12338       if (element == EL_BD_MAGIC_WALL_FULL ||
12339           element == EL_BD_MAGIC_WALL_ACTIVE ||
12340           element == EL_BD_MAGIC_WALL_EMPTYING)
12341         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12342       else if (element == EL_DC_MAGIC_WALL_FULL ||
12343                element == EL_DC_MAGIC_WALL_ACTIVE ||
12344                element == EL_DC_MAGIC_WALL_EMPTYING)
12345         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12346       else
12347         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12348     }
12349
12350     if (game.magic_wall_time_left > 0)
12351     {
12352       game.magic_wall_time_left--;
12353
12354       if (!game.magic_wall_time_left)
12355       {
12356         SCAN_PLAYFIELD(x, y)
12357         {
12358           element = Tile[x][y];
12359
12360           if (element == EL_MAGIC_WALL_ACTIVE ||
12361               element == EL_MAGIC_WALL_FULL)
12362           {
12363             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12364             TEST_DrawLevelField(x, y);
12365           }
12366           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12367                    element == EL_BD_MAGIC_WALL_FULL)
12368           {
12369             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12370             TEST_DrawLevelField(x, y);
12371           }
12372           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12373                    element == EL_DC_MAGIC_WALL_FULL)
12374           {
12375             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12376             TEST_DrawLevelField(x, y);
12377           }
12378         }
12379
12380         game.magic_wall_active = FALSE;
12381       }
12382     }
12383   }
12384
12385   if (game.light_time_left > 0)
12386   {
12387     game.light_time_left--;
12388
12389     if (game.light_time_left == 0)
12390       RedrawAllLightSwitchesAndInvisibleElements();
12391   }
12392
12393   if (game.timegate_time_left > 0)
12394   {
12395     game.timegate_time_left--;
12396
12397     if (game.timegate_time_left == 0)
12398       CloseAllOpenTimegates();
12399   }
12400
12401   if (game.lenses_time_left > 0)
12402   {
12403     game.lenses_time_left--;
12404
12405     if (game.lenses_time_left == 0)
12406       RedrawAllInvisibleElementsForLenses();
12407   }
12408
12409   if (game.magnify_time_left > 0)
12410   {
12411     game.magnify_time_left--;
12412
12413     if (game.magnify_time_left == 0)
12414       RedrawAllInvisibleElementsForMagnifier();
12415   }
12416
12417   for (i = 0; i < MAX_PLAYERS; i++)
12418   {
12419     struct PlayerInfo *player = &stored_player[i];
12420
12421     if (SHIELD_ON(player))
12422     {
12423       if (player->shield_deadly_time_left)
12424         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12425       else if (player->shield_normal_time_left)
12426         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12427     }
12428   }
12429
12430 #if USE_DELAYED_GFX_REDRAW
12431   SCAN_PLAYFIELD(x, y)
12432   {
12433     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12434     {
12435       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12436          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12437
12438       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12439         DrawLevelField(x, y);
12440
12441       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12442         DrawLevelFieldCrumbled(x, y);
12443
12444       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12445         DrawLevelFieldCrumbledNeighbours(x, y);
12446
12447       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12448         DrawTwinkleOnField(x, y);
12449     }
12450
12451     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12452   }
12453 #endif
12454
12455   DrawAllPlayers();
12456   PlayAllPlayersSound();
12457
12458   for (i = 0; i < MAX_PLAYERS; i++)
12459   {
12460     struct PlayerInfo *player = &stored_player[i];
12461
12462     if (player->show_envelope != 0 && (!player->active ||
12463                                        player->MovPos == 0))
12464     {
12465       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12466
12467       player->show_envelope = 0;
12468     }
12469   }
12470
12471   // use random number generator in every frame to make it less predictable
12472   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12473     RND(1);
12474
12475   mouse_action_last = mouse_action;
12476 }
12477
12478 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12479 {
12480   int min_x = x, min_y = y, max_x = x, max_y = y;
12481   int scr_fieldx = getScreenFieldSizeX();
12482   int scr_fieldy = getScreenFieldSizeY();
12483   int i;
12484
12485   for (i = 0; i < MAX_PLAYERS; i++)
12486   {
12487     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12488
12489     if (!stored_player[i].active || &stored_player[i] == player)
12490       continue;
12491
12492     min_x = MIN(min_x, jx);
12493     min_y = MIN(min_y, jy);
12494     max_x = MAX(max_x, jx);
12495     max_y = MAX(max_y, jy);
12496   }
12497
12498   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12499 }
12500
12501 static boolean AllPlayersInVisibleScreen(void)
12502 {
12503   int i;
12504
12505   for (i = 0; i < MAX_PLAYERS; i++)
12506   {
12507     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12508
12509     if (!stored_player[i].active)
12510       continue;
12511
12512     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12513       return FALSE;
12514   }
12515
12516   return TRUE;
12517 }
12518
12519 void ScrollLevel(int dx, int dy)
12520 {
12521   int scroll_offset = 2 * TILEX_VAR;
12522   int x, y;
12523
12524   BlitBitmap(drawto_field, drawto_field,
12525              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12526              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12527              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12528              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12529              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12530              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12531
12532   if (dx != 0)
12533   {
12534     x = (dx == 1 ? BX1 : BX2);
12535     for (y = BY1; y <= BY2; y++)
12536       DrawScreenField(x, y);
12537   }
12538
12539   if (dy != 0)
12540   {
12541     y = (dy == 1 ? BY1 : BY2);
12542     for (x = BX1; x <= BX2; x++)
12543       DrawScreenField(x, y);
12544   }
12545
12546   redraw_mask |= REDRAW_FIELD;
12547 }
12548
12549 static boolean canFallDown(struct PlayerInfo *player)
12550 {
12551   int jx = player->jx, jy = player->jy;
12552
12553   return (IN_LEV_FIELD(jx, jy + 1) &&
12554           (IS_FREE(jx, jy + 1) ||
12555            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12556           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12557           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12558 }
12559
12560 static boolean canPassField(int x, int y, int move_dir)
12561 {
12562   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12563   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12564   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12565   int nextx = x + dx;
12566   int nexty = y + dy;
12567   int element = Tile[x][y];
12568
12569   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12570           !CAN_MOVE(element) &&
12571           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12572           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12573           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12574 }
12575
12576 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12577 {
12578   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12579   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12580   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12581   int newx = x + dx;
12582   int newy = y + dy;
12583
12584   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12585           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12586           (IS_DIGGABLE(Tile[newx][newy]) ||
12587            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12588            canPassField(newx, newy, move_dir)));
12589 }
12590
12591 static void CheckGravityMovement(struct PlayerInfo *player)
12592 {
12593   if (player->gravity && !player->programmed_action)
12594   {
12595     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12596     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12597     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12598     int jx = player->jx, jy = player->jy;
12599     boolean player_is_moving_to_valid_field =
12600       (!player_is_snapping &&
12601        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12602         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12603     boolean player_can_fall_down = canFallDown(player);
12604
12605     if (player_can_fall_down &&
12606         !player_is_moving_to_valid_field)
12607       player->programmed_action = MV_DOWN;
12608   }
12609 }
12610
12611 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12612 {
12613   return CheckGravityMovement(player);
12614
12615   if (player->gravity && !player->programmed_action)
12616   {
12617     int jx = player->jx, jy = player->jy;
12618     boolean field_under_player_is_free =
12619       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12620     boolean player_is_standing_on_valid_field =
12621       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12622        (IS_WALKABLE(Tile[jx][jy]) &&
12623         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12624
12625     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12626       player->programmed_action = MV_DOWN;
12627   }
12628 }
12629
12630 /*
12631   MovePlayerOneStep()
12632   -----------------------------------------------------------------------------
12633   dx, dy:               direction (non-diagonal) to try to move the player to
12634   real_dx, real_dy:     direction as read from input device (can be diagonal)
12635 */
12636
12637 boolean MovePlayerOneStep(struct PlayerInfo *player,
12638                           int dx, int dy, int real_dx, int real_dy)
12639 {
12640   int jx = player->jx, jy = player->jy;
12641   int new_jx = jx + dx, new_jy = jy + dy;
12642   int can_move;
12643   boolean player_can_move = !player->cannot_move;
12644
12645   if (!player->active || (!dx && !dy))
12646     return MP_NO_ACTION;
12647
12648   player->MovDir = (dx < 0 ? MV_LEFT :
12649                     dx > 0 ? MV_RIGHT :
12650                     dy < 0 ? MV_UP :
12651                     dy > 0 ? MV_DOWN :  MV_NONE);
12652
12653   if (!IN_LEV_FIELD(new_jx, new_jy))
12654     return MP_NO_ACTION;
12655
12656   if (!player_can_move)
12657   {
12658     if (player->MovPos == 0)
12659     {
12660       player->is_moving = FALSE;
12661       player->is_digging = FALSE;
12662       player->is_collecting = FALSE;
12663       player->is_snapping = FALSE;
12664       player->is_pushing = FALSE;
12665     }
12666   }
12667
12668   if (!network.enabled && game.centered_player_nr == -1 &&
12669       !AllPlayersInSight(player, new_jx, new_jy))
12670     return MP_NO_ACTION;
12671
12672   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12673   if (can_move != MP_MOVING)
12674     return can_move;
12675
12676   // check if DigField() has caused relocation of the player
12677   if (player->jx != jx || player->jy != jy)
12678     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12679
12680   StorePlayer[jx][jy] = 0;
12681   player->last_jx = jx;
12682   player->last_jy = jy;
12683   player->jx = new_jx;
12684   player->jy = new_jy;
12685   StorePlayer[new_jx][new_jy] = player->element_nr;
12686
12687   if (player->move_delay_value_next != -1)
12688   {
12689     player->move_delay_value = player->move_delay_value_next;
12690     player->move_delay_value_next = -1;
12691   }
12692
12693   player->MovPos =
12694     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12695
12696   player->step_counter++;
12697
12698   PlayerVisit[jx][jy] = FrameCounter;
12699
12700   player->is_moving = TRUE;
12701
12702 #if 1
12703   // should better be called in MovePlayer(), but this breaks some tapes
12704   ScrollPlayer(player, SCROLL_INIT);
12705 #endif
12706
12707   return MP_MOVING;
12708 }
12709
12710 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12711 {
12712   int jx = player->jx, jy = player->jy;
12713   int old_jx = jx, old_jy = jy;
12714   int moved = MP_NO_ACTION;
12715
12716   if (!player->active)
12717     return FALSE;
12718
12719   if (!dx && !dy)
12720   {
12721     if (player->MovPos == 0)
12722     {
12723       player->is_moving = FALSE;
12724       player->is_digging = FALSE;
12725       player->is_collecting = FALSE;
12726       player->is_snapping = FALSE;
12727       player->is_pushing = FALSE;
12728     }
12729
12730     return FALSE;
12731   }
12732
12733   if (player->move_delay > 0)
12734     return FALSE;
12735
12736   player->move_delay = -1;              // set to "uninitialized" value
12737
12738   // store if player is automatically moved to next field
12739   player->is_auto_moving = (player->programmed_action != MV_NONE);
12740
12741   // remove the last programmed player action
12742   player->programmed_action = 0;
12743
12744   if (player->MovPos)
12745   {
12746     // should only happen if pre-1.2 tape recordings are played
12747     // this is only for backward compatibility
12748
12749     int original_move_delay_value = player->move_delay_value;
12750
12751 #if DEBUG
12752     Debug("game:playing:MovePlayer",
12753           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12754           tape.counter);
12755 #endif
12756
12757     // scroll remaining steps with finest movement resolution
12758     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12759
12760     while (player->MovPos)
12761     {
12762       ScrollPlayer(player, SCROLL_GO_ON);
12763       ScrollScreen(NULL, SCROLL_GO_ON);
12764
12765       AdvanceFrameAndPlayerCounters(player->index_nr);
12766
12767       DrawAllPlayers();
12768       BackToFront_WithFrameDelay(0);
12769     }
12770
12771     player->move_delay_value = original_move_delay_value;
12772   }
12773
12774   player->is_active = FALSE;
12775
12776   if (player->last_move_dir & MV_HORIZONTAL)
12777   {
12778     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12779       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12780   }
12781   else
12782   {
12783     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12784       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12785   }
12786
12787   if (!moved && !player->is_active)
12788   {
12789     player->is_moving = FALSE;
12790     player->is_digging = FALSE;
12791     player->is_collecting = FALSE;
12792     player->is_snapping = FALSE;
12793     player->is_pushing = FALSE;
12794   }
12795
12796   jx = player->jx;
12797   jy = player->jy;
12798
12799   if (moved & MP_MOVING && !ScreenMovPos &&
12800       (player->index_nr == game.centered_player_nr ||
12801        game.centered_player_nr == -1))
12802   {
12803     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12804
12805     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12806     {
12807       // actual player has left the screen -- scroll in that direction
12808       if (jx != old_jx)         // player has moved horizontally
12809         scroll_x += (jx - old_jx);
12810       else                      // player has moved vertically
12811         scroll_y += (jy - old_jy);
12812     }
12813     else
12814     {
12815       int offset_raw = game.scroll_delay_value;
12816
12817       if (jx != old_jx)         // player has moved horizontally
12818       {
12819         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12820         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12821         int new_scroll_x = jx - MIDPOSX + offset_x;
12822
12823         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12824             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12825           scroll_x = new_scroll_x;
12826
12827         // don't scroll over playfield boundaries
12828         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12829
12830         // don't scroll more than one field at a time
12831         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12832
12833         // don't scroll against the player's moving direction
12834         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12835             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12836           scroll_x = old_scroll_x;
12837       }
12838       else                      // player has moved vertically
12839       {
12840         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12841         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12842         int new_scroll_y = jy - MIDPOSY + offset_y;
12843
12844         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12845             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12846           scroll_y = new_scroll_y;
12847
12848         // don't scroll over playfield boundaries
12849         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12850
12851         // don't scroll more than one field at a time
12852         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12853
12854         // don't scroll against the player's moving direction
12855         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12856             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12857           scroll_y = old_scroll_y;
12858       }
12859     }
12860
12861     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12862     {
12863       if (!network.enabled && game.centered_player_nr == -1 &&
12864           !AllPlayersInVisibleScreen())
12865       {
12866         scroll_x = old_scroll_x;
12867         scroll_y = old_scroll_y;
12868       }
12869       else
12870       {
12871         ScrollScreen(player, SCROLL_INIT);
12872         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12873       }
12874     }
12875   }
12876
12877   player->StepFrame = 0;
12878
12879   if (moved & MP_MOVING)
12880   {
12881     if (old_jx != jx && old_jy == jy)
12882       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12883     else if (old_jx == jx && old_jy != jy)
12884       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12885
12886     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12887
12888     player->last_move_dir = player->MovDir;
12889     player->is_moving = TRUE;
12890     player->is_snapping = FALSE;
12891     player->is_switching = FALSE;
12892     player->is_dropping = FALSE;
12893     player->is_dropping_pressed = FALSE;
12894     player->drop_pressed_delay = 0;
12895
12896 #if 0
12897     // should better be called here than above, but this breaks some tapes
12898     ScrollPlayer(player, SCROLL_INIT);
12899 #endif
12900   }
12901   else
12902   {
12903     CheckGravityMovementWhenNotMoving(player);
12904
12905     player->is_moving = FALSE;
12906
12907     /* at this point, the player is allowed to move, but cannot move right now
12908        (e.g. because of something blocking the way) -- ensure that the player
12909        is also allowed to move in the next frame (in old versions before 3.1.1,
12910        the player was forced to wait again for eight frames before next try) */
12911
12912     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12913       player->move_delay = 0;   // allow direct movement in the next frame
12914   }
12915
12916   if (player->move_delay == -1)         // not yet initialized by DigField()
12917     player->move_delay = player->move_delay_value;
12918
12919   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12920   {
12921     TestIfPlayerTouchesBadThing(jx, jy);
12922     TestIfPlayerTouchesCustomElement(jx, jy);
12923   }
12924
12925   if (!player->active)
12926     RemovePlayer(player);
12927
12928   return moved;
12929 }
12930
12931 void ScrollPlayer(struct PlayerInfo *player, int mode)
12932 {
12933   int jx = player->jx, jy = player->jy;
12934   int last_jx = player->last_jx, last_jy = player->last_jy;
12935   int move_stepsize = TILEX / player->move_delay_value;
12936
12937   if (!player->active)
12938     return;
12939
12940   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12941     return;
12942
12943   if (mode == SCROLL_INIT)
12944   {
12945     player->actual_frame_counter = FrameCounter;
12946     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12947
12948     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12949         Tile[last_jx][last_jy] == EL_EMPTY)
12950     {
12951       int last_field_block_delay = 0;   // start with no blocking at all
12952       int block_delay_adjustment = player->block_delay_adjustment;
12953
12954       // if player blocks last field, add delay for exactly one move
12955       if (player->block_last_field)
12956       {
12957         last_field_block_delay += player->move_delay_value;
12958
12959         // when blocking enabled, prevent moving up despite gravity
12960         if (player->gravity && player->MovDir == MV_UP)
12961           block_delay_adjustment = -1;
12962       }
12963
12964       // add block delay adjustment (also possible when not blocking)
12965       last_field_block_delay += block_delay_adjustment;
12966
12967       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12968       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12969     }
12970
12971     if (player->MovPos != 0)    // player has not yet reached destination
12972       return;
12973   }
12974   else if (!FrameReached(&player->actual_frame_counter, 1))
12975     return;
12976
12977   if (player->MovPos != 0)
12978   {
12979     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12980     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12981
12982     // before DrawPlayer() to draw correct player graphic for this case
12983     if (player->MovPos == 0)
12984       CheckGravityMovement(player);
12985   }
12986
12987   if (player->MovPos == 0)      // player reached destination field
12988   {
12989     if (player->move_delay_reset_counter > 0)
12990     {
12991       player->move_delay_reset_counter--;
12992
12993       if (player->move_delay_reset_counter == 0)
12994       {
12995         // continue with normal speed after quickly moving through gate
12996         HALVE_PLAYER_SPEED(player);
12997
12998         // be able to make the next move without delay
12999         player->move_delay = 0;
13000       }
13001     }
13002
13003     player->last_jx = jx;
13004     player->last_jy = jy;
13005
13006     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13007         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13008         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13009         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13010         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13011         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13012         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13013         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13014     {
13015       ExitPlayer(player);
13016
13017       if (game.players_still_needed == 0 &&
13018           (game.friends_still_needed == 0 ||
13019            IS_SP_ELEMENT(Tile[jx][jy])))
13020         LevelSolved();
13021     }
13022
13023     // this breaks one level: "machine", level 000
13024     {
13025       int move_direction = player->MovDir;
13026       int enter_side = MV_DIR_OPPOSITE(move_direction);
13027       int leave_side = move_direction;
13028       int old_jx = last_jx;
13029       int old_jy = last_jy;
13030       int old_element = Tile[old_jx][old_jy];
13031       int new_element = Tile[jx][jy];
13032
13033       if (IS_CUSTOM_ELEMENT(old_element))
13034         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13035                                    CE_LEFT_BY_PLAYER,
13036                                    player->index_bit, leave_side);
13037
13038       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13039                                           CE_PLAYER_LEAVES_X,
13040                                           player->index_bit, leave_side);
13041
13042       if (IS_CUSTOM_ELEMENT(new_element))
13043         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13044                                    player->index_bit, enter_side);
13045
13046       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13047                                           CE_PLAYER_ENTERS_X,
13048                                           player->index_bit, enter_side);
13049
13050       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13051                                         CE_MOVE_OF_X, move_direction);
13052     }
13053
13054     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13055     {
13056       TestIfPlayerTouchesBadThing(jx, jy);
13057       TestIfPlayerTouchesCustomElement(jx, jy);
13058
13059       /* needed because pushed element has not yet reached its destination,
13060          so it would trigger a change event at its previous field location */
13061       if (!player->is_pushing)
13062         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13063
13064       if (level.finish_dig_collect &&
13065           (player->is_digging || player->is_collecting))
13066       {
13067         int last_element = player->last_removed_element;
13068         int move_direction = player->MovDir;
13069         int enter_side = MV_DIR_OPPOSITE(move_direction);
13070         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13071                             CE_PLAYER_COLLECTS_X);
13072
13073         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13074                                             player->index_bit, enter_side);
13075
13076         player->last_removed_element = EL_UNDEFINED;
13077       }
13078
13079       if (!player->active)
13080         RemovePlayer(player);
13081     }
13082
13083     if (!game.LevelSolved && level.use_step_counter)
13084     {
13085       int i;
13086
13087       TimePlayed++;
13088
13089       if (TimeLeft > 0)
13090       {
13091         TimeLeft--;
13092
13093         if (TimeLeft <= 10 && setup.time_limit)
13094           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13095
13096         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13097
13098         DisplayGameControlValues();
13099
13100         if (!TimeLeft && setup.time_limit)
13101           for (i = 0; i < MAX_PLAYERS; i++)
13102             KillPlayer(&stored_player[i]);
13103       }
13104       else if (game.no_time_limit && !game.all_players_gone)
13105       {
13106         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13107
13108         DisplayGameControlValues();
13109       }
13110     }
13111
13112     if (tape.single_step && tape.recording && !tape.pausing &&
13113         !player->programmed_action)
13114       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13115
13116     if (!player->programmed_action)
13117       CheckSaveEngineSnapshot(player);
13118   }
13119 }
13120
13121 void ScrollScreen(struct PlayerInfo *player, int mode)
13122 {
13123   static unsigned int screen_frame_counter = 0;
13124
13125   if (mode == SCROLL_INIT)
13126   {
13127     // set scrolling step size according to actual player's moving speed
13128     ScrollStepSize = TILEX / player->move_delay_value;
13129
13130     screen_frame_counter = FrameCounter;
13131     ScreenMovDir = player->MovDir;
13132     ScreenMovPos = player->MovPos;
13133     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13134     return;
13135   }
13136   else if (!FrameReached(&screen_frame_counter, 1))
13137     return;
13138
13139   if (ScreenMovPos)
13140   {
13141     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13142     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13143     redraw_mask |= REDRAW_FIELD;
13144   }
13145   else
13146     ScreenMovDir = MV_NONE;
13147 }
13148
13149 void TestIfPlayerTouchesCustomElement(int x, int y)
13150 {
13151   static int xy[4][2] =
13152   {
13153     { 0, -1 },
13154     { -1, 0 },
13155     { +1, 0 },
13156     { 0, +1 }
13157   };
13158   static int trigger_sides[4][2] =
13159   {
13160     // center side       border side
13161     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13162     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13163     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13164     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13165   };
13166   static int touch_dir[4] =
13167   {
13168     MV_LEFT | MV_RIGHT,
13169     MV_UP   | MV_DOWN,
13170     MV_UP   | MV_DOWN,
13171     MV_LEFT | MV_RIGHT
13172   };
13173   int center_element = Tile[x][y];      // should always be non-moving!
13174   int i;
13175
13176   for (i = 0; i < NUM_DIRECTIONS; i++)
13177   {
13178     int xx = x + xy[i][0];
13179     int yy = y + xy[i][1];
13180     int center_side = trigger_sides[i][0];
13181     int border_side = trigger_sides[i][1];
13182     int border_element;
13183
13184     if (!IN_LEV_FIELD(xx, yy))
13185       continue;
13186
13187     if (IS_PLAYER(x, y))                // player found at center element
13188     {
13189       struct PlayerInfo *player = PLAYERINFO(x, y);
13190
13191       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13192         border_element = Tile[xx][yy];          // may be moving!
13193       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13194         border_element = Tile[xx][yy];
13195       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13196         border_element = MovingOrBlocked2Element(xx, yy);
13197       else
13198         continue;               // center and border element do not touch
13199
13200       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13201                                  player->index_bit, border_side);
13202       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13203                                           CE_PLAYER_TOUCHES_X,
13204                                           player->index_bit, border_side);
13205
13206       {
13207         /* use player element that is initially defined in the level playfield,
13208            not the player element that corresponds to the runtime player number
13209            (example: a level that contains EL_PLAYER_3 as the only player would
13210            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13211         int player_element = PLAYERINFO(x, y)->initial_element;
13212
13213         CheckElementChangeBySide(xx, yy, border_element, player_element,
13214                                  CE_TOUCHING_X, border_side);
13215       }
13216     }
13217     else if (IS_PLAYER(xx, yy))         // player found at border element
13218     {
13219       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13220
13221       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13222       {
13223         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13224           continue;             // center and border element do not touch
13225       }
13226
13227       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13228                                  player->index_bit, center_side);
13229       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13230                                           CE_PLAYER_TOUCHES_X,
13231                                           player->index_bit, center_side);
13232
13233       {
13234         /* use player element that is initially defined in the level playfield,
13235            not the player element that corresponds to the runtime player number
13236            (example: a level that contains EL_PLAYER_3 as the only player would
13237            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13238         int player_element = PLAYERINFO(xx, yy)->initial_element;
13239
13240         CheckElementChangeBySide(x, y, center_element, player_element,
13241                                  CE_TOUCHING_X, center_side);
13242       }
13243
13244       break;
13245     }
13246   }
13247 }
13248
13249 void TestIfElementTouchesCustomElement(int x, int y)
13250 {
13251   static int xy[4][2] =
13252   {
13253     { 0, -1 },
13254     { -1, 0 },
13255     { +1, 0 },
13256     { 0, +1 }
13257   };
13258   static int trigger_sides[4][2] =
13259   {
13260     // center side      border side
13261     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13262     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13263     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13264     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13265   };
13266   static int touch_dir[4] =
13267   {
13268     MV_LEFT | MV_RIGHT,
13269     MV_UP   | MV_DOWN,
13270     MV_UP   | MV_DOWN,
13271     MV_LEFT | MV_RIGHT
13272   };
13273   boolean change_center_element = FALSE;
13274   int center_element = Tile[x][y];      // should always be non-moving!
13275   int border_element_old[NUM_DIRECTIONS];
13276   int i;
13277
13278   for (i = 0; i < NUM_DIRECTIONS; i++)
13279   {
13280     int xx = x + xy[i][0];
13281     int yy = y + xy[i][1];
13282     int border_element;
13283
13284     border_element_old[i] = -1;
13285
13286     if (!IN_LEV_FIELD(xx, yy))
13287       continue;
13288
13289     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13290       border_element = Tile[xx][yy];    // may be moving!
13291     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13292       border_element = Tile[xx][yy];
13293     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13294       border_element = MovingOrBlocked2Element(xx, yy);
13295     else
13296       continue;                 // center and border element do not touch
13297
13298     border_element_old[i] = border_element;
13299   }
13300
13301   for (i = 0; i < NUM_DIRECTIONS; i++)
13302   {
13303     int xx = x + xy[i][0];
13304     int yy = y + xy[i][1];
13305     int center_side = trigger_sides[i][0];
13306     int border_element = border_element_old[i];
13307
13308     if (border_element == -1)
13309       continue;
13310
13311     // check for change of border element
13312     CheckElementChangeBySide(xx, yy, border_element, center_element,
13313                              CE_TOUCHING_X, center_side);
13314
13315     // (center element cannot be player, so we dont have to check this here)
13316   }
13317
13318   for (i = 0; i < NUM_DIRECTIONS; i++)
13319   {
13320     int xx = x + xy[i][0];
13321     int yy = y + xy[i][1];
13322     int border_side = trigger_sides[i][1];
13323     int border_element = border_element_old[i];
13324
13325     if (border_element == -1)
13326       continue;
13327
13328     // check for change of center element (but change it only once)
13329     if (!change_center_element)
13330       change_center_element =
13331         CheckElementChangeBySide(x, y, center_element, border_element,
13332                                  CE_TOUCHING_X, border_side);
13333
13334     if (IS_PLAYER(xx, yy))
13335     {
13336       /* use player element that is initially defined in the level playfield,
13337          not the player element that corresponds to the runtime player number
13338          (example: a level that contains EL_PLAYER_3 as the only player would
13339          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13340       int player_element = PLAYERINFO(xx, yy)->initial_element;
13341
13342       CheckElementChangeBySide(x, y, center_element, player_element,
13343                                CE_TOUCHING_X, border_side);
13344     }
13345   }
13346 }
13347
13348 void TestIfElementHitsCustomElement(int x, int y, int direction)
13349 {
13350   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13351   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13352   int hitx = x + dx, hity = y + dy;
13353   int hitting_element = Tile[x][y];
13354   int touched_element;
13355
13356   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13357     return;
13358
13359   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13360                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13361
13362   if (IN_LEV_FIELD(hitx, hity))
13363   {
13364     int opposite_direction = MV_DIR_OPPOSITE(direction);
13365     int hitting_side = direction;
13366     int touched_side = opposite_direction;
13367     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13368                           MovDir[hitx][hity] != direction ||
13369                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13370
13371     object_hit = TRUE;
13372
13373     if (object_hit)
13374     {
13375       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13376                                CE_HITTING_X, touched_side);
13377
13378       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13379                                CE_HIT_BY_X, hitting_side);
13380
13381       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13382                                CE_HIT_BY_SOMETHING, opposite_direction);
13383
13384       if (IS_PLAYER(hitx, hity))
13385       {
13386         /* use player element that is initially defined in the level playfield,
13387            not the player element that corresponds to the runtime player number
13388            (example: a level that contains EL_PLAYER_3 as the only player would
13389            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13390         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13391
13392         CheckElementChangeBySide(x, y, hitting_element, player_element,
13393                                  CE_HITTING_X, touched_side);
13394       }
13395     }
13396   }
13397
13398   // "hitting something" is also true when hitting the playfield border
13399   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13400                            CE_HITTING_SOMETHING, direction);
13401 }
13402
13403 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13404 {
13405   int i, kill_x = -1, kill_y = -1;
13406
13407   int bad_element = -1;
13408   static int test_xy[4][2] =
13409   {
13410     { 0, -1 },
13411     { -1, 0 },
13412     { +1, 0 },
13413     { 0, +1 }
13414   };
13415   static int test_dir[4] =
13416   {
13417     MV_UP,
13418     MV_LEFT,
13419     MV_RIGHT,
13420     MV_DOWN
13421   };
13422
13423   for (i = 0; i < NUM_DIRECTIONS; i++)
13424   {
13425     int test_x, test_y, test_move_dir, test_element;
13426
13427     test_x = good_x + test_xy[i][0];
13428     test_y = good_y + test_xy[i][1];
13429
13430     if (!IN_LEV_FIELD(test_x, test_y))
13431       continue;
13432
13433     test_move_dir =
13434       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13435
13436     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13437
13438     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13439        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13440     */
13441     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13442         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13443     {
13444       kill_x = test_x;
13445       kill_y = test_y;
13446       bad_element = test_element;
13447
13448       break;
13449     }
13450   }
13451
13452   if (kill_x != -1 || kill_y != -1)
13453   {
13454     if (IS_PLAYER(good_x, good_y))
13455     {
13456       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13457
13458       if (player->shield_deadly_time_left > 0 &&
13459           !IS_INDESTRUCTIBLE(bad_element))
13460         Bang(kill_x, kill_y);
13461       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13462         KillPlayer(player);
13463     }
13464     else
13465       Bang(good_x, good_y);
13466   }
13467 }
13468
13469 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13470 {
13471   int i, kill_x = -1, kill_y = -1;
13472   int bad_element = Tile[bad_x][bad_y];
13473   static int test_xy[4][2] =
13474   {
13475     { 0, -1 },
13476     { -1, 0 },
13477     { +1, 0 },
13478     { 0, +1 }
13479   };
13480   static int touch_dir[4] =
13481   {
13482     MV_LEFT | MV_RIGHT,
13483     MV_UP   | MV_DOWN,
13484     MV_UP   | MV_DOWN,
13485     MV_LEFT | MV_RIGHT
13486   };
13487   static int test_dir[4] =
13488   {
13489     MV_UP,
13490     MV_LEFT,
13491     MV_RIGHT,
13492     MV_DOWN
13493   };
13494
13495   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13496     return;
13497
13498   for (i = 0; i < NUM_DIRECTIONS; i++)
13499   {
13500     int test_x, test_y, test_move_dir, test_element;
13501
13502     test_x = bad_x + test_xy[i][0];
13503     test_y = bad_y + test_xy[i][1];
13504
13505     if (!IN_LEV_FIELD(test_x, test_y))
13506       continue;
13507
13508     test_move_dir =
13509       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13510
13511     test_element = Tile[test_x][test_y];
13512
13513     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13514        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13515     */
13516     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13517         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13518     {
13519       // good thing is player or penguin that does not move away
13520       if (IS_PLAYER(test_x, test_y))
13521       {
13522         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13523
13524         if (bad_element == EL_ROBOT && player->is_moving)
13525           continue;     // robot does not kill player if he is moving
13526
13527         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13528         {
13529           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13530             continue;           // center and border element do not touch
13531         }
13532
13533         kill_x = test_x;
13534         kill_y = test_y;
13535
13536         break;
13537       }
13538       else if (test_element == EL_PENGUIN)
13539       {
13540         kill_x = test_x;
13541         kill_y = test_y;
13542
13543         break;
13544       }
13545     }
13546   }
13547
13548   if (kill_x != -1 || kill_y != -1)
13549   {
13550     if (IS_PLAYER(kill_x, kill_y))
13551     {
13552       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13553
13554       if (player->shield_deadly_time_left > 0 &&
13555           !IS_INDESTRUCTIBLE(bad_element))
13556         Bang(bad_x, bad_y);
13557       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13558         KillPlayer(player);
13559     }
13560     else
13561       Bang(kill_x, kill_y);
13562   }
13563 }
13564
13565 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13566 {
13567   int bad_element = Tile[bad_x][bad_y];
13568   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13569   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13570   int test_x = bad_x + dx, test_y = bad_y + dy;
13571   int test_move_dir, test_element;
13572   int kill_x = -1, kill_y = -1;
13573
13574   if (!IN_LEV_FIELD(test_x, test_y))
13575     return;
13576
13577   test_move_dir =
13578     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13579
13580   test_element = Tile[test_x][test_y];
13581
13582   if (test_move_dir != bad_move_dir)
13583   {
13584     // good thing can be player or penguin that does not move away
13585     if (IS_PLAYER(test_x, test_y))
13586     {
13587       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13588
13589       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13590          player as being hit when he is moving towards the bad thing, because
13591          the "get hit by" condition would be lost after the player stops) */
13592       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13593         return;         // player moves away from bad thing
13594
13595       kill_x = test_x;
13596       kill_y = test_y;
13597     }
13598     else if (test_element == EL_PENGUIN)
13599     {
13600       kill_x = test_x;
13601       kill_y = test_y;
13602     }
13603   }
13604
13605   if (kill_x != -1 || kill_y != -1)
13606   {
13607     if (IS_PLAYER(kill_x, kill_y))
13608     {
13609       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13610
13611       if (player->shield_deadly_time_left > 0 &&
13612           !IS_INDESTRUCTIBLE(bad_element))
13613         Bang(bad_x, bad_y);
13614       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13615         KillPlayer(player);
13616     }
13617     else
13618       Bang(kill_x, kill_y);
13619   }
13620 }
13621
13622 void TestIfPlayerTouchesBadThing(int x, int y)
13623 {
13624   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13625 }
13626
13627 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13628 {
13629   TestIfGoodThingHitsBadThing(x, y, move_dir);
13630 }
13631
13632 void TestIfBadThingTouchesPlayer(int x, int y)
13633 {
13634   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13635 }
13636
13637 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13638 {
13639   TestIfBadThingHitsGoodThing(x, y, move_dir);
13640 }
13641
13642 void TestIfFriendTouchesBadThing(int x, int y)
13643 {
13644   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13645 }
13646
13647 void TestIfBadThingTouchesFriend(int x, int y)
13648 {
13649   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13650 }
13651
13652 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13653 {
13654   int i, kill_x = bad_x, kill_y = bad_y;
13655   static int xy[4][2] =
13656   {
13657     { 0, -1 },
13658     { -1, 0 },
13659     { +1, 0 },
13660     { 0, +1 }
13661   };
13662
13663   for (i = 0; i < NUM_DIRECTIONS; i++)
13664   {
13665     int x, y, element;
13666
13667     x = bad_x + xy[i][0];
13668     y = bad_y + xy[i][1];
13669     if (!IN_LEV_FIELD(x, y))
13670       continue;
13671
13672     element = Tile[x][y];
13673     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13674         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13675     {
13676       kill_x = x;
13677       kill_y = y;
13678       break;
13679     }
13680   }
13681
13682   if (kill_x != bad_x || kill_y != bad_y)
13683     Bang(bad_x, bad_y);
13684 }
13685
13686 void KillPlayer(struct PlayerInfo *player)
13687 {
13688   int jx = player->jx, jy = player->jy;
13689
13690   if (!player->active)
13691     return;
13692
13693 #if 0
13694   Debug("game:playing:KillPlayer",
13695         "0: killed == %d, active == %d, reanimated == %d",
13696         player->killed, player->active, player->reanimated);
13697 #endif
13698
13699   /* the following code was introduced to prevent an infinite loop when calling
13700      -> Bang()
13701      -> CheckTriggeredElementChangeExt()
13702      -> ExecuteCustomElementAction()
13703      -> KillPlayer()
13704      -> (infinitely repeating the above sequence of function calls)
13705      which occurs when killing the player while having a CE with the setting
13706      "kill player X when explosion of <player X>"; the solution using a new
13707      field "player->killed" was chosen for backwards compatibility, although
13708      clever use of the fields "player->active" etc. would probably also work */
13709 #if 1
13710   if (player->killed)
13711     return;
13712 #endif
13713
13714   player->killed = TRUE;
13715
13716   // remove accessible field at the player's position
13717   Tile[jx][jy] = EL_EMPTY;
13718
13719   // deactivate shield (else Bang()/Explode() would not work right)
13720   player->shield_normal_time_left = 0;
13721   player->shield_deadly_time_left = 0;
13722
13723 #if 0
13724   Debug("game:playing:KillPlayer",
13725         "1: killed == %d, active == %d, reanimated == %d",
13726         player->killed, player->active, player->reanimated);
13727 #endif
13728
13729   Bang(jx, jy);
13730
13731 #if 0
13732   Debug("game:playing:KillPlayer",
13733         "2: killed == %d, active == %d, reanimated == %d",
13734         player->killed, player->active, player->reanimated);
13735 #endif
13736
13737   if (player->reanimated)       // killed player may have been reanimated
13738     player->killed = player->reanimated = FALSE;
13739   else
13740     BuryPlayer(player);
13741 }
13742
13743 static void KillPlayerUnlessEnemyProtected(int x, int y)
13744 {
13745   if (!PLAYER_ENEMY_PROTECTED(x, y))
13746     KillPlayer(PLAYERINFO(x, y));
13747 }
13748
13749 static void KillPlayerUnlessExplosionProtected(int x, int y)
13750 {
13751   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13752     KillPlayer(PLAYERINFO(x, y));
13753 }
13754
13755 void BuryPlayer(struct PlayerInfo *player)
13756 {
13757   int jx = player->jx, jy = player->jy;
13758
13759   if (!player->active)
13760     return;
13761
13762   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13763   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13764
13765   RemovePlayer(player);
13766
13767   player->buried = TRUE;
13768
13769   if (game.all_players_gone)
13770     game.GameOver = TRUE;
13771 }
13772
13773 void RemovePlayer(struct PlayerInfo *player)
13774 {
13775   int jx = player->jx, jy = player->jy;
13776   int i, found = FALSE;
13777
13778   player->present = FALSE;
13779   player->active = FALSE;
13780
13781   // required for some CE actions (even if the player is not active anymore)
13782   player->MovPos = 0;
13783
13784   if (!ExplodeField[jx][jy])
13785     StorePlayer[jx][jy] = 0;
13786
13787   if (player->is_moving)
13788     TEST_DrawLevelField(player->last_jx, player->last_jy);
13789
13790   for (i = 0; i < MAX_PLAYERS; i++)
13791     if (stored_player[i].active)
13792       found = TRUE;
13793
13794   if (!found)
13795   {
13796     game.all_players_gone = TRUE;
13797     game.GameOver = TRUE;
13798   }
13799
13800   game.exit_x = game.robot_wheel_x = jx;
13801   game.exit_y = game.robot_wheel_y = jy;
13802 }
13803
13804 void ExitPlayer(struct PlayerInfo *player)
13805 {
13806   DrawPlayer(player);   // needed here only to cleanup last field
13807   RemovePlayer(player);
13808
13809   if (game.players_still_needed > 0)
13810     game.players_still_needed--;
13811 }
13812
13813 static void SetFieldForSnapping(int x, int y, int element, int direction,
13814                                 int player_index_bit)
13815 {
13816   struct ElementInfo *ei = &element_info[element];
13817   int direction_bit = MV_DIR_TO_BIT(direction);
13818   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13819   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13820                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13821
13822   Tile[x][y] = EL_ELEMENT_SNAPPING;
13823   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13824   MovDir[x][y] = direction;
13825   Store[x][y] = element;
13826   Store2[x][y] = player_index_bit;
13827
13828   ResetGfxAnimation(x, y);
13829
13830   GfxElement[x][y] = element;
13831   GfxAction[x][y] = action;
13832   GfxDir[x][y] = direction;
13833   GfxFrame[x][y] = -1;
13834 }
13835
13836 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13837                                    int player_index_bit)
13838 {
13839   TestIfElementTouchesCustomElement(x, y);      // for empty space
13840
13841   if (level.finish_dig_collect)
13842   {
13843     int dig_side = MV_DIR_OPPOSITE(direction);
13844
13845     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13846                                         player_index_bit, dig_side);
13847   }
13848 }
13849
13850 /*
13851   =============================================================================
13852   checkDiagonalPushing()
13853   -----------------------------------------------------------------------------
13854   check if diagonal input device direction results in pushing of object
13855   (by checking if the alternative direction is walkable, diggable, ...)
13856   =============================================================================
13857 */
13858
13859 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13860                                     int x, int y, int real_dx, int real_dy)
13861 {
13862   int jx, jy, dx, dy, xx, yy;
13863
13864   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13865     return TRUE;
13866
13867   // diagonal direction: check alternative direction
13868   jx = player->jx;
13869   jy = player->jy;
13870   dx = x - jx;
13871   dy = y - jy;
13872   xx = jx + (dx == 0 ? real_dx : 0);
13873   yy = jy + (dy == 0 ? real_dy : 0);
13874
13875   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13876 }
13877
13878 /*
13879   =============================================================================
13880   DigField()
13881   -----------------------------------------------------------------------------
13882   x, y:                 field next to player (non-diagonal) to try to dig to
13883   real_dx, real_dy:     direction as read from input device (can be diagonal)
13884   =============================================================================
13885 */
13886
13887 static int DigField(struct PlayerInfo *player,
13888                     int oldx, int oldy, int x, int y,
13889                     int real_dx, int real_dy, int mode)
13890 {
13891   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13892   boolean player_was_pushing = player->is_pushing;
13893   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13894   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13895   int jx = oldx, jy = oldy;
13896   int dx = x - jx, dy = y - jy;
13897   int nextx = x + dx, nexty = y + dy;
13898   int move_direction = (dx == -1 ? MV_LEFT  :
13899                         dx == +1 ? MV_RIGHT :
13900                         dy == -1 ? MV_UP    :
13901                         dy == +1 ? MV_DOWN  : MV_NONE);
13902   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13903   int dig_side = MV_DIR_OPPOSITE(move_direction);
13904   int old_element = Tile[jx][jy];
13905   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13906   int collect_count;
13907
13908   if (is_player)                // function can also be called by EL_PENGUIN
13909   {
13910     if (player->MovPos == 0)
13911     {
13912       player->is_digging = FALSE;
13913       player->is_collecting = FALSE;
13914     }
13915
13916     if (player->MovPos == 0)    // last pushing move finished
13917       player->is_pushing = FALSE;
13918
13919     if (mode == DF_NO_PUSH)     // player just stopped pushing
13920     {
13921       player->is_switching = FALSE;
13922       player->push_delay = -1;
13923
13924       return MP_NO_ACTION;
13925     }
13926   }
13927
13928   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13929     old_element = Back[jx][jy];
13930
13931   // in case of element dropped at player position, check background
13932   else if (Back[jx][jy] != EL_EMPTY &&
13933            game.engine_version >= VERSION_IDENT(2,2,0,0))
13934     old_element = Back[jx][jy];
13935
13936   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13937     return MP_NO_ACTION;        // field has no opening in this direction
13938
13939   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13940     return MP_NO_ACTION;        // field has no opening in this direction
13941
13942   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13943   {
13944     SplashAcid(x, y);
13945
13946     Tile[jx][jy] = player->artwork_element;
13947     InitMovingField(jx, jy, MV_DOWN);
13948     Store[jx][jy] = EL_ACID;
13949     ContinueMoving(jx, jy);
13950     BuryPlayer(player);
13951
13952     return MP_DONT_RUN_INTO;
13953   }
13954
13955   if (player_can_move && DONT_RUN_INTO(element))
13956   {
13957     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13958
13959     return MP_DONT_RUN_INTO;
13960   }
13961
13962   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13963     return MP_NO_ACTION;
13964
13965   collect_count = element_info[element].collect_count_initial;
13966
13967   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13968     return MP_NO_ACTION;
13969
13970   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13971     player_can_move = player_can_move_or_snap;
13972
13973   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13974       game.engine_version >= VERSION_IDENT(2,2,0,0))
13975   {
13976     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13977                                player->index_bit, dig_side);
13978     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13979                                         player->index_bit, dig_side);
13980
13981     if (element == EL_DC_LANDMINE)
13982       Bang(x, y);
13983
13984     if (Tile[x][y] != element)          // field changed by snapping
13985       return MP_ACTION;
13986
13987     return MP_NO_ACTION;
13988   }
13989
13990   if (player->gravity && is_player && !player->is_auto_moving &&
13991       canFallDown(player) && move_direction != MV_DOWN &&
13992       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13993     return MP_NO_ACTION;        // player cannot walk here due to gravity
13994
13995   if (player_can_move &&
13996       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13997   {
13998     int sound_element = SND_ELEMENT(element);
13999     int sound_action = ACTION_WALKING;
14000
14001     if (IS_RND_GATE(element))
14002     {
14003       if (!player->key[RND_GATE_NR(element)])
14004         return MP_NO_ACTION;
14005     }
14006     else if (IS_RND_GATE_GRAY(element))
14007     {
14008       if (!player->key[RND_GATE_GRAY_NR(element)])
14009         return MP_NO_ACTION;
14010     }
14011     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14012     {
14013       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14014         return MP_NO_ACTION;
14015     }
14016     else if (element == EL_EXIT_OPEN ||
14017              element == EL_EM_EXIT_OPEN ||
14018              element == EL_EM_EXIT_OPENING ||
14019              element == EL_STEEL_EXIT_OPEN ||
14020              element == EL_EM_STEEL_EXIT_OPEN ||
14021              element == EL_EM_STEEL_EXIT_OPENING ||
14022              element == EL_SP_EXIT_OPEN ||
14023              element == EL_SP_EXIT_OPENING)
14024     {
14025       sound_action = ACTION_PASSING;    // player is passing exit
14026     }
14027     else if (element == EL_EMPTY)
14028     {
14029       sound_action = ACTION_MOVING;             // nothing to walk on
14030     }
14031
14032     // play sound from background or player, whatever is available
14033     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14034       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14035     else
14036       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14037   }
14038   else if (player_can_move &&
14039            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14040   {
14041     if (!ACCESS_FROM(element, opposite_direction))
14042       return MP_NO_ACTION;      // field not accessible from this direction
14043
14044     if (CAN_MOVE(element))      // only fixed elements can be passed!
14045       return MP_NO_ACTION;
14046
14047     if (IS_EM_GATE(element))
14048     {
14049       if (!player->key[EM_GATE_NR(element)])
14050         return MP_NO_ACTION;
14051     }
14052     else if (IS_EM_GATE_GRAY(element))
14053     {
14054       if (!player->key[EM_GATE_GRAY_NR(element)])
14055         return MP_NO_ACTION;
14056     }
14057     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14058     {
14059       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14060         return MP_NO_ACTION;
14061     }
14062     else if (IS_EMC_GATE(element))
14063     {
14064       if (!player->key[EMC_GATE_NR(element)])
14065         return MP_NO_ACTION;
14066     }
14067     else if (IS_EMC_GATE_GRAY(element))
14068     {
14069       if (!player->key[EMC_GATE_GRAY_NR(element)])
14070         return MP_NO_ACTION;
14071     }
14072     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14073     {
14074       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14075         return MP_NO_ACTION;
14076     }
14077     else if (element == EL_DC_GATE_WHITE ||
14078              element == EL_DC_GATE_WHITE_GRAY ||
14079              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14080     {
14081       if (player->num_white_keys == 0)
14082         return MP_NO_ACTION;
14083
14084       player->num_white_keys--;
14085     }
14086     else if (IS_SP_PORT(element))
14087     {
14088       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14089           element == EL_SP_GRAVITY_PORT_RIGHT ||
14090           element == EL_SP_GRAVITY_PORT_UP ||
14091           element == EL_SP_GRAVITY_PORT_DOWN)
14092         player->gravity = !player->gravity;
14093       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14094                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14095                element == EL_SP_GRAVITY_ON_PORT_UP ||
14096                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14097         player->gravity = TRUE;
14098       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14099                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14100                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14101                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14102         player->gravity = FALSE;
14103     }
14104
14105     // automatically move to the next field with double speed
14106     player->programmed_action = move_direction;
14107
14108     if (player->move_delay_reset_counter == 0)
14109     {
14110       player->move_delay_reset_counter = 2;     // two double speed steps
14111
14112       DOUBLE_PLAYER_SPEED(player);
14113     }
14114
14115     PlayLevelSoundAction(x, y, ACTION_PASSING);
14116   }
14117   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14118   {
14119     RemoveField(x, y);
14120
14121     if (mode != DF_SNAP)
14122     {
14123       GfxElement[x][y] = GFX_ELEMENT(element);
14124       player->is_digging = TRUE;
14125     }
14126
14127     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14128
14129     // use old behaviour for old levels (digging)
14130     if (!level.finish_dig_collect)
14131     {
14132       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14133                                           player->index_bit, dig_side);
14134
14135       // if digging triggered player relocation, finish digging tile
14136       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14137         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14138     }
14139
14140     if (mode == DF_SNAP)
14141     {
14142       if (level.block_snap_field)
14143         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14144       else
14145         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14146
14147       // use old behaviour for old levels (snapping)
14148       if (!level.finish_dig_collect)
14149         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14150                                             player->index_bit, dig_side);
14151     }
14152   }
14153   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14154   {
14155     RemoveField(x, y);
14156
14157     if (is_player && mode != DF_SNAP)
14158     {
14159       GfxElement[x][y] = element;
14160       player->is_collecting = TRUE;
14161     }
14162
14163     if (element == EL_SPEED_PILL)
14164     {
14165       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14166     }
14167     else if (element == EL_EXTRA_TIME && level.time > 0)
14168     {
14169       TimeLeft += level.extra_time;
14170
14171       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14172
14173       DisplayGameControlValues();
14174     }
14175     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14176     {
14177       player->shield_normal_time_left += level.shield_normal_time;
14178       if (element == EL_SHIELD_DEADLY)
14179         player->shield_deadly_time_left += level.shield_deadly_time;
14180     }
14181     else if (element == EL_DYNAMITE ||
14182              element == EL_EM_DYNAMITE ||
14183              element == EL_SP_DISK_RED)
14184     {
14185       if (player->inventory_size < MAX_INVENTORY_SIZE)
14186         player->inventory_element[player->inventory_size++] = element;
14187
14188       DrawGameDoorValues();
14189     }
14190     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14191     {
14192       player->dynabomb_count++;
14193       player->dynabombs_left++;
14194     }
14195     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14196     {
14197       player->dynabomb_size++;
14198     }
14199     else if (element == EL_DYNABOMB_INCREASE_POWER)
14200     {
14201       player->dynabomb_xl = TRUE;
14202     }
14203     else if (IS_KEY(element))
14204     {
14205       player->key[KEY_NR(element)] = TRUE;
14206
14207       DrawGameDoorValues();
14208     }
14209     else if (element == EL_DC_KEY_WHITE)
14210     {
14211       player->num_white_keys++;
14212
14213       // display white keys?
14214       // DrawGameDoorValues();
14215     }
14216     else if (IS_ENVELOPE(element))
14217     {
14218       player->show_envelope = element;
14219     }
14220     else if (element == EL_EMC_LENSES)
14221     {
14222       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14223
14224       RedrawAllInvisibleElementsForLenses();
14225     }
14226     else if (element == EL_EMC_MAGNIFIER)
14227     {
14228       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14229
14230       RedrawAllInvisibleElementsForMagnifier();
14231     }
14232     else if (IS_DROPPABLE(element) ||
14233              IS_THROWABLE(element))     // can be collected and dropped
14234     {
14235       int i;
14236
14237       if (collect_count == 0)
14238         player->inventory_infinite_element = element;
14239       else
14240         for (i = 0; i < collect_count; i++)
14241           if (player->inventory_size < MAX_INVENTORY_SIZE)
14242             player->inventory_element[player->inventory_size++] = element;
14243
14244       DrawGameDoorValues();
14245     }
14246     else if (collect_count > 0)
14247     {
14248       game.gems_still_needed -= collect_count;
14249       if (game.gems_still_needed < 0)
14250         game.gems_still_needed = 0;
14251
14252       game.snapshot.collected_item = TRUE;
14253
14254       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14255
14256       DisplayGameControlValues();
14257     }
14258
14259     RaiseScoreElement(element);
14260     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14261
14262     // use old behaviour for old levels (collecting)
14263     if (!level.finish_dig_collect && is_player)
14264     {
14265       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14266                                           player->index_bit, dig_side);
14267
14268       // if collecting triggered player relocation, finish collecting tile
14269       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14270         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14271     }
14272
14273     if (mode == DF_SNAP)
14274     {
14275       if (level.block_snap_field)
14276         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14277       else
14278         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14279
14280       // use old behaviour for old levels (snapping)
14281       if (!level.finish_dig_collect)
14282         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14283                                             player->index_bit, dig_side);
14284     }
14285   }
14286   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14287   {
14288     if (mode == DF_SNAP && element != EL_BD_ROCK)
14289       return MP_NO_ACTION;
14290
14291     if (CAN_FALL(element) && dy)
14292       return MP_NO_ACTION;
14293
14294     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14295         !(element == EL_SPRING && level.use_spring_bug))
14296       return MP_NO_ACTION;
14297
14298     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14299         ((move_direction & MV_VERTICAL &&
14300           ((element_info[element].move_pattern & MV_LEFT &&
14301             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14302            (element_info[element].move_pattern & MV_RIGHT &&
14303             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14304          (move_direction & MV_HORIZONTAL &&
14305           ((element_info[element].move_pattern & MV_UP &&
14306             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14307            (element_info[element].move_pattern & MV_DOWN &&
14308             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14309       return MP_NO_ACTION;
14310
14311     // do not push elements already moving away faster than player
14312     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14313         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14314       return MP_NO_ACTION;
14315
14316     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14317     {
14318       if (player->push_delay_value == -1 || !player_was_pushing)
14319         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14320     }
14321     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14322     {
14323       if (player->push_delay_value == -1)
14324         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14325     }
14326     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14327     {
14328       if (!player->is_pushing)
14329         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14330     }
14331
14332     player->is_pushing = TRUE;
14333     player->is_active = TRUE;
14334
14335     if (!(IN_LEV_FIELD(nextx, nexty) &&
14336           (IS_FREE(nextx, nexty) ||
14337            (IS_SB_ELEMENT(element) &&
14338             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14339            (IS_CUSTOM_ELEMENT(element) &&
14340             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14341       return MP_NO_ACTION;
14342
14343     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14344       return MP_NO_ACTION;
14345
14346     if (player->push_delay == -1)       // new pushing; restart delay
14347       player->push_delay = 0;
14348
14349     if (player->push_delay < player->push_delay_value &&
14350         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14351         element != EL_SPRING && element != EL_BALLOON)
14352     {
14353       // make sure that there is no move delay before next try to push
14354       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14355         player->move_delay = 0;
14356
14357       return MP_NO_ACTION;
14358     }
14359
14360     if (IS_CUSTOM_ELEMENT(element) &&
14361         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14362     {
14363       if (!DigFieldByCE(nextx, nexty, element))
14364         return MP_NO_ACTION;
14365     }
14366
14367     if (IS_SB_ELEMENT(element))
14368     {
14369       boolean sokoban_task_solved = FALSE;
14370
14371       if (element == EL_SOKOBAN_FIELD_FULL)
14372       {
14373         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14374
14375         IncrementSokobanFieldsNeeded();
14376         IncrementSokobanObjectsNeeded();
14377       }
14378
14379       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14380       {
14381         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14382
14383         DecrementSokobanFieldsNeeded();
14384         DecrementSokobanObjectsNeeded();
14385
14386         // sokoban object was pushed from empty field to sokoban field
14387         if (Back[x][y] == EL_EMPTY)
14388           sokoban_task_solved = TRUE;
14389       }
14390
14391       Tile[x][y] = EL_SOKOBAN_OBJECT;
14392
14393       if (Back[x][y] == Back[nextx][nexty])
14394         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14395       else if (Back[x][y] != 0)
14396         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14397                                     ACTION_EMPTYING);
14398       else
14399         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14400                                     ACTION_FILLING);
14401
14402       if (sokoban_task_solved &&
14403           game.sokoban_fields_still_needed == 0 &&
14404           game.sokoban_objects_still_needed == 0 &&
14405           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14406       {
14407         game.players_still_needed = 0;
14408
14409         LevelSolved();
14410
14411         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14412       }
14413     }
14414     else
14415       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14416
14417     InitMovingField(x, y, move_direction);
14418     GfxAction[x][y] = ACTION_PUSHING;
14419
14420     if (mode == DF_SNAP)
14421       ContinueMoving(x, y);
14422     else
14423       MovPos[x][y] = (dx != 0 ? dx : dy);
14424
14425     Pushed[x][y] = TRUE;
14426     Pushed[nextx][nexty] = TRUE;
14427
14428     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14429       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14430     else
14431       player->push_delay_value = -1;    // get new value later
14432
14433     // check for element change _after_ element has been pushed
14434     if (game.use_change_when_pushing_bug)
14435     {
14436       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14437                                  player->index_bit, dig_side);
14438       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14439                                           player->index_bit, dig_side);
14440     }
14441   }
14442   else if (IS_SWITCHABLE(element))
14443   {
14444     if (PLAYER_SWITCHING(player, x, y))
14445     {
14446       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14447                                           player->index_bit, dig_side);
14448
14449       return MP_ACTION;
14450     }
14451
14452     player->is_switching = TRUE;
14453     player->switch_x = x;
14454     player->switch_y = y;
14455
14456     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14457
14458     if (element == EL_ROBOT_WHEEL)
14459     {
14460       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14461
14462       game.robot_wheel_x = x;
14463       game.robot_wheel_y = y;
14464       game.robot_wheel_active = TRUE;
14465
14466       TEST_DrawLevelField(x, y);
14467     }
14468     else if (element == EL_SP_TERMINAL)
14469     {
14470       int xx, yy;
14471
14472       SCAN_PLAYFIELD(xx, yy)
14473       {
14474         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14475         {
14476           Bang(xx, yy);
14477         }
14478         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14479         {
14480           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14481
14482           ResetGfxAnimation(xx, yy);
14483           TEST_DrawLevelField(xx, yy);
14484         }
14485       }
14486     }
14487     else if (IS_BELT_SWITCH(element))
14488     {
14489       ToggleBeltSwitch(x, y);
14490     }
14491     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14492              element == EL_SWITCHGATE_SWITCH_DOWN ||
14493              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14494              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14495     {
14496       ToggleSwitchgateSwitch(x, y);
14497     }
14498     else if (element == EL_LIGHT_SWITCH ||
14499              element == EL_LIGHT_SWITCH_ACTIVE)
14500     {
14501       ToggleLightSwitch(x, y);
14502     }
14503     else if (element == EL_TIMEGATE_SWITCH ||
14504              element == EL_DC_TIMEGATE_SWITCH)
14505     {
14506       ActivateTimegateSwitch(x, y);
14507     }
14508     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14509              element == EL_BALLOON_SWITCH_RIGHT ||
14510              element == EL_BALLOON_SWITCH_UP    ||
14511              element == EL_BALLOON_SWITCH_DOWN  ||
14512              element == EL_BALLOON_SWITCH_NONE  ||
14513              element == EL_BALLOON_SWITCH_ANY)
14514     {
14515       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14516                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14517                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14518                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14519                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14520                              move_direction);
14521     }
14522     else if (element == EL_LAMP)
14523     {
14524       Tile[x][y] = EL_LAMP_ACTIVE;
14525       game.lights_still_needed--;
14526
14527       ResetGfxAnimation(x, y);
14528       TEST_DrawLevelField(x, y);
14529     }
14530     else if (element == EL_TIME_ORB_FULL)
14531     {
14532       Tile[x][y] = EL_TIME_ORB_EMPTY;
14533
14534       if (level.time > 0 || level.use_time_orb_bug)
14535       {
14536         TimeLeft += level.time_orb_time;
14537         game.no_time_limit = FALSE;
14538
14539         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14540
14541         DisplayGameControlValues();
14542       }
14543
14544       ResetGfxAnimation(x, y);
14545       TEST_DrawLevelField(x, y);
14546     }
14547     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14548              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14549     {
14550       int xx, yy;
14551
14552       game.ball_active = !game.ball_active;
14553
14554       SCAN_PLAYFIELD(xx, yy)
14555       {
14556         int e = Tile[xx][yy];
14557
14558         if (game.ball_active)
14559         {
14560           if (e == EL_EMC_MAGIC_BALL)
14561             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14562           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14563             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14564         }
14565         else
14566         {
14567           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14568             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14569           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14570             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14571         }
14572       }
14573     }
14574
14575     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14576                                         player->index_bit, dig_side);
14577
14578     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14579                                         player->index_bit, dig_side);
14580
14581     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14582                                         player->index_bit, dig_side);
14583
14584     return MP_ACTION;
14585   }
14586   else
14587   {
14588     if (!PLAYER_SWITCHING(player, x, y))
14589     {
14590       player->is_switching = TRUE;
14591       player->switch_x = x;
14592       player->switch_y = y;
14593
14594       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14595                                  player->index_bit, dig_side);
14596       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14597                                           player->index_bit, dig_side);
14598
14599       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14600                                  player->index_bit, dig_side);
14601       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14602                                           player->index_bit, dig_side);
14603     }
14604
14605     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14606                                player->index_bit, dig_side);
14607     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14608                                         player->index_bit, dig_side);
14609
14610     return MP_NO_ACTION;
14611   }
14612
14613   player->push_delay = -1;
14614
14615   if (is_player)                // function can also be called by EL_PENGUIN
14616   {
14617     if (Tile[x][y] != element)          // really digged/collected something
14618     {
14619       player->is_collecting = !player->is_digging;
14620       player->is_active = TRUE;
14621
14622       player->last_removed_element = element;
14623     }
14624   }
14625
14626   return MP_MOVING;
14627 }
14628
14629 static boolean DigFieldByCE(int x, int y, int digging_element)
14630 {
14631   int element = Tile[x][y];
14632
14633   if (!IS_FREE(x, y))
14634   {
14635     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14636                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14637                   ACTION_BREAKING);
14638
14639     // no element can dig solid indestructible elements
14640     if (IS_INDESTRUCTIBLE(element) &&
14641         !IS_DIGGABLE(element) &&
14642         !IS_COLLECTIBLE(element))
14643       return FALSE;
14644
14645     if (AmoebaNr[x][y] &&
14646         (element == EL_AMOEBA_FULL ||
14647          element == EL_BD_AMOEBA ||
14648          element == EL_AMOEBA_GROWING))
14649     {
14650       AmoebaCnt[AmoebaNr[x][y]]--;
14651       AmoebaCnt2[AmoebaNr[x][y]]--;
14652     }
14653
14654     if (IS_MOVING(x, y))
14655       RemoveMovingField(x, y);
14656     else
14657     {
14658       RemoveField(x, y);
14659       TEST_DrawLevelField(x, y);
14660     }
14661
14662     // if digged element was about to explode, prevent the explosion
14663     ExplodeField[x][y] = EX_TYPE_NONE;
14664
14665     PlayLevelSoundAction(x, y, action);
14666   }
14667
14668   Store[x][y] = EL_EMPTY;
14669
14670   // this makes it possible to leave the removed element again
14671   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14672     Store[x][y] = element;
14673
14674   return TRUE;
14675 }
14676
14677 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14678 {
14679   int jx = player->jx, jy = player->jy;
14680   int x = jx + dx, y = jy + dy;
14681   int snap_direction = (dx == -1 ? MV_LEFT  :
14682                         dx == +1 ? MV_RIGHT :
14683                         dy == -1 ? MV_UP    :
14684                         dy == +1 ? MV_DOWN  : MV_NONE);
14685   boolean can_continue_snapping = (level.continuous_snapping &&
14686                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14687
14688   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14689     return FALSE;
14690
14691   if (!player->active || !IN_LEV_FIELD(x, y))
14692     return FALSE;
14693
14694   if (dx && dy)
14695     return FALSE;
14696
14697   if (!dx && !dy)
14698   {
14699     if (player->MovPos == 0)
14700       player->is_pushing = FALSE;
14701
14702     player->is_snapping = FALSE;
14703
14704     if (player->MovPos == 0)
14705     {
14706       player->is_moving = FALSE;
14707       player->is_digging = FALSE;
14708       player->is_collecting = FALSE;
14709     }
14710
14711     return FALSE;
14712   }
14713
14714   // prevent snapping with already pressed snap key when not allowed
14715   if (player->is_snapping && !can_continue_snapping)
14716     return FALSE;
14717
14718   player->MovDir = snap_direction;
14719
14720   if (player->MovPos == 0)
14721   {
14722     player->is_moving = FALSE;
14723     player->is_digging = FALSE;
14724     player->is_collecting = FALSE;
14725   }
14726
14727   player->is_dropping = FALSE;
14728   player->is_dropping_pressed = FALSE;
14729   player->drop_pressed_delay = 0;
14730
14731   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14732     return FALSE;
14733
14734   player->is_snapping = TRUE;
14735   player->is_active = TRUE;
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   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14745     TEST_DrawLevelField(player->last_jx, player->last_jy);
14746
14747   TEST_DrawLevelField(x, y);
14748
14749   return TRUE;
14750 }
14751
14752 static boolean DropElement(struct PlayerInfo *player)
14753 {
14754   int old_element, new_element;
14755   int dropx = player->jx, dropy = player->jy;
14756   int drop_direction = player->MovDir;
14757   int drop_side = drop_direction;
14758   int drop_element = get_next_dropped_element(player);
14759
14760   /* do not drop an element on top of another element; when holding drop key
14761      pressed without moving, dropped element must move away before the next
14762      element can be dropped (this is especially important if the next element
14763      is dynamite, which can be placed on background for historical reasons) */
14764   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14765     return MP_ACTION;
14766
14767   if (IS_THROWABLE(drop_element))
14768   {
14769     dropx += GET_DX_FROM_DIR(drop_direction);
14770     dropy += GET_DY_FROM_DIR(drop_direction);
14771
14772     if (!IN_LEV_FIELD(dropx, dropy))
14773       return FALSE;
14774   }
14775
14776   old_element = Tile[dropx][dropy];     // old element at dropping position
14777   new_element = drop_element;           // default: no change when dropping
14778
14779   // check if player is active, not moving and ready to drop
14780   if (!player->active || player->MovPos || player->drop_delay > 0)
14781     return FALSE;
14782
14783   // check if player has anything that can be dropped
14784   if (new_element == EL_UNDEFINED)
14785     return FALSE;
14786
14787   // only set if player has anything that can be dropped
14788   player->is_dropping_pressed = TRUE;
14789
14790   // check if drop key was pressed long enough for EM style dynamite
14791   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14792     return FALSE;
14793
14794   // check if anything can be dropped at the current position
14795   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14796     return FALSE;
14797
14798   // collected custom elements can only be dropped on empty fields
14799   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14800     return FALSE;
14801
14802   if (old_element != EL_EMPTY)
14803     Back[dropx][dropy] = old_element;   // store old element on this field
14804
14805   ResetGfxAnimation(dropx, dropy);
14806   ResetRandomAnimationValue(dropx, dropy);
14807
14808   if (player->inventory_size > 0 ||
14809       player->inventory_infinite_element != EL_UNDEFINED)
14810   {
14811     if (player->inventory_size > 0)
14812     {
14813       player->inventory_size--;
14814
14815       DrawGameDoorValues();
14816
14817       if (new_element == EL_DYNAMITE)
14818         new_element = EL_DYNAMITE_ACTIVE;
14819       else if (new_element == EL_EM_DYNAMITE)
14820         new_element = EL_EM_DYNAMITE_ACTIVE;
14821       else if (new_element == EL_SP_DISK_RED)
14822         new_element = EL_SP_DISK_RED_ACTIVE;
14823     }
14824
14825     Tile[dropx][dropy] = new_element;
14826
14827     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14828       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14829                           el2img(Tile[dropx][dropy]), 0);
14830
14831     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14832
14833     // needed if previous element just changed to "empty" in the last frame
14834     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14835
14836     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14837                                player->index_bit, drop_side);
14838     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14839                                         CE_PLAYER_DROPS_X,
14840                                         player->index_bit, drop_side);
14841
14842     TestIfElementTouchesCustomElement(dropx, dropy);
14843   }
14844   else          // player is dropping a dyna bomb
14845   {
14846     player->dynabombs_left--;
14847
14848     Tile[dropx][dropy] = new_element;
14849
14850     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14851       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14852                           el2img(Tile[dropx][dropy]), 0);
14853
14854     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14855   }
14856
14857   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14858     InitField_WithBug1(dropx, dropy, FALSE);
14859
14860   new_element = Tile[dropx][dropy];     // element might have changed
14861
14862   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14863       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14864   {
14865     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14866       MovDir[dropx][dropy] = drop_direction;
14867
14868     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14869
14870     // do not cause impact style collision by dropping elements that can fall
14871     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14872   }
14873
14874   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14875   player->is_dropping = TRUE;
14876
14877   player->drop_pressed_delay = 0;
14878   player->is_dropping_pressed = FALSE;
14879
14880   player->drop_x = dropx;
14881   player->drop_y = dropy;
14882
14883   return TRUE;
14884 }
14885
14886 // ----------------------------------------------------------------------------
14887 // game sound playing functions
14888 // ----------------------------------------------------------------------------
14889
14890 static int *loop_sound_frame = NULL;
14891 static int *loop_sound_volume = NULL;
14892
14893 void InitPlayLevelSound(void)
14894 {
14895   int num_sounds = getSoundListSize();
14896
14897   checked_free(loop_sound_frame);
14898   checked_free(loop_sound_volume);
14899
14900   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14901   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14902 }
14903
14904 static void PlayLevelSound(int x, int y, int nr)
14905 {
14906   int sx = SCREENX(x), sy = SCREENY(y);
14907   int volume, stereo_position;
14908   int max_distance = 8;
14909   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14910
14911   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14912       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14913     return;
14914
14915   if (!IN_LEV_FIELD(x, y) ||
14916       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14917       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14918     return;
14919
14920   volume = SOUND_MAX_VOLUME;
14921
14922   if (!IN_SCR_FIELD(sx, sy))
14923   {
14924     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14925     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14926
14927     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14928   }
14929
14930   stereo_position = (SOUND_MAX_LEFT +
14931                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14932                      (SCR_FIELDX + 2 * max_distance));
14933
14934   if (IS_LOOP_SOUND(nr))
14935   {
14936     /* This assures that quieter loop sounds do not overwrite louder ones,
14937        while restarting sound volume comparison with each new game frame. */
14938
14939     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14940       return;
14941
14942     loop_sound_volume[nr] = volume;
14943     loop_sound_frame[nr] = FrameCounter;
14944   }
14945
14946   PlaySoundExt(nr, volume, stereo_position, type);
14947 }
14948
14949 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14950 {
14951   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14952                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14953                  y < LEVELY(BY1) ? LEVELY(BY1) :
14954                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14955                  sound_action);
14956 }
14957
14958 static void PlayLevelSoundAction(int x, int y, int action)
14959 {
14960   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14961 }
14962
14963 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14964 {
14965   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14966
14967   if (sound_effect != SND_UNDEFINED)
14968     PlayLevelSound(x, y, sound_effect);
14969 }
14970
14971 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14972                                               int action)
14973 {
14974   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14975
14976   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14977     PlayLevelSound(x, y, sound_effect);
14978 }
14979
14980 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14981 {
14982   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14983
14984   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14985     PlayLevelSound(x, y, sound_effect);
14986 }
14987
14988 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14989 {
14990   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14991
14992   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14993     StopSound(sound_effect);
14994 }
14995
14996 static int getLevelMusicNr(void)
14997 {
14998   if (levelset.music[level_nr] != MUS_UNDEFINED)
14999     return levelset.music[level_nr];            // from config file
15000   else
15001     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15002 }
15003
15004 static void FadeLevelSounds(void)
15005 {
15006   FadeSounds();
15007 }
15008
15009 static void FadeLevelMusic(void)
15010 {
15011   int music_nr = getLevelMusicNr();
15012   char *curr_music = getCurrentlyPlayingMusicFilename();
15013   char *next_music = getMusicInfoEntryFilename(music_nr);
15014
15015   if (!strEqual(curr_music, next_music))
15016     FadeMusic();
15017 }
15018
15019 void FadeLevelSoundsAndMusic(void)
15020 {
15021   FadeLevelSounds();
15022   FadeLevelMusic();
15023 }
15024
15025 static void PlayLevelMusic(void)
15026 {
15027   int music_nr = getLevelMusicNr();
15028   char *curr_music = getCurrentlyPlayingMusicFilename();
15029   char *next_music = getMusicInfoEntryFilename(music_nr);
15030
15031   if (!strEqual(curr_music, next_music))
15032     PlayMusicLoop(music_nr);
15033 }
15034
15035 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15036 {
15037   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15038   int offset = 0;
15039   int x = xx - offset;
15040   int y = yy - offset;
15041
15042   switch (sample)
15043   {
15044     case SOUND_blank:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15046       break;
15047
15048     case SOUND_roll:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15050       break;
15051
15052     case SOUND_stone:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15054       break;
15055
15056     case SOUND_nut:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15058       break;
15059
15060     case SOUND_crack:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15062       break;
15063
15064     case SOUND_bug:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15066       break;
15067
15068     case SOUND_tank:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15070       break;
15071
15072     case SOUND_android_clone:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15074       break;
15075
15076     case SOUND_android_move:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15078       break;
15079
15080     case SOUND_spring:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15082       break;
15083
15084     case SOUND_slurp:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15086       break;
15087
15088     case SOUND_eater:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15090       break;
15091
15092     case SOUND_eater_eat:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15094       break;
15095
15096     case SOUND_alien:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15098       break;
15099
15100     case SOUND_collect:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15102       break;
15103
15104     case SOUND_diamond:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15106       break;
15107
15108     case SOUND_squash:
15109       // !!! CHECK THIS !!!
15110 #if 1
15111       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15112 #else
15113       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15114 #endif
15115       break;
15116
15117     case SOUND_wonderfall:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15119       break;
15120
15121     case SOUND_drip:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15123       break;
15124
15125     case SOUND_push:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15127       break;
15128
15129     case SOUND_dirt:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15131       break;
15132
15133     case SOUND_acid:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15135       break;
15136
15137     case SOUND_ball:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15139       break;
15140
15141     case SOUND_slide:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15143       break;
15144
15145     case SOUND_wonder:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15147       break;
15148
15149     case SOUND_door:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15151       break;
15152
15153     case SOUND_exit_open:
15154       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15155       break;
15156
15157     case SOUND_exit_leave:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15159       break;
15160
15161     case SOUND_dynamite:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15163       break;
15164
15165     case SOUND_tick:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15167       break;
15168
15169     case SOUND_press:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15171       break;
15172
15173     case SOUND_wheel:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15175       break;
15176
15177     case SOUND_boom:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15179       break;
15180
15181     case SOUND_die:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15183       break;
15184
15185     case SOUND_time:
15186       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15187       break;
15188
15189     default:
15190       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15191       break;
15192   }
15193 }
15194
15195 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15196 {
15197   int element = map_element_SP_to_RND(element_sp);
15198   int action = map_action_SP_to_RND(action_sp);
15199   int offset = (setup.sp_show_border_elements ? 0 : 1);
15200   int x = xx - offset;
15201   int y = yy - offset;
15202
15203   PlayLevelSoundElementAction(x, y, element, action);
15204 }
15205
15206 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15207 {
15208   int element = map_element_MM_to_RND(element_mm);
15209   int action = map_action_MM_to_RND(action_mm);
15210   int offset = 0;
15211   int x = xx - offset;
15212   int y = yy - offset;
15213
15214   if (!IS_MM_ELEMENT(element))
15215     element = EL_MM_DEFAULT;
15216
15217   PlayLevelSoundElementAction(x, y, element, action);
15218 }
15219
15220 void PlaySound_MM(int sound_mm)
15221 {
15222   int sound = map_sound_MM_to_RND(sound_mm);
15223
15224   if (sound == SND_UNDEFINED)
15225     return;
15226
15227   PlaySound(sound);
15228 }
15229
15230 void PlaySoundLoop_MM(int sound_mm)
15231 {
15232   int sound = map_sound_MM_to_RND(sound_mm);
15233
15234   if (sound == SND_UNDEFINED)
15235     return;
15236
15237   PlaySoundLoop(sound);
15238 }
15239
15240 void StopSound_MM(int sound_mm)
15241 {
15242   int sound = map_sound_MM_to_RND(sound_mm);
15243
15244   if (sound == SND_UNDEFINED)
15245     return;
15246
15247   StopSound(sound);
15248 }
15249
15250 void RaiseScore(int value)
15251 {
15252   game.score += value;
15253
15254   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15255
15256   DisplayGameControlValues();
15257 }
15258
15259 void RaiseScoreElement(int element)
15260 {
15261   switch (element)
15262   {
15263     case EL_EMERALD:
15264     case EL_BD_DIAMOND:
15265     case EL_EMERALD_YELLOW:
15266     case EL_EMERALD_RED:
15267     case EL_EMERALD_PURPLE:
15268     case EL_SP_INFOTRON:
15269       RaiseScore(level.score[SC_EMERALD]);
15270       break;
15271     case EL_DIAMOND:
15272       RaiseScore(level.score[SC_DIAMOND]);
15273       break;
15274     case EL_CRYSTAL:
15275       RaiseScore(level.score[SC_CRYSTAL]);
15276       break;
15277     case EL_PEARL:
15278       RaiseScore(level.score[SC_PEARL]);
15279       break;
15280     case EL_BUG:
15281     case EL_BD_BUTTERFLY:
15282     case EL_SP_ELECTRON:
15283       RaiseScore(level.score[SC_BUG]);
15284       break;
15285     case EL_SPACESHIP:
15286     case EL_BD_FIREFLY:
15287     case EL_SP_SNIKSNAK:
15288       RaiseScore(level.score[SC_SPACESHIP]);
15289       break;
15290     case EL_YAMYAM:
15291     case EL_DARK_YAMYAM:
15292       RaiseScore(level.score[SC_YAMYAM]);
15293       break;
15294     case EL_ROBOT:
15295       RaiseScore(level.score[SC_ROBOT]);
15296       break;
15297     case EL_PACMAN:
15298       RaiseScore(level.score[SC_PACMAN]);
15299       break;
15300     case EL_NUT:
15301       RaiseScore(level.score[SC_NUT]);
15302       break;
15303     case EL_DYNAMITE:
15304     case EL_EM_DYNAMITE:
15305     case EL_SP_DISK_RED:
15306     case EL_DYNABOMB_INCREASE_NUMBER:
15307     case EL_DYNABOMB_INCREASE_SIZE:
15308     case EL_DYNABOMB_INCREASE_POWER:
15309       RaiseScore(level.score[SC_DYNAMITE]);
15310       break;
15311     case EL_SHIELD_NORMAL:
15312     case EL_SHIELD_DEADLY:
15313       RaiseScore(level.score[SC_SHIELD]);
15314       break;
15315     case EL_EXTRA_TIME:
15316       RaiseScore(level.extra_time_score);
15317       break;
15318     case EL_KEY_1:
15319     case EL_KEY_2:
15320     case EL_KEY_3:
15321     case EL_KEY_4:
15322     case EL_EM_KEY_1:
15323     case EL_EM_KEY_2:
15324     case EL_EM_KEY_3:
15325     case EL_EM_KEY_4:
15326     case EL_EMC_KEY_5:
15327     case EL_EMC_KEY_6:
15328     case EL_EMC_KEY_7:
15329     case EL_EMC_KEY_8:
15330     case EL_DC_KEY_WHITE:
15331       RaiseScore(level.score[SC_KEY]);
15332       break;
15333     default:
15334       RaiseScore(element_info[element].collect_score);
15335       break;
15336   }
15337 }
15338
15339 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15340 {
15341   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15342   {
15343     // closing door required in case of envelope style request dialogs
15344     if (!skip_request)
15345     {
15346       // prevent short reactivation of overlay buttons while closing door
15347       SetOverlayActive(FALSE);
15348
15349       CloseDoor(DOOR_CLOSE_1);
15350     }
15351
15352     if (network.enabled)
15353       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15354     else
15355     {
15356       if (quick_quit)
15357         FadeSkipNextFadeIn();
15358
15359       SetGameStatus(GAME_MODE_MAIN);
15360
15361       DrawMainMenu();
15362     }
15363   }
15364   else          // continue playing the game
15365   {
15366     if (tape.playing && tape.deactivate_display)
15367       TapeDeactivateDisplayOff(TRUE);
15368
15369     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15370
15371     if (tape.playing && tape.deactivate_display)
15372       TapeDeactivateDisplayOn();
15373   }
15374 }
15375
15376 void RequestQuitGame(boolean ask_if_really_quit)
15377 {
15378   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15379   boolean skip_request = game.all_players_gone || quick_quit;
15380
15381   RequestQuitGameExt(skip_request, quick_quit,
15382                      "Do you really want to quit the game?");
15383 }
15384
15385 void RequestRestartGame(char *message)
15386 {
15387   game.restart_game_message = NULL;
15388
15389   boolean has_started_game = hasStartedNetworkGame();
15390   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15391
15392   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15393   {
15394     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15395   }
15396   else
15397   {
15398     // needed in case of envelope request to close game panel
15399     CloseDoor(DOOR_CLOSE_1);
15400
15401     SetGameStatus(GAME_MODE_MAIN);
15402
15403     DrawMainMenu();
15404   }
15405 }
15406
15407 void CheckGameOver(void)
15408 {
15409   static boolean last_game_over = FALSE;
15410   static int game_over_delay = 0;
15411   int game_over_delay_value = 50;
15412   boolean game_over = checkGameFailed();
15413
15414   // do not handle game over if request dialog is already active
15415   if (game.request_active)
15416     return;
15417
15418   // do not ask to play again if game was never actually played
15419   if (!game.GamePlayed)
15420     return;
15421
15422   if (!game_over)
15423   {
15424     last_game_over = FALSE;
15425     game_over_delay = game_over_delay_value;
15426
15427     return;
15428   }
15429
15430   if (game_over_delay > 0)
15431   {
15432     game_over_delay--;
15433
15434     return;
15435   }
15436
15437   if (last_game_over != game_over)
15438     game.restart_game_message = (hasStartedNetworkGame() ?
15439                                  "Game over! Play it again?" :
15440                                  "Game over!");
15441
15442   last_game_over = game_over;
15443 }
15444
15445 boolean checkGameSolved(void)
15446 {
15447   // set for all game engines if level was solved
15448   return game.LevelSolved_GameEnd;
15449 }
15450
15451 boolean checkGameFailed(void)
15452 {
15453   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15454     return (game_em.game_over && !game_em.level_solved);
15455   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15456     return (game_sp.game_over && !game_sp.level_solved);
15457   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15458     return (game_mm.game_over && !game_mm.level_solved);
15459   else                          // GAME_ENGINE_TYPE_RND
15460     return (game.GameOver && !game.LevelSolved);
15461 }
15462
15463 boolean checkGameEnded(void)
15464 {
15465   return (checkGameSolved() || checkGameFailed());
15466 }
15467
15468
15469 // ----------------------------------------------------------------------------
15470 // random generator functions
15471 // ----------------------------------------------------------------------------
15472
15473 unsigned int InitEngineRandom_RND(int seed)
15474 {
15475   game.num_random_calls = 0;
15476
15477   return InitEngineRandom(seed);
15478 }
15479
15480 unsigned int RND(int max)
15481 {
15482   if (max > 0)
15483   {
15484     game.num_random_calls++;
15485
15486     return GetEngineRandom(max);
15487   }
15488
15489   return 0;
15490 }
15491
15492
15493 // ----------------------------------------------------------------------------
15494 // game engine snapshot handling functions
15495 // ----------------------------------------------------------------------------
15496
15497 struct EngineSnapshotInfo
15498 {
15499   // runtime values for custom element collect score
15500   int collect_score[NUM_CUSTOM_ELEMENTS];
15501
15502   // runtime values for group element choice position
15503   int choice_pos[NUM_GROUP_ELEMENTS];
15504
15505   // runtime values for belt position animations
15506   int belt_graphic[4][NUM_BELT_PARTS];
15507   int belt_anim_mode[4][NUM_BELT_PARTS];
15508 };
15509
15510 static struct EngineSnapshotInfo engine_snapshot_rnd;
15511 static char *snapshot_level_identifier = NULL;
15512 static int snapshot_level_nr = -1;
15513
15514 static void SaveEngineSnapshotValues_RND(void)
15515 {
15516   static int belt_base_active_element[4] =
15517   {
15518     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15519     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15520     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15521     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15522   };
15523   int i, j;
15524
15525   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15526   {
15527     int element = EL_CUSTOM_START + i;
15528
15529     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15530   }
15531
15532   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15533   {
15534     int element = EL_GROUP_START + i;
15535
15536     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15537   }
15538
15539   for (i = 0; i < 4; i++)
15540   {
15541     for (j = 0; j < NUM_BELT_PARTS; j++)
15542     {
15543       int element = belt_base_active_element[i] + j;
15544       int graphic = el2img(element);
15545       int anim_mode = graphic_info[graphic].anim_mode;
15546
15547       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15548       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15549     }
15550   }
15551 }
15552
15553 static void LoadEngineSnapshotValues_RND(void)
15554 {
15555   unsigned int num_random_calls = game.num_random_calls;
15556   int i, j;
15557
15558   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15559   {
15560     int element = EL_CUSTOM_START + i;
15561
15562     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15563   }
15564
15565   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15566   {
15567     int element = EL_GROUP_START + i;
15568
15569     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15570   }
15571
15572   for (i = 0; i < 4; i++)
15573   {
15574     for (j = 0; j < NUM_BELT_PARTS; j++)
15575     {
15576       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15577       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15578
15579       graphic_info[graphic].anim_mode = anim_mode;
15580     }
15581   }
15582
15583   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15584   {
15585     InitRND(tape.random_seed);
15586     for (i = 0; i < num_random_calls; i++)
15587       RND(1);
15588   }
15589
15590   if (game.num_random_calls != num_random_calls)
15591   {
15592     Error("number of random calls out of sync");
15593     Error("number of random calls should be %d", num_random_calls);
15594     Error("number of random calls is %d", game.num_random_calls);
15595
15596     Fail("this should not happen -- please debug");
15597   }
15598 }
15599
15600 void FreeEngineSnapshotSingle(void)
15601 {
15602   FreeSnapshotSingle();
15603
15604   setString(&snapshot_level_identifier, NULL);
15605   snapshot_level_nr = -1;
15606 }
15607
15608 void FreeEngineSnapshotList(void)
15609 {
15610   FreeSnapshotList();
15611 }
15612
15613 static ListNode *SaveEngineSnapshotBuffers(void)
15614 {
15615   ListNode *buffers = NULL;
15616
15617   // copy some special values to a structure better suited for the snapshot
15618
15619   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15620     SaveEngineSnapshotValues_RND();
15621   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15622     SaveEngineSnapshotValues_EM();
15623   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15624     SaveEngineSnapshotValues_SP(&buffers);
15625   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15626     SaveEngineSnapshotValues_MM(&buffers);
15627
15628   // save values stored in special snapshot structure
15629
15630   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15632   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15634   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15635     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15636   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15637     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15638
15639   // save further RND engine values
15640
15641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15644
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15650
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15654
15655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15656
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15659
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15678
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15681
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15685
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15688
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15694
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15697
15698 #if 0
15699   ListNode *node = engine_snapshot_list_rnd;
15700   int num_bytes = 0;
15701
15702   while (node != NULL)
15703   {
15704     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15705
15706     node = node->next;
15707   }
15708
15709   Debug("game:playing:SaveEngineSnapshotBuffers",
15710         "size of engine snapshot: %d bytes", num_bytes);
15711 #endif
15712
15713   return buffers;
15714 }
15715
15716 void SaveEngineSnapshotSingle(void)
15717 {
15718   ListNode *buffers = SaveEngineSnapshotBuffers();
15719
15720   // finally save all snapshot buffers to single snapshot
15721   SaveSnapshotSingle(buffers);
15722
15723   // save level identification information
15724   setString(&snapshot_level_identifier, leveldir_current->identifier);
15725   snapshot_level_nr = level_nr;
15726 }
15727
15728 boolean CheckSaveEngineSnapshotToList(void)
15729 {
15730   boolean save_snapshot =
15731     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15732      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15733       game.snapshot.changed_action) ||
15734      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15735       game.snapshot.collected_item));
15736
15737   game.snapshot.changed_action = FALSE;
15738   game.snapshot.collected_item = FALSE;
15739   game.snapshot.save_snapshot = save_snapshot;
15740
15741   return save_snapshot;
15742 }
15743
15744 void SaveEngineSnapshotToList(void)
15745 {
15746   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15747       tape.quick_resume)
15748     return;
15749
15750   ListNode *buffers = SaveEngineSnapshotBuffers();
15751
15752   // finally save all snapshot buffers to snapshot list
15753   SaveSnapshotToList(buffers);
15754 }
15755
15756 void SaveEngineSnapshotToListInitial(void)
15757 {
15758   FreeEngineSnapshotList();
15759
15760   SaveEngineSnapshotToList();
15761 }
15762
15763 static void LoadEngineSnapshotValues(void)
15764 {
15765   // restore special values from snapshot structure
15766
15767   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15768     LoadEngineSnapshotValues_RND();
15769   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15770     LoadEngineSnapshotValues_EM();
15771   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15772     LoadEngineSnapshotValues_SP();
15773   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15774     LoadEngineSnapshotValues_MM();
15775 }
15776
15777 void LoadEngineSnapshotSingle(void)
15778 {
15779   LoadSnapshotSingle();
15780
15781   LoadEngineSnapshotValues();
15782 }
15783
15784 static void LoadEngineSnapshot_Undo(int steps)
15785 {
15786   LoadSnapshotFromList_Older(steps);
15787
15788   LoadEngineSnapshotValues();
15789 }
15790
15791 static void LoadEngineSnapshot_Redo(int steps)
15792 {
15793   LoadSnapshotFromList_Newer(steps);
15794
15795   LoadEngineSnapshotValues();
15796 }
15797
15798 boolean CheckEngineSnapshotSingle(void)
15799 {
15800   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15801           snapshot_level_nr == level_nr);
15802 }
15803
15804 boolean CheckEngineSnapshotList(void)
15805 {
15806   return CheckSnapshotList();
15807 }
15808
15809
15810 // ---------- new game button stuff -------------------------------------------
15811
15812 static struct
15813 {
15814   int graphic;
15815   struct XY *pos;
15816   int gadget_id;
15817   boolean *setup_value;
15818   boolean allowed_on_tape;
15819   boolean is_touch_button;
15820   char *infotext;
15821 } gamebutton_info[NUM_GAME_BUTTONS] =
15822 {
15823   {
15824     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15825     GAME_CTRL_ID_STOP,                          NULL,
15826     TRUE, FALSE,                                "stop game"
15827   },
15828   {
15829     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15830     GAME_CTRL_ID_PAUSE,                         NULL,
15831     TRUE, FALSE,                                "pause game"
15832   },
15833   {
15834     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15835     GAME_CTRL_ID_PLAY,                          NULL,
15836     TRUE, FALSE,                                "play game"
15837   },
15838   {
15839     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15840     GAME_CTRL_ID_UNDO,                          NULL,
15841     TRUE, FALSE,                                "undo step"
15842   },
15843   {
15844     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15845     GAME_CTRL_ID_REDO,                          NULL,
15846     TRUE, FALSE,                                "redo step"
15847   },
15848   {
15849     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15850     GAME_CTRL_ID_SAVE,                          NULL,
15851     TRUE, FALSE,                                "save game"
15852   },
15853   {
15854     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15855     GAME_CTRL_ID_PAUSE2,                        NULL,
15856     TRUE, FALSE,                                "pause game"
15857   },
15858   {
15859     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15860     GAME_CTRL_ID_LOAD,                          NULL,
15861     TRUE, FALSE,                                "load game"
15862   },
15863   {
15864     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15865     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15866     FALSE, FALSE,                               "stop game"
15867   },
15868   {
15869     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15870     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15871     FALSE, FALSE,                               "pause game"
15872   },
15873   {
15874     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15875     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15876     FALSE, FALSE,                               "play game"
15877   },
15878   {
15879     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15880     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15881     FALSE, TRUE,                                "stop game"
15882   },
15883   {
15884     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15885     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15886     FALSE, TRUE,                                "pause game"
15887   },
15888   {
15889     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15890     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15891     TRUE, FALSE,                                "background music on/off"
15892   },
15893   {
15894     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15895     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15896     TRUE, FALSE,                                "sound loops on/off"
15897   },
15898   {
15899     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15900     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15901     TRUE, FALSE,                                "normal sounds on/off"
15902   },
15903   {
15904     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15905     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15906     FALSE, FALSE,                               "background music on/off"
15907   },
15908   {
15909     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15910     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15911     FALSE, FALSE,                               "sound loops on/off"
15912   },
15913   {
15914     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15915     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15916     FALSE, FALSE,                               "normal sounds on/off"
15917   }
15918 };
15919
15920 void CreateGameButtons(void)
15921 {
15922   int i;
15923
15924   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15925   {
15926     int graphic = gamebutton_info[i].graphic;
15927     struct GraphicInfo *gfx = &graphic_info[graphic];
15928     struct XY *pos = gamebutton_info[i].pos;
15929     struct GadgetInfo *gi;
15930     int button_type;
15931     boolean checked;
15932     unsigned int event_mask;
15933     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15934     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15935     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15936     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15937     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15938     int gd_x   = gfx->src_x;
15939     int gd_y   = gfx->src_y;
15940     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15941     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15942     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15943     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15944     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15945     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15946     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15947     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15948     int id = i;
15949
15950     if (gfx->bitmap == NULL)
15951     {
15952       game_gadget[id] = NULL;
15953
15954       continue;
15955     }
15956
15957     if (id == GAME_CTRL_ID_STOP ||
15958         id == GAME_CTRL_ID_PANEL_STOP ||
15959         id == GAME_CTRL_ID_TOUCH_STOP ||
15960         id == GAME_CTRL_ID_PLAY ||
15961         id == GAME_CTRL_ID_PANEL_PLAY ||
15962         id == GAME_CTRL_ID_SAVE ||
15963         id == GAME_CTRL_ID_LOAD)
15964     {
15965       button_type = GD_TYPE_NORMAL_BUTTON;
15966       checked = FALSE;
15967       event_mask = GD_EVENT_RELEASED;
15968     }
15969     else if (id == GAME_CTRL_ID_UNDO ||
15970              id == GAME_CTRL_ID_REDO)
15971     {
15972       button_type = GD_TYPE_NORMAL_BUTTON;
15973       checked = FALSE;
15974       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15975     }
15976     else
15977     {
15978       button_type = GD_TYPE_CHECK_BUTTON;
15979       checked = (gamebutton_info[i].setup_value != NULL ?
15980                  *gamebutton_info[i].setup_value : FALSE);
15981       event_mask = GD_EVENT_PRESSED;
15982     }
15983
15984     gi = CreateGadget(GDI_CUSTOM_ID, id,
15985                       GDI_IMAGE_ID, graphic,
15986                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15987                       GDI_X, base_x + x,
15988                       GDI_Y, base_y + y,
15989                       GDI_WIDTH, gfx->width,
15990                       GDI_HEIGHT, gfx->height,
15991                       GDI_TYPE, button_type,
15992                       GDI_STATE, GD_BUTTON_UNPRESSED,
15993                       GDI_CHECKED, checked,
15994                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15995                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15996                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15997                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15998                       GDI_DIRECT_DRAW, FALSE,
15999                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16000                       GDI_EVENT_MASK, event_mask,
16001                       GDI_CALLBACK_ACTION, HandleGameButtons,
16002                       GDI_END);
16003
16004     if (gi == NULL)
16005       Fail("cannot create gadget");
16006
16007     game_gadget[id] = gi;
16008   }
16009 }
16010
16011 void FreeGameButtons(void)
16012 {
16013   int i;
16014
16015   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16016     FreeGadget(game_gadget[i]);
16017 }
16018
16019 static void UnmapGameButtonsAtSamePosition(int id)
16020 {
16021   int i;
16022
16023   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16024     if (i != id &&
16025         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16026         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16027       UnmapGadget(game_gadget[i]);
16028 }
16029
16030 static void UnmapGameButtonsAtSamePosition_All(void)
16031 {
16032   if (setup.show_snapshot_buttons)
16033   {
16034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16037   }
16038   else
16039   {
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16043
16044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16047   }
16048 }
16049
16050 static void MapGameButtonsAtSamePosition(int id)
16051 {
16052   int i;
16053
16054   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16055     if (i != id &&
16056         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16057         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16058       MapGadget(game_gadget[i]);
16059
16060   UnmapGameButtonsAtSamePosition_All();
16061 }
16062
16063 void MapUndoRedoButtons(void)
16064 {
16065   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16066   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16067
16068   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16069   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16070 }
16071
16072 void UnmapUndoRedoButtons(void)
16073 {
16074   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16075   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16076
16077   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16078   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16079 }
16080
16081 void ModifyPauseButtons(void)
16082 {
16083   static int ids[] =
16084   {
16085     GAME_CTRL_ID_PAUSE,
16086     GAME_CTRL_ID_PAUSE2,
16087     GAME_CTRL_ID_PANEL_PAUSE,
16088     GAME_CTRL_ID_TOUCH_PAUSE,
16089     -1
16090   };
16091   int i;
16092
16093   for (i = 0; ids[i] > -1; i++)
16094     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16095 }
16096
16097 static void MapGameButtonsExt(boolean on_tape)
16098 {
16099   int i;
16100
16101   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16102     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16103         i != GAME_CTRL_ID_UNDO &&
16104         i != GAME_CTRL_ID_REDO)
16105       MapGadget(game_gadget[i]);
16106
16107   UnmapGameButtonsAtSamePosition_All();
16108
16109   RedrawGameButtons();
16110 }
16111
16112 static void UnmapGameButtonsExt(boolean on_tape)
16113 {
16114   int i;
16115
16116   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16117     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16118       UnmapGadget(game_gadget[i]);
16119 }
16120
16121 static void RedrawGameButtonsExt(boolean on_tape)
16122 {
16123   int i;
16124
16125   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16126     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16127       RedrawGadget(game_gadget[i]);
16128 }
16129
16130 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16131 {
16132   if (gi == NULL)
16133     return;
16134
16135   gi->checked = state;
16136 }
16137
16138 static void RedrawSoundButtonGadget(int id)
16139 {
16140   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16141              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16142              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16143              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16144              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16145              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16146              id);
16147
16148   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16149   RedrawGadget(game_gadget[id2]);
16150 }
16151
16152 void MapGameButtons(void)
16153 {
16154   MapGameButtonsExt(FALSE);
16155 }
16156
16157 void UnmapGameButtons(void)
16158 {
16159   UnmapGameButtonsExt(FALSE);
16160 }
16161
16162 void RedrawGameButtons(void)
16163 {
16164   RedrawGameButtonsExt(FALSE);
16165 }
16166
16167 void MapGameButtonsOnTape(void)
16168 {
16169   MapGameButtonsExt(TRUE);
16170 }
16171
16172 void UnmapGameButtonsOnTape(void)
16173 {
16174   UnmapGameButtonsExt(TRUE);
16175 }
16176
16177 void RedrawGameButtonsOnTape(void)
16178 {
16179   RedrawGameButtonsExt(TRUE);
16180 }
16181
16182 static void GameUndoRedoExt(void)
16183 {
16184   ClearPlayerAction();
16185
16186   tape.pausing = TRUE;
16187
16188   RedrawPlayfield();
16189   UpdateAndDisplayGameControlValues();
16190
16191   DrawCompleteVideoDisplay();
16192   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16193   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16194   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16195
16196   BackToFront();
16197 }
16198
16199 static void GameUndo(int steps)
16200 {
16201   if (!CheckEngineSnapshotList())
16202     return;
16203
16204   LoadEngineSnapshot_Undo(steps);
16205
16206   GameUndoRedoExt();
16207 }
16208
16209 static void GameRedo(int steps)
16210 {
16211   if (!CheckEngineSnapshotList())
16212     return;
16213
16214   LoadEngineSnapshot_Redo(steps);
16215
16216   GameUndoRedoExt();
16217 }
16218
16219 static void HandleGameButtonsExt(int id, int button)
16220 {
16221   static boolean game_undo_executed = FALSE;
16222   int steps = BUTTON_STEPSIZE(button);
16223   boolean handle_game_buttons =
16224     (game_status == GAME_MODE_PLAYING ||
16225      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16226
16227   if (!handle_game_buttons)
16228     return;
16229
16230   switch (id)
16231   {
16232     case GAME_CTRL_ID_STOP:
16233     case GAME_CTRL_ID_PANEL_STOP:
16234     case GAME_CTRL_ID_TOUCH_STOP:
16235       if (game_status == GAME_MODE_MAIN)
16236         break;
16237
16238       if (tape.playing)
16239         TapeStop();
16240       else
16241         RequestQuitGame(TRUE);
16242
16243       break;
16244
16245     case GAME_CTRL_ID_PAUSE:
16246     case GAME_CTRL_ID_PAUSE2:
16247     case GAME_CTRL_ID_PANEL_PAUSE:
16248     case GAME_CTRL_ID_TOUCH_PAUSE:
16249       if (network.enabled && game_status == GAME_MODE_PLAYING)
16250       {
16251         if (tape.pausing)
16252           SendToServer_ContinuePlaying();
16253         else
16254           SendToServer_PausePlaying();
16255       }
16256       else
16257         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16258
16259       game_undo_executed = FALSE;
16260
16261       break;
16262
16263     case GAME_CTRL_ID_PLAY:
16264     case GAME_CTRL_ID_PANEL_PLAY:
16265       if (game_status == GAME_MODE_MAIN)
16266       {
16267         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16268       }
16269       else if (tape.pausing)
16270       {
16271         if (network.enabled)
16272           SendToServer_ContinuePlaying();
16273         else
16274           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16275       }
16276       break;
16277
16278     case GAME_CTRL_ID_UNDO:
16279       // Important: When using "save snapshot when collecting an item" mode,
16280       // load last (current) snapshot for first "undo" after pressing "pause"
16281       // (else the last-but-one snapshot would be loaded, because the snapshot
16282       // pointer already points to the last snapshot when pressing "pause",
16283       // which is fine for "every step/move" mode, but not for "every collect")
16284       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16285           !game_undo_executed)
16286         steps--;
16287
16288       game_undo_executed = TRUE;
16289
16290       GameUndo(steps);
16291       break;
16292
16293     case GAME_CTRL_ID_REDO:
16294       GameRedo(steps);
16295       break;
16296
16297     case GAME_CTRL_ID_SAVE:
16298       TapeQuickSave();
16299       break;
16300
16301     case GAME_CTRL_ID_LOAD:
16302       TapeQuickLoad();
16303       break;
16304
16305     case SOUND_CTRL_ID_MUSIC:
16306     case SOUND_CTRL_ID_PANEL_MUSIC:
16307       if (setup.sound_music)
16308       { 
16309         setup.sound_music = FALSE;
16310
16311         FadeMusic();
16312       }
16313       else if (audio.music_available)
16314       { 
16315         setup.sound = setup.sound_music = TRUE;
16316
16317         SetAudioMode(setup.sound);
16318
16319         if (game_status == GAME_MODE_PLAYING)
16320           PlayLevelMusic();
16321       }
16322
16323       RedrawSoundButtonGadget(id);
16324
16325       break;
16326
16327     case SOUND_CTRL_ID_LOOPS:
16328     case SOUND_CTRL_ID_PANEL_LOOPS:
16329       if (setup.sound_loops)
16330         setup.sound_loops = FALSE;
16331       else if (audio.loops_available)
16332       {
16333         setup.sound = setup.sound_loops = TRUE;
16334
16335         SetAudioMode(setup.sound);
16336       }
16337
16338       RedrawSoundButtonGadget(id);
16339
16340       break;
16341
16342     case SOUND_CTRL_ID_SIMPLE:
16343     case SOUND_CTRL_ID_PANEL_SIMPLE:
16344       if (setup.sound_simple)
16345         setup.sound_simple = FALSE;
16346       else if (audio.sound_available)
16347       {
16348         setup.sound = setup.sound_simple = TRUE;
16349
16350         SetAudioMode(setup.sound);
16351       }
16352
16353       RedrawSoundButtonGadget(id);
16354
16355       break;
16356
16357     default:
16358       break;
16359   }
16360 }
16361
16362 static void HandleGameButtons(struct GadgetInfo *gi)
16363 {
16364   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16365 }
16366
16367 void HandleSoundButtonKeys(Key key)
16368 {
16369   if (key == setup.shortcut.sound_simple)
16370     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16371   else if (key == setup.shortcut.sound_loops)
16372     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16373   else if (key == setup.shortcut.sound_music)
16374     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16375 }