removed duplicate code
[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 struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254
2255   UpdatePlayfieldElementCount();
2256
2257   // update game panel control values
2258
2259   // used instead of "level_nr" (for network games)
2260   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2261   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262
2263   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2264   for (i = 0; i < MAX_NUM_KEYS; i++)
2265     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268
2269   if (game.centered_player_nr == -1)
2270   {
2271     for (i = 0; i < MAX_PLAYERS; i++)
2272     {
2273       // only one player in Supaplex game engine
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2275         break;
2276
2277       for (k = 0; k < MAX_NUM_KEYS; k++)
2278       {
2279         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280         {
2281           if (game_em.ply[i]->keys & (1 << k))
2282             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283               get_key_element_from_nr(k);
2284         }
2285         else if (stored_player[i].key[k])
2286           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2287             get_key_element_from_nr(k);
2288       }
2289
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         getPlayerInventorySize(i);
2292
2293       if (stored_player[i].num_white_keys > 0)
2294         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295           EL_DC_KEY_WHITE;
2296
2297       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298         stored_player[i].num_white_keys;
2299     }
2300   }
2301   else
2302   {
2303     int player_nr = game.centered_player_nr;
2304
2305     for (k = 0; k < MAX_NUM_KEYS; k++)
2306     {
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308       {
2309         if (game_em.ply[player_nr]->keys & (1 << k))
2310           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2311             get_key_element_from_nr(k);
2312       }
2313       else if (stored_player[player_nr].key[k])
2314         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315           get_key_element_from_nr(k);
2316     }
2317
2318     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2319       getPlayerInventorySize(player_nr);
2320
2321     if (stored_player[player_nr].num_white_keys > 0)
2322       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323
2324     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2325       stored_player[player_nr].num_white_keys;
2326   }
2327
2328   // try to display as many collected keys as possible in the default game panel
2329   for (i = STD_NUM_KEYS; i < MAX_NUM_KEYS + 1; i++)     // EMC keys + white key
2330   {
2331     int nr = GAME_PANEL_KEY_1 + i;
2332     int emc_key = get_key_element_from_nr(i);
2333     int element = (i < MAX_NUM_KEYS ? emc_key : EL_DC_KEY_WHITE);
2334     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2335     struct TextPosInfo *pos = gpc->pos;
2336
2337     // check if panel position is undefined for a certain EMC key or white key
2338     if (gpc->value != EL_EMPTY && pos->x == -1 && pos->y == -1)
2339     {
2340       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2341
2342       // 1st try: display key at the same position as normal or EM keys
2343       if (game_panel_controls[nr_new].value == EL_EMPTY)
2344       {
2345         game_panel_controls[nr_new].value = element;
2346       }
2347       else
2348       {
2349         // 2nd try: display key at the next free position in the key panel
2350         for (k = 0; k < STD_NUM_KEYS; k++)
2351         {
2352           nr_new = GAME_PANEL_KEY_1 + k;
2353
2354           if (game_panel_controls[nr_new].value == EL_EMPTY)
2355           {
2356             game_panel_controls[nr_new].value = element;
2357
2358             break;
2359           }
2360         }
2361       }
2362     }
2363   }
2364
2365   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2366   {
2367     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2368       get_inventory_element_from_pos(local_player, i);
2369     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2370       get_inventory_element_from_pos(local_player, -i - 1);
2371   }
2372
2373   game_panel_controls[GAME_PANEL_SCORE].value = score;
2374   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2375
2376   game_panel_controls[GAME_PANEL_TIME].value = time;
2377
2378   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2379   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2380   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2381
2382   if (level.time == 0)
2383     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2384   else
2385     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2386
2387   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2388   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2389
2390   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2391
2392   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2393     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2394      EL_EMPTY);
2395   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2396     local_player->shield_normal_time_left;
2397   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2398     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2399      EL_EMPTY);
2400   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2401     local_player->shield_deadly_time_left;
2402
2403   game_panel_controls[GAME_PANEL_EXIT].value =
2404     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2405
2406   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2407     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2408   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2409     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2410      EL_EMC_MAGIC_BALL_SWITCH);
2411
2412   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2413     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2414   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2415     game.light_time_left;
2416
2417   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2418     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2419   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2420     game.timegate_time_left;
2421
2422   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2423     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2424
2425   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2426     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2427   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2428     game.lenses_time_left;
2429
2430   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2431     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2432   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2433     game.magnify_time_left;
2434
2435   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2436     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2437      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2438      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2439      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2440      EL_BALLOON_SWITCH_NONE);
2441
2442   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2443     local_player->dynabomb_count;
2444   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2445     local_player->dynabomb_size;
2446   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2447     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2448
2449   game_panel_controls[GAME_PANEL_PENGUINS].value =
2450     game.friends_still_needed;
2451
2452   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2453     game.sokoban_objects_still_needed;
2454   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2455     game.sokoban_fields_still_needed;
2456
2457   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2458     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2459
2460   for (i = 0; i < NUM_BELTS; i++)
2461   {
2462     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2463       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2464        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2465     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2466       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2467   }
2468
2469   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2470     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2471   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2472     game.magic_wall_time_left;
2473
2474   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2475     local_player->gravity;
2476
2477   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2478     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2479
2480   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2481     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2482       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2483        game.panel.element[i].id : EL_UNDEFINED);
2484
2485   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2486     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2487       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2488        element_info[game.panel.element_count[i].id].element_count : 0);
2489
2490   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2491     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2492       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2493        element_info[game.panel.ce_score[i].id].collect_score : 0);
2494
2495   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2496     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2497       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2498        element_info[game.panel.ce_score_element[i].id].collect_score :
2499        EL_UNDEFINED);
2500
2501   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2502   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2503   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2504
2505   // update game panel control frames
2506
2507   for (i = 0; game_panel_controls[i].nr != -1; i++)
2508   {
2509     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2510
2511     if (gpc->type == TYPE_ELEMENT)
2512     {
2513       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2514       {
2515         int last_anim_random_frame = gfx.anim_random_frame;
2516         int element = gpc->value;
2517         int graphic = el2panelimg(element);
2518
2519         if (gpc->value != gpc->last_value)
2520         {
2521           gpc->gfx_frame = 0;
2522           gpc->gfx_random = INIT_GFX_RANDOM();
2523         }
2524         else
2525         {
2526           gpc->gfx_frame++;
2527
2528           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2529               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2530             gpc->gfx_random = INIT_GFX_RANDOM();
2531         }
2532
2533         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2534           gfx.anim_random_frame = gpc->gfx_random;
2535
2536         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2537           gpc->gfx_frame = element_info[element].collect_score;
2538
2539         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2540
2541         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2542           gfx.anim_random_frame = last_anim_random_frame;
2543       }
2544     }
2545     else if (gpc->type == TYPE_GRAPHIC)
2546     {
2547       if (gpc->graphic != IMG_UNDEFINED)
2548       {
2549         int last_anim_random_frame = gfx.anim_random_frame;
2550         int graphic = gpc->graphic;
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = INIT_GFX_RANDOM();
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = INIT_GFX_RANDOM();
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2570
2571         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2572           gfx.anim_random_frame = last_anim_random_frame;
2573       }
2574     }
2575   }
2576 }
2577
2578 static void DisplayGameControlValues(void)
2579 {
2580   boolean redraw_panel = FALSE;
2581   int i;
2582
2583   for (i = 0; game_panel_controls[i].nr != -1; i++)
2584   {
2585     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2586
2587     if (PANEL_DEACTIVATED(gpc->pos))
2588       continue;
2589
2590     if (gpc->value == gpc->last_value &&
2591         gpc->frame == gpc->last_frame)
2592       continue;
2593
2594     redraw_panel = TRUE;
2595   }
2596
2597   if (!redraw_panel)
2598     return;
2599
2600   // copy default game door content to main double buffer
2601
2602   // !!! CHECK AGAIN !!!
2603   SetPanelBackground();
2604   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2605   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2606
2607   // redraw game control buttons
2608   RedrawGameButtons();
2609
2610   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2611
2612   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2613   {
2614     int nr = game_panel_order[i].nr;
2615     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2616     struct TextPosInfo *pos = gpc->pos;
2617     int type = gpc->type;
2618     int value = gpc->value;
2619     int frame = gpc->frame;
2620     int size = pos->size;
2621     int font = pos->font;
2622     boolean draw_masked = pos->draw_masked;
2623     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2624
2625     if (PANEL_DEACTIVATED(pos))
2626       continue;
2627
2628     if (pos->class == get_hash_from_key("extra_panel_items") &&
2629         !setup.prefer_extra_panel_items)
2630       continue;
2631
2632     gpc->last_value = value;
2633     gpc->last_frame = frame;
2634
2635     if (type == TYPE_INTEGER)
2636     {
2637       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2638           nr == GAME_PANEL_TIME)
2639       {
2640         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2641
2642         if (use_dynamic_size)           // use dynamic number of digits
2643         {
2644           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2645           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2646           int size2 = size1 + 1;
2647           int font1 = pos->font;
2648           int font2 = pos->font_alt;
2649
2650           size = (value < value_change ? size1 : size2);
2651           font = (value < value_change ? font1 : font2);
2652         }
2653       }
2654
2655       // correct text size if "digits" is zero or less
2656       if (size <= 0)
2657         size = strlen(int2str(value, size));
2658
2659       // dynamically correct text alignment
2660       pos->width = size * getFontWidth(font);
2661
2662       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2663                   int2str(value, size), font, mask_mode);
2664     }
2665     else if (type == TYPE_ELEMENT)
2666     {
2667       int element, graphic;
2668       Bitmap *src_bitmap;
2669       int src_x, src_y;
2670       int width, height;
2671       int dst_x = PANEL_XPOS(pos);
2672       int dst_y = PANEL_YPOS(pos);
2673
2674       if (value != EL_UNDEFINED && value != EL_EMPTY)
2675       {
2676         element = value;
2677         graphic = el2panelimg(value);
2678
2679 #if 0
2680         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2681               element, EL_NAME(element), size);
2682 #endif
2683
2684         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2685           size = TILESIZE;
2686
2687         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2688                               &src_x, &src_y);
2689
2690         width  = graphic_info[graphic].width  * size / TILESIZE;
2691         height = graphic_info[graphic].height * size / TILESIZE;
2692
2693         if (draw_masked)
2694           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2695                            dst_x, dst_y);
2696         else
2697           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2698                      dst_x, dst_y);
2699       }
2700     }
2701     else if (type == TYPE_GRAPHIC)
2702     {
2703       int graphic        = gpc->graphic;
2704       int graphic_active = gpc->graphic_active;
2705       Bitmap *src_bitmap;
2706       int src_x, src_y;
2707       int width, height;
2708       int dst_x = PANEL_XPOS(pos);
2709       int dst_y = PANEL_YPOS(pos);
2710       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2711                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2712
2713       if (graphic != IMG_UNDEFINED && !skip)
2714       {
2715         if (pos->style == STYLE_REVERSE)
2716           value = 100 - value;
2717
2718         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2719
2720         if (pos->direction & MV_HORIZONTAL)
2721         {
2722           width  = graphic_info[graphic_active].width * value / 100;
2723           height = graphic_info[graphic_active].height;
2724
2725           if (pos->direction == MV_LEFT)
2726           {
2727             src_x += graphic_info[graphic_active].width - width;
2728             dst_x += graphic_info[graphic_active].width - width;
2729           }
2730         }
2731         else
2732         {
2733           width  = graphic_info[graphic_active].width;
2734           height = graphic_info[graphic_active].height * value / 100;
2735
2736           if (pos->direction == MV_UP)
2737           {
2738             src_y += graphic_info[graphic_active].height - height;
2739             dst_y += graphic_info[graphic_active].height - height;
2740           }
2741         }
2742
2743         if (draw_masked)
2744           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2745                            dst_x, dst_y);
2746         else
2747           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2748                      dst_x, dst_y);
2749
2750         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2751
2752         if (pos->direction & MV_HORIZONTAL)
2753         {
2754           if (pos->direction == MV_RIGHT)
2755           {
2756             src_x += width;
2757             dst_x += width;
2758           }
2759           else
2760           {
2761             dst_x = PANEL_XPOS(pos);
2762           }
2763
2764           width = graphic_info[graphic].width - width;
2765         }
2766         else
2767         {
2768           if (pos->direction == MV_DOWN)
2769           {
2770             src_y += height;
2771             dst_y += height;
2772           }
2773           else
2774           {
2775             dst_y = PANEL_YPOS(pos);
2776           }
2777
2778           height = graphic_info[graphic].height - height;
2779         }
2780
2781         if (draw_masked)
2782           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2783                            dst_x, dst_y);
2784         else
2785           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2786                      dst_x, dst_y);
2787       }
2788     }
2789     else if (type == TYPE_STRING)
2790     {
2791       boolean active = (value != 0);
2792       char *state_normal = "off";
2793       char *state_active = "on";
2794       char *state = (active ? state_active : state_normal);
2795       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2796                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2797                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2798                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2799
2800       if (nr == GAME_PANEL_GRAVITY_STATE)
2801       {
2802         int font1 = pos->font;          // (used for normal state)
2803         int font2 = pos->font_alt;      // (used for active state)
2804
2805         font = (active ? font2 : font1);
2806       }
2807
2808       if (s != NULL)
2809       {
2810         char *s_cut;
2811
2812         if (size <= 0)
2813         {
2814           // don't truncate output if "chars" is zero or less
2815           size = strlen(s);
2816
2817           // dynamically correct text alignment
2818           pos->width = size * getFontWidth(font);
2819         }
2820
2821         s_cut = getStringCopyN(s, size);
2822
2823         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2824                     s_cut, font, mask_mode);
2825
2826         free(s_cut);
2827       }
2828     }
2829
2830     redraw_mask |= REDRAW_DOOR_1;
2831   }
2832
2833   SetGameStatus(GAME_MODE_PLAYING);
2834 }
2835
2836 void UpdateAndDisplayGameControlValues(void)
2837 {
2838   if (tape.deactivate_display)
2839     return;
2840
2841   UpdateGameControlValues();
2842   DisplayGameControlValues();
2843 }
2844
2845 #if 0
2846 static void UpdateGameDoorValues(void)
2847 {
2848   UpdateGameControlValues();
2849 }
2850 #endif
2851
2852 void DrawGameDoorValues(void)
2853 {
2854   DisplayGameControlValues();
2855 }
2856
2857
2858 // ============================================================================
2859 // InitGameEngine()
2860 // ----------------------------------------------------------------------------
2861 // initialize game engine due to level / tape version number
2862 // ============================================================================
2863
2864 static void InitGameEngine(void)
2865 {
2866   int i, j, k, l, x, y;
2867
2868   // set game engine from tape file when re-playing, else from level file
2869   game.engine_version = (tape.playing ? tape.engine_version :
2870                          level.game_version);
2871
2872   // set single or multi-player game mode (needed for re-playing tapes)
2873   game.team_mode = setup.team_mode;
2874
2875   if (tape.playing)
2876   {
2877     int num_players = 0;
2878
2879     for (i = 0; i < MAX_PLAYERS; i++)
2880       if (tape.player_participates[i])
2881         num_players++;
2882
2883     // multi-player tapes contain input data for more than one player
2884     game.team_mode = (num_players > 1);
2885   }
2886
2887 #if 0
2888   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2889         level.game_version);
2890   Debug("game:init:level", "          tape.file_version   == %06d",
2891         tape.file_version);
2892   Debug("game:init:level", "          tape.game_version   == %06d",
2893         tape.game_version);
2894   Debug("game:init:level", "          tape.engine_version == %06d",
2895         tape.engine_version);
2896   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2897         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2898 #endif
2899
2900   // --------------------------------------------------------------------------
2901   // set flags for bugs and changes according to active game engine version
2902   // --------------------------------------------------------------------------
2903
2904   /*
2905     Summary of bugfix:
2906     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2907
2908     Bug was introduced in version:
2909     2.0.1
2910
2911     Bug was fixed in version:
2912     4.2.0.0
2913
2914     Description:
2915     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2916     but the property "can fall" was missing, which caused some levels to be
2917     unsolvable. This was fixed in version 4.2.0.0.
2918
2919     Affected levels/tapes:
2920     An example for a tape that was fixed by this bugfix is tape 029 from the
2921     level set "rnd_sam_bateman".
2922     The wrong behaviour will still be used for all levels or tapes that were
2923     created/recorded with it. An example for this is tape 023 from the level
2924     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2925   */
2926
2927   boolean use_amoeba_dropping_cannot_fall_bug =
2928     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2929       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2930      (tape.playing &&
2931       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2932       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2933
2934   /*
2935     Summary of bugfix/change:
2936     Fixed move speed of elements entering or leaving magic wall.
2937
2938     Fixed/changed in version:
2939     2.0.1
2940
2941     Description:
2942     Before 2.0.1, move speed of elements entering or leaving magic wall was
2943     twice as fast as it is now.
2944     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2945
2946     Affected levels/tapes:
2947     The first condition is generally needed for all levels/tapes before version
2948     2.0.1, which might use the old behaviour before it was changed; known tapes
2949     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2950     The second condition is an exception from the above case and is needed for
2951     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2952     above, but before it was known that this change would break tapes like the
2953     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2954     although the engine version while recording maybe was before 2.0.1. There
2955     are a lot of tapes that are affected by this exception, like tape 006 from
2956     the level set "rnd_conor_mancone".
2957   */
2958
2959   boolean use_old_move_stepsize_for_magic_wall =
2960     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2961      !(tape.playing &&
2962        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2963        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2964
2965   /*
2966     Summary of bugfix/change:
2967     Fixed handling for custom elements that change when pushed by the player.
2968
2969     Fixed/changed in version:
2970     3.1.0
2971
2972     Description:
2973     Before 3.1.0, custom elements that "change when pushing" changed directly
2974     after the player started pushing them (until then handled in "DigField()").
2975     Since 3.1.0, these custom elements are not changed until the "pushing"
2976     move of the element is finished (now handled in "ContinueMoving()").
2977
2978     Affected levels/tapes:
2979     The first condition is generally needed for all levels/tapes before version
2980     3.1.0, which might use the old behaviour before it was changed; known tapes
2981     that are affected are some tapes from the level set "Walpurgis Gardens" by
2982     Jamie Cullen.
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2985     above (including some development versions of 3.1.0), but before it was
2986     known that this change would break tapes like the above and was fixed in
2987     3.1.1, so that the changed behaviour was active although the engine version
2988     while recording maybe was before 3.1.0. There is at least one tape that is
2989     affected by this exception, which is the tape for the one-level set "Bug
2990     Machine" by Juergen Bonhagen.
2991   */
2992
2993   game.use_change_when_pushing_bug =
2994     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2995      !(tape.playing &&
2996        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2997        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2998
2999   /*
3000     Summary of bugfix/change:
3001     Fixed handling for blocking the field the player leaves when moving.
3002
3003     Fixed/changed in version:
3004     3.1.1
3005
3006     Description:
3007     Before 3.1.1, when "block last field when moving" was enabled, the field
3008     the player is leaving when moving was blocked for the time of the move,
3009     and was directly unblocked afterwards. This resulted in the last field
3010     being blocked for exactly one less than the number of frames of one player
3011     move. Additionally, even when blocking was disabled, the last field was
3012     blocked for exactly one frame.
3013     Since 3.1.1, due to changes in player movement handling, the last field
3014     is not blocked at all when blocking is disabled. When blocking is enabled,
3015     the last field is blocked for exactly the number of frames of one player
3016     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3017     last field is blocked for exactly one more than the number of frames of
3018     one player move.
3019
3020     Affected levels/tapes:
3021     (!!! yet to be determined -- probably many !!!)
3022   */
3023
3024   game.use_block_last_field_bug =
3025     (game.engine_version < VERSION_IDENT(3,1,1,0));
3026
3027   /* various special flags and settings for native Emerald Mine game engine */
3028
3029   game_em.use_single_button =
3030     (game.engine_version > VERSION_IDENT(4,0,0,2));
3031
3032   game_em.use_snap_key_bug =
3033     (game.engine_version < VERSION_IDENT(4,0,1,0));
3034
3035   game_em.use_random_bug =
3036     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3037
3038   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3039
3040   game_em.use_old_explosions            = use_old_em_engine;
3041   game_em.use_old_android               = use_old_em_engine;
3042   game_em.use_old_push_elements         = use_old_em_engine;
3043   game_em.use_old_push_into_acid        = use_old_em_engine;
3044
3045   game_em.use_wrap_around               = !use_old_em_engine;
3046
3047   // --------------------------------------------------------------------------
3048
3049   // set maximal allowed number of custom element changes per game frame
3050   game.max_num_changes_per_frame = 1;
3051
3052   // default scan direction: scan playfield from top/left to bottom/right
3053   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3054
3055   // dynamically adjust element properties according to game engine version
3056   InitElementPropertiesEngine(game.engine_version);
3057
3058   // ---------- initialize special element properties -------------------------
3059
3060   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3061   if (use_amoeba_dropping_cannot_fall_bug)
3062     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3063
3064   // ---------- initialize player's initial move delay ------------------------
3065
3066   // dynamically adjust player properties according to level information
3067   for (i = 0; i < MAX_PLAYERS; i++)
3068     game.initial_move_delay_value[i] =
3069       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3070
3071   // dynamically adjust player properties according to game engine version
3072   for (i = 0; i < MAX_PLAYERS; i++)
3073     game.initial_move_delay[i] =
3074       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3075        game.initial_move_delay_value[i] : 0);
3076
3077   // ---------- initialize player's initial push delay ------------------------
3078
3079   // dynamically adjust player properties according to game engine version
3080   game.initial_push_delay_value =
3081     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3082
3083   // ---------- initialize changing elements ----------------------------------
3084
3085   // initialize changing elements information
3086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3087   {
3088     struct ElementInfo *ei = &element_info[i];
3089
3090     // this pointer might have been changed in the level editor
3091     ei->change = &ei->change_page[0];
3092
3093     if (!IS_CUSTOM_ELEMENT(i))
3094     {
3095       ei->change->target_element = EL_EMPTY_SPACE;
3096       ei->change->delay_fixed = 0;
3097       ei->change->delay_random = 0;
3098       ei->change->delay_frames = 1;
3099     }
3100
3101     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3102     {
3103       ei->has_change_event[j] = FALSE;
3104
3105       ei->event_page_nr[j] = 0;
3106       ei->event_page[j] = &ei->change_page[0];
3107     }
3108   }
3109
3110   // add changing elements from pre-defined list
3111   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3114     struct ElementInfo *ei = &element_info[ch_delay->element];
3115
3116     ei->change->target_element       = ch_delay->target_element;
3117     ei->change->delay_fixed          = ch_delay->change_delay;
3118
3119     ei->change->pre_change_function  = ch_delay->pre_change_function;
3120     ei->change->change_function      = ch_delay->change_function;
3121     ei->change->post_change_function = ch_delay->post_change_function;
3122
3123     ei->change->can_change = TRUE;
3124     ei->change->can_change_or_has_action = TRUE;
3125
3126     ei->has_change_event[CE_DELAY] = TRUE;
3127
3128     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3129     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3130   }
3131
3132   // ---------- initialize internal run-time variables ------------------------
3133
3134   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3135   {
3136     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3137
3138     for (j = 0; j < ei->num_change_pages; j++)
3139     {
3140       ei->change_page[j].can_change_or_has_action =
3141         (ei->change_page[j].can_change |
3142          ei->change_page[j].has_action);
3143     }
3144   }
3145
3146   // add change events from custom element configuration
3147   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3148   {
3149     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3150
3151     for (j = 0; j < ei->num_change_pages; j++)
3152     {
3153       if (!ei->change_page[j].can_change_or_has_action)
3154         continue;
3155
3156       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3157       {
3158         // only add event page for the first page found with this event
3159         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3160         {
3161           ei->has_change_event[k] = TRUE;
3162
3163           ei->event_page_nr[k] = j;
3164           ei->event_page[k] = &ei->change_page[j];
3165         }
3166       }
3167     }
3168   }
3169
3170   // ---------- initialize reference elements in change conditions ------------
3171
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     int element = EL_CUSTOM_START + i;
3175     struct ElementInfo *ei = &element_info[element];
3176
3177     for (j = 0; j < ei->num_change_pages; j++)
3178     {
3179       int trigger_element = ei->change_page[j].initial_trigger_element;
3180
3181       if (trigger_element >= EL_PREV_CE_8 &&
3182           trigger_element <= EL_NEXT_CE_8)
3183         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3184
3185       ei->change_page[j].trigger_element = trigger_element;
3186     }
3187   }
3188
3189   // ---------- initialize run-time trigger player and element ----------------
3190
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3198       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3199       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3200       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3201       ei->change_page[j].actual_trigger_ce_value = 0;
3202       ei->change_page[j].actual_trigger_ce_score = 0;
3203     }
3204   }
3205
3206   // ---------- initialize trigger events -------------------------------------
3207
3208   // initialize trigger events information
3209   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3210     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3211       trigger_events[i][j] = FALSE;
3212
3213   // add trigger events from element change event properties
3214   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3215   {
3216     struct ElementInfo *ei = &element_info[i];
3217
3218     for (j = 0; j < ei->num_change_pages; j++)
3219     {
3220       if (!ei->change_page[j].can_change_or_has_action)
3221         continue;
3222
3223       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3224       {
3225         int trigger_element = ei->change_page[j].trigger_element;
3226
3227         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3228         {
3229           if (ei->change_page[j].has_event[k])
3230           {
3231             if (IS_GROUP_ELEMENT(trigger_element))
3232             {
3233               struct ElementGroupInfo *group =
3234                 element_info[trigger_element].group;
3235
3236               for (l = 0; l < group->num_elements_resolved; l++)
3237                 trigger_events[group->element_resolved[l]][k] = TRUE;
3238             }
3239             else if (trigger_element == EL_ANY_ELEMENT)
3240               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3241                 trigger_events[l][k] = TRUE;
3242             else
3243               trigger_events[trigger_element][k] = TRUE;
3244           }
3245         }
3246       }
3247     }
3248   }
3249
3250   // ---------- initialize push delay -----------------------------------------
3251
3252   // initialize push delay values to default
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254   {
3255     if (!IS_CUSTOM_ELEMENT(i))
3256     {
3257       // set default push delay values (corrected since version 3.0.7-1)
3258       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3259       {
3260         element_info[i].push_delay_fixed = 2;
3261         element_info[i].push_delay_random = 8;
3262       }
3263       else
3264       {
3265         element_info[i].push_delay_fixed = 8;
3266         element_info[i].push_delay_random = 8;
3267       }
3268     }
3269   }
3270
3271   // set push delay value for certain elements from pre-defined list
3272   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3273   {
3274     int e = push_delay_list[i].element;
3275
3276     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3277     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3278   }
3279
3280   // set push delay value for Supaplex elements for newer engine versions
3281   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3282   {
3283     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3284     {
3285       if (IS_SP_ELEMENT(i))
3286       {
3287         // set SP push delay to just enough to push under a falling zonk
3288         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3289
3290         element_info[i].push_delay_fixed  = delay;
3291         element_info[i].push_delay_random = 0;
3292       }
3293     }
3294   }
3295
3296   // ---------- initialize move stepsize --------------------------------------
3297
3298   // initialize move stepsize values to default
3299   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3300     if (!IS_CUSTOM_ELEMENT(i))
3301       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3302
3303   // set move stepsize value for certain elements from pre-defined list
3304   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3305   {
3306     int e = move_stepsize_list[i].element;
3307
3308     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3309
3310     // set move stepsize value for certain elements for older engine versions
3311     if (use_old_move_stepsize_for_magic_wall)
3312     {
3313       if (e == EL_MAGIC_WALL_FILLING ||
3314           e == EL_MAGIC_WALL_EMPTYING ||
3315           e == EL_BD_MAGIC_WALL_FILLING ||
3316           e == EL_BD_MAGIC_WALL_EMPTYING)
3317         element_info[e].move_stepsize *= 2;
3318     }
3319   }
3320
3321   // ---------- initialize collect score --------------------------------------
3322
3323   // initialize collect score values for custom elements from initial value
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (IS_CUSTOM_ELEMENT(i))
3326       element_info[i].collect_score = element_info[i].collect_score_initial;
3327
3328   // ---------- initialize collect count --------------------------------------
3329
3330   // initialize collect count values for non-custom elements
3331   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3332     if (!IS_CUSTOM_ELEMENT(i))
3333       element_info[i].collect_count_initial = 0;
3334
3335   // add collect count values for all elements from pre-defined list
3336   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3337     element_info[collect_count_list[i].element].collect_count_initial =
3338       collect_count_list[i].count;
3339
3340   // ---------- initialize access direction -----------------------------------
3341
3342   // initialize access direction values to default (access from every side)
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3346
3347   // set access direction value for certain elements from pre-defined list
3348   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3349     element_info[access_direction_list[i].element].access_direction =
3350       access_direction_list[i].direction;
3351
3352   // ---------- initialize explosion content ----------------------------------
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354   {
3355     if (IS_CUSTOM_ELEMENT(i))
3356       continue;
3357
3358     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3359     {
3360       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3361
3362       element_info[i].content.e[x][y] =
3363         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3364          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3365          i == EL_PLAYER_3 ? EL_EMERALD :
3366          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3367          i == EL_MOLE ? EL_EMERALD_RED :
3368          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3369          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3370          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3371          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3372          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3373          i == EL_WALL_EMERALD ? EL_EMERALD :
3374          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3375          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3376          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3377          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3378          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3379          i == EL_WALL_PEARL ? EL_PEARL :
3380          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3381          EL_EMPTY);
3382     }
3383   }
3384
3385   // ---------- initialize recursion detection --------------------------------
3386   recursion_loop_depth = 0;
3387   recursion_loop_detected = FALSE;
3388   recursion_loop_element = EL_UNDEFINED;
3389
3390   // ---------- initialize graphics engine ------------------------------------
3391   game.scroll_delay_value =
3392     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3393      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3394      !setup.forced_scroll_delay           ? 0 :
3395      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3396   game.scroll_delay_value =
3397     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3398
3399   // ---------- initialize game engine snapshots ------------------------------
3400   for (i = 0; i < MAX_PLAYERS; i++)
3401     game.snapshot.last_action[i] = 0;
3402   game.snapshot.changed_action = FALSE;
3403   game.snapshot.collected_item = FALSE;
3404   game.snapshot.mode =
3405     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3406      SNAPSHOT_MODE_EVERY_STEP :
3407      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3408      SNAPSHOT_MODE_EVERY_MOVE :
3409      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3410      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3411   game.snapshot.save_snapshot = FALSE;
3412
3413   // ---------- initialize level time for Supaplex engine ---------------------
3414   // Supaplex levels with time limit currently unsupported -- should be added
3415   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3416     level.time = 0;
3417
3418   // ---------- initialize flags for handling game actions --------------------
3419
3420   // set flags for game actions to default values
3421   game.use_key_actions = TRUE;
3422   game.use_mouse_actions = FALSE;
3423
3424   // when using Mirror Magic game engine, handle mouse events only
3425   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3426   {
3427     game.use_key_actions = FALSE;
3428     game.use_mouse_actions = TRUE;
3429   }
3430
3431   // check for custom elements with mouse click events
3432   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3433   {
3434     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3435     {
3436       int element = EL_CUSTOM_START + i;
3437
3438       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3439           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3440           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3441           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3442         game.use_mouse_actions = TRUE;
3443     }
3444   }
3445 }
3446
3447 static int get_num_special_action(int element, int action_first,
3448                                   int action_last)
3449 {
3450   int num_special_action = 0;
3451   int i, j;
3452
3453   for (i = action_first; i <= action_last; i++)
3454   {
3455     boolean found = FALSE;
3456
3457     for (j = 0; j < NUM_DIRECTIONS; j++)
3458       if (el_act_dir2img(element, i, j) !=
3459           el_act_dir2img(element, ACTION_DEFAULT, j))
3460         found = TRUE;
3461
3462     if (found)
3463       num_special_action++;
3464     else
3465       break;
3466   }
3467
3468   return num_special_action;
3469 }
3470
3471
3472 // ============================================================================
3473 // InitGame()
3474 // ----------------------------------------------------------------------------
3475 // initialize and start new game
3476 // ============================================================================
3477
3478 #if DEBUG_INIT_PLAYER
3479 static void DebugPrintPlayerStatus(char *message)
3480 {
3481   int i;
3482
3483   if (!options.debug)
3484     return;
3485
3486   Debug("game:init:player", "%s:", message);
3487
3488   for (i = 0; i < MAX_PLAYERS; i++)
3489   {
3490     struct PlayerInfo *player = &stored_player[i];
3491
3492     Debug("game:init:player",
3493           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3494           i + 1,
3495           player->present,
3496           player->connected,
3497           player->connected_locally,
3498           player->connected_network,
3499           player->active,
3500           (local_player == player ? " (local player)" : ""));
3501   }
3502 }
3503 #endif
3504
3505 void InitGame(void)
3506 {
3507   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3508   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3509   int fade_mask = REDRAW_FIELD;
3510
3511   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3512   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3513   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3514   int initial_move_dir = MV_DOWN;
3515   int i, j, x, y;
3516
3517   // required here to update video display before fading (FIX THIS)
3518   DrawMaskedBorder(REDRAW_DOOR_2);
3519
3520   if (!game.restart_level)
3521     CloseDoor(DOOR_CLOSE_1);
3522
3523   SetGameStatus(GAME_MODE_PLAYING);
3524
3525   if (level_editor_test_game)
3526     FadeSkipNextFadeOut();
3527   else
3528     FadeSetEnterScreen();
3529
3530   if (CheckFadeAll())
3531     fade_mask = REDRAW_ALL;
3532
3533   FadeLevelSoundsAndMusic();
3534
3535   ExpireSoundLoops(TRUE);
3536
3537   FadeOut(fade_mask);
3538
3539   if (level_editor_test_game)
3540     FadeSkipNextFadeIn();
3541
3542   // needed if different viewport properties defined for playing
3543   ChangeViewportPropertiesIfNeeded();
3544
3545   ClearField();
3546
3547   DrawCompleteVideoDisplay();
3548
3549   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3550
3551   InitGameEngine();
3552   InitGameControlValues();
3553
3554   // initialize tape actions from game when recording tape
3555   if (tape.recording)
3556   {
3557     tape.use_key_actions   = game.use_key_actions;
3558     tape.use_mouse_actions = game.use_mouse_actions;
3559   }
3560
3561   // don't play tapes over network
3562   network_playing = (network.enabled && !tape.playing);
3563
3564   for (i = 0; i < MAX_PLAYERS; i++)
3565   {
3566     struct PlayerInfo *player = &stored_player[i];
3567
3568     player->index_nr = i;
3569     player->index_bit = (1 << i);
3570     player->element_nr = EL_PLAYER_1 + i;
3571
3572     player->present = FALSE;
3573     player->active = FALSE;
3574     player->mapped = FALSE;
3575
3576     player->killed = FALSE;
3577     player->reanimated = FALSE;
3578     player->buried = FALSE;
3579
3580     player->action = 0;
3581     player->effective_action = 0;
3582     player->programmed_action = 0;
3583     player->snap_action = 0;
3584
3585     player->mouse_action.lx = 0;
3586     player->mouse_action.ly = 0;
3587     player->mouse_action.button = 0;
3588     player->mouse_action.button_hint = 0;
3589
3590     player->effective_mouse_action.lx = 0;
3591     player->effective_mouse_action.ly = 0;
3592     player->effective_mouse_action.button = 0;
3593     player->effective_mouse_action.button_hint = 0;
3594
3595     for (j = 0; j < MAX_NUM_KEYS; j++)
3596       player->key[j] = FALSE;
3597
3598     player->num_white_keys = 0;
3599
3600     player->dynabomb_count = 0;
3601     player->dynabomb_size = 1;
3602     player->dynabombs_left = 0;
3603     player->dynabomb_xl = FALSE;
3604
3605     player->MovDir = initial_move_dir;
3606     player->MovPos = 0;
3607     player->GfxPos = 0;
3608     player->GfxDir = initial_move_dir;
3609     player->GfxAction = ACTION_DEFAULT;
3610     player->Frame = 0;
3611     player->StepFrame = 0;
3612
3613     player->initial_element = player->element_nr;
3614     player->artwork_element =
3615       (level.use_artwork_element[i] ? level.artwork_element[i] :
3616        player->element_nr);
3617     player->use_murphy = FALSE;
3618
3619     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3620     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3621
3622     player->gravity = level.initial_player_gravity[i];
3623
3624     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3625
3626     player->actual_frame_counter = 0;
3627
3628     player->step_counter = 0;
3629
3630     player->last_move_dir = initial_move_dir;
3631
3632     player->is_active = FALSE;
3633
3634     player->is_waiting = FALSE;
3635     player->is_moving = FALSE;
3636     player->is_auto_moving = FALSE;
3637     player->is_digging = FALSE;
3638     player->is_snapping = FALSE;
3639     player->is_collecting = FALSE;
3640     player->is_pushing = FALSE;
3641     player->is_switching = FALSE;
3642     player->is_dropping = FALSE;
3643     player->is_dropping_pressed = FALSE;
3644
3645     player->is_bored = FALSE;
3646     player->is_sleeping = FALSE;
3647
3648     player->was_waiting = TRUE;
3649     player->was_moving = FALSE;
3650     player->was_snapping = FALSE;
3651     player->was_dropping = FALSE;
3652
3653     player->force_dropping = FALSE;
3654
3655     player->frame_counter_bored = -1;
3656     player->frame_counter_sleeping = -1;
3657
3658     player->anim_delay_counter = 0;
3659     player->post_delay_counter = 0;
3660
3661     player->dir_waiting = initial_move_dir;
3662     player->action_waiting = ACTION_DEFAULT;
3663     player->last_action_waiting = ACTION_DEFAULT;
3664     player->special_action_bored = ACTION_DEFAULT;
3665     player->special_action_sleeping = ACTION_DEFAULT;
3666
3667     player->switch_x = -1;
3668     player->switch_y = -1;
3669
3670     player->drop_x = -1;
3671     player->drop_y = -1;
3672
3673     player->show_envelope = 0;
3674
3675     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3676
3677     player->push_delay       = -1;      // initialized when pushing starts
3678     player->push_delay_value = game.initial_push_delay_value;
3679
3680     player->drop_delay = 0;
3681     player->drop_pressed_delay = 0;
3682
3683     player->last_jx = -1;
3684     player->last_jy = -1;
3685     player->jx = -1;
3686     player->jy = -1;
3687
3688     player->shield_normal_time_left = 0;
3689     player->shield_deadly_time_left = 0;
3690
3691     player->inventory_infinite_element = EL_UNDEFINED;
3692     player->inventory_size = 0;
3693
3694     if (level.use_initial_inventory[i])
3695     {
3696       for (j = 0; j < level.initial_inventory_size[i]; j++)
3697       {
3698         int element = level.initial_inventory_content[i][j];
3699         int collect_count = element_info[element].collect_count_initial;
3700         int k;
3701
3702         if (!IS_CUSTOM_ELEMENT(element))
3703           collect_count = 1;
3704
3705         if (collect_count == 0)
3706           player->inventory_infinite_element = element;
3707         else
3708           for (k = 0; k < collect_count; k++)
3709             if (player->inventory_size < MAX_INVENTORY_SIZE)
3710               player->inventory_element[player->inventory_size++] = element;
3711       }
3712     }
3713
3714     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3715     SnapField(player, 0, 0);
3716
3717     map_player_action[i] = i;
3718   }
3719
3720   network_player_action_received = FALSE;
3721
3722   // initial null action
3723   if (network_playing)
3724     SendToServer_MovePlayer(MV_NONE);
3725
3726   FrameCounter = 0;
3727   TimeFrames = 0;
3728   TimePlayed = 0;
3729   TimeLeft = level.time;
3730   TapeTime = 0;
3731
3732   ScreenMovDir = MV_NONE;
3733   ScreenMovPos = 0;
3734   ScreenGfxPos = 0;
3735
3736   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3737
3738   game.robot_wheel_x = -1;
3739   game.robot_wheel_y = -1;
3740
3741   game.exit_x = -1;
3742   game.exit_y = -1;
3743
3744   game.all_players_gone = FALSE;
3745
3746   game.LevelSolved = FALSE;
3747   game.GameOver = FALSE;
3748
3749   game.GamePlayed = !tape.playing;
3750
3751   game.LevelSolved_GameWon = FALSE;
3752   game.LevelSolved_GameEnd = FALSE;
3753   game.LevelSolved_SaveTape = FALSE;
3754   game.LevelSolved_SaveScore = FALSE;
3755
3756   game.LevelSolved_CountingTime = 0;
3757   game.LevelSolved_CountingScore = 0;
3758   game.LevelSolved_CountingHealth = 0;
3759
3760   game.panel.active = TRUE;
3761
3762   game.no_time_limit = (level.time == 0);
3763
3764   game.yamyam_content_nr = 0;
3765   game.robot_wheel_active = FALSE;
3766   game.magic_wall_active = FALSE;
3767   game.magic_wall_time_left = 0;
3768   game.light_time_left = 0;
3769   game.timegate_time_left = 0;
3770   game.switchgate_pos = 0;
3771   game.wind_direction = level.wind_direction_initial;
3772
3773   game.score = 0;
3774   game.score_final = 0;
3775
3776   game.health = MAX_HEALTH;
3777   game.health_final = MAX_HEALTH;
3778
3779   game.gems_still_needed = level.gems_needed;
3780   game.sokoban_fields_still_needed = 0;
3781   game.sokoban_objects_still_needed = 0;
3782   game.lights_still_needed = 0;
3783   game.players_still_needed = 0;
3784   game.friends_still_needed = 0;
3785
3786   game.lenses_time_left = 0;
3787   game.magnify_time_left = 0;
3788
3789   game.ball_active = level.ball_active_initial;
3790   game.ball_content_nr = 0;
3791
3792   game.explosions_delayed = TRUE;
3793
3794   game.envelope_active = FALSE;
3795
3796   for (i = 0; i < NUM_BELTS; i++)
3797   {
3798     game.belt_dir[i] = MV_NONE;
3799     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3800   }
3801
3802   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3803     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3804
3805 #if DEBUG_INIT_PLAYER
3806   DebugPrintPlayerStatus("Player status at level initialization");
3807 #endif
3808
3809   SCAN_PLAYFIELD(x, y)
3810   {
3811     Tile[x][y] = Last[x][y] = level.field[x][y];
3812     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3813     ChangeDelay[x][y] = 0;
3814     ChangePage[x][y] = -1;
3815     CustomValue[x][y] = 0;              // initialized in InitField()
3816     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3817     AmoebaNr[x][y] = 0;
3818     WasJustMoving[x][y] = 0;
3819     WasJustFalling[x][y] = 0;
3820     CheckCollision[x][y] = 0;
3821     CheckImpact[x][y] = 0;
3822     Stop[x][y] = FALSE;
3823     Pushed[x][y] = FALSE;
3824
3825     ChangeCount[x][y] = 0;
3826     ChangeEvent[x][y] = -1;
3827
3828     ExplodePhase[x][y] = 0;
3829     ExplodeDelay[x][y] = 0;
3830     ExplodeField[x][y] = EX_TYPE_NONE;
3831
3832     RunnerVisit[x][y] = 0;
3833     PlayerVisit[x][y] = 0;
3834
3835     GfxFrame[x][y] = 0;
3836     GfxRandom[x][y] = INIT_GFX_RANDOM();
3837     GfxElement[x][y] = EL_UNDEFINED;
3838     GfxAction[x][y] = ACTION_DEFAULT;
3839     GfxDir[x][y] = MV_NONE;
3840     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3841   }
3842
3843   SCAN_PLAYFIELD(x, y)
3844   {
3845     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3846       emulate_bd = FALSE;
3847     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3848       emulate_sb = FALSE;
3849     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3850       emulate_sp = FALSE;
3851
3852     InitField(x, y, TRUE);
3853
3854     ResetGfxAnimation(x, y);
3855   }
3856
3857   InitBeltMovement();
3858
3859   for (i = 0; i < MAX_PLAYERS; i++)
3860   {
3861     struct PlayerInfo *player = &stored_player[i];
3862
3863     // set number of special actions for bored and sleeping animation
3864     player->num_special_action_bored =
3865       get_num_special_action(player->artwork_element,
3866                              ACTION_BORING_1, ACTION_BORING_LAST);
3867     player->num_special_action_sleeping =
3868       get_num_special_action(player->artwork_element,
3869                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3870   }
3871
3872   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3873                     emulate_sb ? EMU_SOKOBAN :
3874                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3875
3876   // initialize type of slippery elements
3877   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3878   {
3879     if (!IS_CUSTOM_ELEMENT(i))
3880     {
3881       // default: elements slip down either to the left or right randomly
3882       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3883
3884       // SP style elements prefer to slip down on the left side
3885       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3886         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3887
3888       // BD style elements prefer to slip down on the left side
3889       if (game.emulation == EMU_BOULDERDASH)
3890         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3891     }
3892   }
3893
3894   // initialize explosion and ignition delay
3895   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3896   {
3897     if (!IS_CUSTOM_ELEMENT(i))
3898     {
3899       int num_phase = 8;
3900       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3901                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3902                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3903       int last_phase = (num_phase + 1) * delay;
3904       int half_phase = (num_phase / 2) * delay;
3905
3906       element_info[i].explosion_delay = last_phase - 1;
3907       element_info[i].ignition_delay = half_phase;
3908
3909       if (i == EL_BLACK_ORB)
3910         element_info[i].ignition_delay = 1;
3911     }
3912   }
3913
3914   // correct non-moving belts to start moving left
3915   for (i = 0; i < NUM_BELTS; i++)
3916     if (game.belt_dir[i] == MV_NONE)
3917       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3918
3919 #if USE_NEW_PLAYER_ASSIGNMENTS
3920   // use preferred player also in local single-player mode
3921   if (!network.enabled && !game.team_mode)
3922   {
3923     int new_index_nr = setup.network_player_nr;
3924
3925     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3926     {
3927       for (i = 0; i < MAX_PLAYERS; i++)
3928         stored_player[i].connected_locally = FALSE;
3929
3930       stored_player[new_index_nr].connected_locally = TRUE;
3931     }
3932   }
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     stored_player[i].connected = FALSE;
3937
3938     // in network game mode, the local player might not be the first player
3939     if (stored_player[i].connected_locally)
3940       local_player = &stored_player[i];
3941   }
3942
3943   if (!network.enabled)
3944     local_player->connected = TRUE;
3945
3946   if (tape.playing)
3947   {
3948     for (i = 0; i < MAX_PLAYERS; i++)
3949       stored_player[i].connected = tape.player_participates[i];
3950   }
3951   else if (network.enabled)
3952   {
3953     // add team mode players connected over the network (needed for correct
3954     // assignment of player figures from level to locally playing players)
3955
3956     for (i = 0; i < MAX_PLAYERS; i++)
3957       if (stored_player[i].connected_network)
3958         stored_player[i].connected = TRUE;
3959   }
3960   else if (game.team_mode)
3961   {
3962     // try to guess locally connected team mode players (needed for correct
3963     // assignment of player figures from level to locally playing players)
3964
3965     for (i = 0; i < MAX_PLAYERS; i++)
3966       if (setup.input[i].use_joystick ||
3967           setup.input[i].key.left != KSYM_UNDEFINED)
3968         stored_player[i].connected = TRUE;
3969   }
3970
3971 #if DEBUG_INIT_PLAYER
3972   DebugPrintPlayerStatus("Player status after level initialization");
3973 #endif
3974
3975 #if DEBUG_INIT_PLAYER
3976   Debug("game:init:player", "Reassigning players ...");
3977 #endif
3978
3979   // check if any connected player was not found in playfield
3980   for (i = 0; i < MAX_PLAYERS; i++)
3981   {
3982     struct PlayerInfo *player = &stored_player[i];
3983
3984     if (player->connected && !player->present)
3985     {
3986       struct PlayerInfo *field_player = NULL;
3987
3988 #if DEBUG_INIT_PLAYER
3989       Debug("game:init:player",
3990             "- looking for field player for player %d ...", i + 1);
3991 #endif
3992
3993       // assign first free player found that is present in the playfield
3994
3995       // first try: look for unmapped playfield player that is not connected
3996       for (j = 0; j < MAX_PLAYERS; j++)
3997         if (field_player == NULL &&
3998             stored_player[j].present &&
3999             !stored_player[j].mapped &&
4000             !stored_player[j].connected)
4001           field_player = &stored_player[j];
4002
4003       // second try: look for *any* unmapped playfield player
4004       for (j = 0; j < MAX_PLAYERS; j++)
4005         if (field_player == NULL &&
4006             stored_player[j].present &&
4007             !stored_player[j].mapped)
4008           field_player = &stored_player[j];
4009
4010       if (field_player != NULL)
4011       {
4012         int jx = field_player->jx, jy = field_player->jy;
4013
4014 #if DEBUG_INIT_PLAYER
4015         Debug("game:init:player", "- found player %d",
4016               field_player->index_nr + 1);
4017 #endif
4018
4019         player->present = FALSE;
4020         player->active = FALSE;
4021
4022         field_player->present = TRUE;
4023         field_player->active = TRUE;
4024
4025         /*
4026         player->initial_element = field_player->initial_element;
4027         player->artwork_element = field_player->artwork_element;
4028
4029         player->block_last_field       = field_player->block_last_field;
4030         player->block_delay_adjustment = field_player->block_delay_adjustment;
4031         */
4032
4033         StorePlayer[jx][jy] = field_player->element_nr;
4034
4035         field_player->jx = field_player->last_jx = jx;
4036         field_player->jy = field_player->last_jy = jy;
4037
4038         if (local_player == player)
4039           local_player = field_player;
4040
4041         map_player_action[field_player->index_nr] = i;
4042
4043         field_player->mapped = TRUE;
4044
4045 #if DEBUG_INIT_PLAYER
4046         Debug("game:init:player", "- map_player_action[%d] == %d",
4047               field_player->index_nr + 1, i + 1);
4048 #endif
4049       }
4050     }
4051
4052     if (player->connected && player->present)
4053       player->mapped = TRUE;
4054   }
4055
4056 #if DEBUG_INIT_PLAYER
4057   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4058 #endif
4059
4060 #else
4061
4062   // check if any connected player was not found in playfield
4063   for (i = 0; i < MAX_PLAYERS; i++)
4064   {
4065     struct PlayerInfo *player = &stored_player[i];
4066
4067     if (player->connected && !player->present)
4068     {
4069       for (j = 0; j < MAX_PLAYERS; j++)
4070       {
4071         struct PlayerInfo *field_player = &stored_player[j];
4072         int jx = field_player->jx, jy = field_player->jy;
4073
4074         // assign first free player found that is present in the playfield
4075         if (field_player->present && !field_player->connected)
4076         {
4077           player->present = TRUE;
4078           player->active = TRUE;
4079
4080           field_player->present = FALSE;
4081           field_player->active = FALSE;
4082
4083           player->initial_element = field_player->initial_element;
4084           player->artwork_element = field_player->artwork_element;
4085
4086           player->block_last_field       = field_player->block_last_field;
4087           player->block_delay_adjustment = field_player->block_delay_adjustment;
4088
4089           StorePlayer[jx][jy] = player->element_nr;
4090
4091           player->jx = player->last_jx = jx;
4092           player->jy = player->last_jy = jy;
4093
4094           break;
4095         }
4096       }
4097     }
4098   }
4099 #endif
4100
4101 #if 0
4102   Debug("game:init:player", "local_player->present == %d",
4103         local_player->present);
4104 #endif
4105
4106   // set focus to local player for network games, else to all players
4107   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4108   game.centered_player_nr_next = game.centered_player_nr;
4109   game.set_centered_player = FALSE;
4110   game.set_centered_player_wrap = FALSE;
4111
4112   if (network_playing && tape.recording)
4113   {
4114     // store client dependent player focus when recording network games
4115     tape.centered_player_nr_next = game.centered_player_nr_next;
4116     tape.set_centered_player = TRUE;
4117   }
4118
4119   if (tape.playing)
4120   {
4121     // when playing a tape, eliminate all players who do not participate
4122
4123 #if USE_NEW_PLAYER_ASSIGNMENTS
4124
4125     if (!game.team_mode)
4126     {
4127       for (i = 0; i < MAX_PLAYERS; i++)
4128       {
4129         if (stored_player[i].active &&
4130             !tape.player_participates[map_player_action[i]])
4131         {
4132           struct PlayerInfo *player = &stored_player[i];
4133           int jx = player->jx, jy = player->jy;
4134
4135 #if DEBUG_INIT_PLAYER
4136           Debug("game:init:player", "Removing player %d at (%d, %d)",
4137                 i + 1, jx, jy);
4138 #endif
4139
4140           player->active = FALSE;
4141           StorePlayer[jx][jy] = 0;
4142           Tile[jx][jy] = EL_EMPTY;
4143         }
4144       }
4145     }
4146
4147 #else
4148
4149     for (i = 0; i < MAX_PLAYERS; i++)
4150     {
4151       if (stored_player[i].active &&
4152           !tape.player_participates[i])
4153       {
4154         struct PlayerInfo *player = &stored_player[i];
4155         int jx = player->jx, jy = player->jy;
4156
4157         player->active = FALSE;
4158         StorePlayer[jx][jy] = 0;
4159         Tile[jx][jy] = EL_EMPTY;
4160       }
4161     }
4162 #endif
4163   }
4164   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4165   {
4166     // when in single player mode, eliminate all but the local player
4167
4168     for (i = 0; i < MAX_PLAYERS; i++)
4169     {
4170       struct PlayerInfo *player = &stored_player[i];
4171
4172       if (player->active && player != local_player)
4173       {
4174         int jx = player->jx, jy = player->jy;
4175
4176         player->active = FALSE;
4177         player->present = FALSE;
4178
4179         StorePlayer[jx][jy] = 0;
4180         Tile[jx][jy] = EL_EMPTY;
4181       }
4182     }
4183   }
4184
4185   for (i = 0; i < MAX_PLAYERS; i++)
4186     if (stored_player[i].active)
4187       game.players_still_needed++;
4188
4189   if (level.solved_by_one_player)
4190     game.players_still_needed = 1;
4191
4192   // when recording the game, store which players take part in the game
4193   if (tape.recording)
4194   {
4195 #if USE_NEW_PLAYER_ASSIGNMENTS
4196     for (i = 0; i < MAX_PLAYERS; i++)
4197       if (stored_player[i].connected)
4198         tape.player_participates[i] = TRUE;
4199 #else
4200     for (i = 0; i < MAX_PLAYERS; i++)
4201       if (stored_player[i].active)
4202         tape.player_participates[i] = TRUE;
4203 #endif
4204   }
4205
4206 #if DEBUG_INIT_PLAYER
4207   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4208 #endif
4209
4210   if (BorderElement == EL_EMPTY)
4211   {
4212     SBX_Left = 0;
4213     SBX_Right = lev_fieldx - SCR_FIELDX;
4214     SBY_Upper = 0;
4215     SBY_Lower = lev_fieldy - SCR_FIELDY;
4216   }
4217   else
4218   {
4219     SBX_Left = -1;
4220     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4221     SBY_Upper = -1;
4222     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4223   }
4224
4225   if (full_lev_fieldx <= SCR_FIELDX)
4226     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4227   if (full_lev_fieldy <= SCR_FIELDY)
4228     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4229
4230   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4231     SBX_Left--;
4232   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4233     SBY_Upper--;
4234
4235   // if local player not found, look for custom element that might create
4236   // the player (make some assumptions about the right custom element)
4237   if (!local_player->present)
4238   {
4239     int start_x = 0, start_y = 0;
4240     int found_rating = 0;
4241     int found_element = EL_UNDEFINED;
4242     int player_nr = local_player->index_nr;
4243
4244     SCAN_PLAYFIELD(x, y)
4245     {
4246       int element = Tile[x][y];
4247       int content;
4248       int xx, yy;
4249       boolean is_player;
4250
4251       if (level.use_start_element[player_nr] &&
4252           level.start_element[player_nr] == element &&
4253           found_rating < 4)
4254       {
4255         start_x = x;
4256         start_y = y;
4257
4258         found_rating = 4;
4259         found_element = element;
4260       }
4261
4262       if (!IS_CUSTOM_ELEMENT(element))
4263         continue;
4264
4265       if (CAN_CHANGE(element))
4266       {
4267         for (i = 0; i < element_info[element].num_change_pages; i++)
4268         {
4269           // check for player created from custom element as single target
4270           content = element_info[element].change_page[i].target_element;
4271           is_player = ELEM_IS_PLAYER(content);
4272
4273           if (is_player && (found_rating < 3 ||
4274                             (found_rating == 3 && element < found_element)))
4275           {
4276             start_x = x;
4277             start_y = y;
4278
4279             found_rating = 3;
4280             found_element = element;
4281           }
4282         }
4283       }
4284
4285       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4286       {
4287         // check for player created from custom element as explosion content
4288         content = element_info[element].content.e[xx][yy];
4289         is_player = ELEM_IS_PLAYER(content);
4290
4291         if (is_player && (found_rating < 2 ||
4292                           (found_rating == 2 && element < found_element)))
4293         {
4294           start_x = x + xx - 1;
4295           start_y = y + yy - 1;
4296
4297           found_rating = 2;
4298           found_element = element;
4299         }
4300
4301         if (!CAN_CHANGE(element))
4302           continue;
4303
4304         for (i = 0; i < element_info[element].num_change_pages; i++)
4305         {
4306           // check for player created from custom element as extended target
4307           content =
4308             element_info[element].change_page[i].target_content.e[xx][yy];
4309
4310           is_player = ELEM_IS_PLAYER(content);
4311
4312           if (is_player && (found_rating < 1 ||
4313                             (found_rating == 1 && element < found_element)))
4314           {
4315             start_x = x + xx - 1;
4316             start_y = y + yy - 1;
4317
4318             found_rating = 1;
4319             found_element = element;
4320           }
4321         }
4322       }
4323     }
4324
4325     scroll_x = SCROLL_POSITION_X(start_x);
4326     scroll_y = SCROLL_POSITION_Y(start_y);
4327   }
4328   else
4329   {
4330     scroll_x = SCROLL_POSITION_X(local_player->jx);
4331     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4332   }
4333
4334   // !!! FIX THIS (START) !!!
4335   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4336   {
4337     InitGameEngine_EM();
4338   }
4339   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4340   {
4341     InitGameEngine_SP();
4342   }
4343   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4344   {
4345     InitGameEngine_MM();
4346   }
4347   else
4348   {
4349     DrawLevel(REDRAW_FIELD);
4350     DrawAllPlayers();
4351
4352     // after drawing the level, correct some elements
4353     if (game.timegate_time_left == 0)
4354       CloseAllOpenTimegates();
4355   }
4356
4357   // blit playfield from scroll buffer to normal back buffer for fading in
4358   BlitScreenToBitmap(backbuffer);
4359   // !!! FIX THIS (END) !!!
4360
4361   DrawMaskedBorder(fade_mask);
4362
4363   FadeIn(fade_mask);
4364
4365 #if 1
4366   // full screen redraw is required at this point in the following cases:
4367   // - special editor door undrawn when game was started from level editor
4368   // - drawing area (playfield) was changed and has to be removed completely
4369   redraw_mask = REDRAW_ALL;
4370   BackToFront();
4371 #endif
4372
4373   if (!game.restart_level)
4374   {
4375     // copy default game door content to main double buffer
4376
4377     // !!! CHECK AGAIN !!!
4378     SetPanelBackground();
4379     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4380     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4381   }
4382
4383   SetPanelBackground();
4384   SetDrawBackgroundMask(REDRAW_DOOR_1);
4385
4386   UpdateAndDisplayGameControlValues();
4387
4388   if (!game.restart_level)
4389   {
4390     UnmapGameButtons();
4391     UnmapTapeButtons();
4392
4393     FreeGameButtons();
4394     CreateGameButtons();
4395
4396     MapGameButtons();
4397     MapTapeButtons();
4398
4399     // copy actual game door content to door double buffer for OpenDoor()
4400     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4401
4402     OpenDoor(DOOR_OPEN_ALL);
4403
4404     KeyboardAutoRepeatOffUnlessAutoplay();
4405
4406 #if DEBUG_INIT_PLAYER
4407     DebugPrintPlayerStatus("Player status (final)");
4408 #endif
4409   }
4410
4411   UnmapAllGadgets();
4412
4413   MapGameButtons();
4414   MapTapeButtons();
4415
4416   if (!game.restart_level && !tape.playing)
4417   {
4418     LevelStats_incPlayed(level_nr);
4419
4420     SaveLevelSetup_SeriesInfo();
4421   }
4422
4423   game.restart_level = FALSE;
4424   game.restart_game_message = NULL;
4425   game.request_active = FALSE;
4426
4427   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4428     InitGameActions_MM();
4429
4430   SaveEngineSnapshotToListInitial();
4431
4432   if (!game.restart_level)
4433   {
4434     PlaySound(SND_GAME_STARTING);
4435
4436     if (setup.sound_music)
4437       PlayLevelMusic();
4438   }
4439 }
4440
4441 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4442                         int actual_player_x, int actual_player_y)
4443 {
4444   // this is used for non-R'n'D game engines to update certain engine values
4445
4446   // needed to determine if sounds are played within the visible screen area
4447   scroll_x = actual_scroll_x;
4448   scroll_y = actual_scroll_y;
4449
4450   // needed to get player position for "follow finger" playing input method
4451   local_player->jx = actual_player_x;
4452   local_player->jy = actual_player_y;
4453 }
4454
4455 void InitMovDir(int x, int y)
4456 {
4457   int i, element = Tile[x][y];
4458   static int xy[4][2] =
4459   {
4460     {  0, +1 },
4461     { +1,  0 },
4462     {  0, -1 },
4463     { -1,  0 }
4464   };
4465   static int direction[3][4] =
4466   {
4467     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4468     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4469     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4470   };
4471
4472   switch (element)
4473   {
4474     case EL_BUG_RIGHT:
4475     case EL_BUG_UP:
4476     case EL_BUG_LEFT:
4477     case EL_BUG_DOWN:
4478       Tile[x][y] = EL_BUG;
4479       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4480       break;
4481
4482     case EL_SPACESHIP_RIGHT:
4483     case EL_SPACESHIP_UP:
4484     case EL_SPACESHIP_LEFT:
4485     case EL_SPACESHIP_DOWN:
4486       Tile[x][y] = EL_SPACESHIP;
4487       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4488       break;
4489
4490     case EL_BD_BUTTERFLY_RIGHT:
4491     case EL_BD_BUTTERFLY_UP:
4492     case EL_BD_BUTTERFLY_LEFT:
4493     case EL_BD_BUTTERFLY_DOWN:
4494       Tile[x][y] = EL_BD_BUTTERFLY;
4495       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4496       break;
4497
4498     case EL_BD_FIREFLY_RIGHT:
4499     case EL_BD_FIREFLY_UP:
4500     case EL_BD_FIREFLY_LEFT:
4501     case EL_BD_FIREFLY_DOWN:
4502       Tile[x][y] = EL_BD_FIREFLY;
4503       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4504       break;
4505
4506     case EL_PACMAN_RIGHT:
4507     case EL_PACMAN_UP:
4508     case EL_PACMAN_LEFT:
4509     case EL_PACMAN_DOWN:
4510       Tile[x][y] = EL_PACMAN;
4511       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4512       break;
4513
4514     case EL_YAMYAM_LEFT:
4515     case EL_YAMYAM_RIGHT:
4516     case EL_YAMYAM_UP:
4517     case EL_YAMYAM_DOWN:
4518       Tile[x][y] = EL_YAMYAM;
4519       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4520       break;
4521
4522     case EL_SP_SNIKSNAK:
4523       MovDir[x][y] = MV_UP;
4524       break;
4525
4526     case EL_SP_ELECTRON:
4527       MovDir[x][y] = MV_LEFT;
4528       break;
4529
4530     case EL_MOLE_LEFT:
4531     case EL_MOLE_RIGHT:
4532     case EL_MOLE_UP:
4533     case EL_MOLE_DOWN:
4534       Tile[x][y] = EL_MOLE;
4535       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4536       break;
4537
4538     case EL_SPRING_LEFT:
4539     case EL_SPRING_RIGHT:
4540       Tile[x][y] = EL_SPRING;
4541       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4542       break;
4543
4544     default:
4545       if (IS_CUSTOM_ELEMENT(element))
4546       {
4547         struct ElementInfo *ei = &element_info[element];
4548         int move_direction_initial = ei->move_direction_initial;
4549         int move_pattern = ei->move_pattern;
4550
4551         if (move_direction_initial == MV_START_PREVIOUS)
4552         {
4553           if (MovDir[x][y] != MV_NONE)
4554             return;
4555
4556           move_direction_initial = MV_START_AUTOMATIC;
4557         }
4558
4559         if (move_direction_initial == MV_START_RANDOM)
4560           MovDir[x][y] = 1 << RND(4);
4561         else if (move_direction_initial & MV_ANY_DIRECTION)
4562           MovDir[x][y] = move_direction_initial;
4563         else if (move_pattern == MV_ALL_DIRECTIONS ||
4564                  move_pattern == MV_TURNING_LEFT ||
4565                  move_pattern == MV_TURNING_RIGHT ||
4566                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4567                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4568                  move_pattern == MV_TURNING_RANDOM)
4569           MovDir[x][y] = 1 << RND(4);
4570         else if (move_pattern == MV_HORIZONTAL)
4571           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4572         else if (move_pattern == MV_VERTICAL)
4573           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4574         else if (move_pattern & MV_ANY_DIRECTION)
4575           MovDir[x][y] = element_info[element].move_pattern;
4576         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4577                  move_pattern == MV_ALONG_RIGHT_SIDE)
4578         {
4579           // use random direction as default start direction
4580           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4581             MovDir[x][y] = 1 << RND(4);
4582
4583           for (i = 0; i < NUM_DIRECTIONS; i++)
4584           {
4585             int x1 = x + xy[i][0];
4586             int y1 = y + xy[i][1];
4587
4588             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4589             {
4590               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4591                 MovDir[x][y] = direction[0][i];
4592               else
4593                 MovDir[x][y] = direction[1][i];
4594
4595               break;
4596             }
4597           }
4598         }                
4599       }
4600       else
4601       {
4602         MovDir[x][y] = 1 << RND(4);
4603
4604         if (element != EL_BUG &&
4605             element != EL_SPACESHIP &&
4606             element != EL_BD_BUTTERFLY &&
4607             element != EL_BD_FIREFLY)
4608           break;
4609
4610         for (i = 0; i < NUM_DIRECTIONS; i++)
4611         {
4612           int x1 = x + xy[i][0];
4613           int y1 = y + xy[i][1];
4614
4615           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4616           {
4617             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4618             {
4619               MovDir[x][y] = direction[0][i];
4620               break;
4621             }
4622             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4623                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4624             {
4625               MovDir[x][y] = direction[1][i];
4626               break;
4627             }
4628           }
4629         }
4630       }
4631       break;
4632   }
4633
4634   GfxDir[x][y] = MovDir[x][y];
4635 }
4636
4637 void InitAmoebaNr(int x, int y)
4638 {
4639   int i;
4640   int group_nr = AmoebaNeighbourNr(x, y);
4641
4642   if (group_nr == 0)
4643   {
4644     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4645     {
4646       if (AmoebaCnt[i] == 0)
4647       {
4648         group_nr = i;
4649         break;
4650       }
4651     }
4652   }
4653
4654   AmoebaNr[x][y] = group_nr;
4655   AmoebaCnt[group_nr]++;
4656   AmoebaCnt2[group_nr]++;
4657 }
4658
4659 static void LevelSolved(void)
4660 {
4661   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4662       game.players_still_needed > 0)
4663     return;
4664
4665   game.LevelSolved = TRUE;
4666   game.GameOver = TRUE;
4667
4668   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4669                       game_em.lev->score :
4670                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4671                       game_mm.score :
4672                       game.score);
4673   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4674                        MM_HEALTH(game_mm.laser_overload_value) :
4675                        game.health);
4676
4677   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4678   game.LevelSolved_CountingScore = game.score_final;
4679   game.LevelSolved_CountingHealth = game.health_final;
4680 }
4681
4682 void GameWon(void)
4683 {
4684   static int time_count_steps;
4685   static int time, time_final;
4686   static int score, score_final;
4687   static int health, health_final;
4688   static int game_over_delay_1 = 0;
4689   static int game_over_delay_2 = 0;
4690   static int game_over_delay_3 = 0;
4691   int game_over_delay_value_1 = 50;
4692   int game_over_delay_value_2 = 25;
4693   int game_over_delay_value_3 = 50;
4694
4695   if (!game.LevelSolved_GameWon)
4696   {
4697     int i;
4698
4699     // do not start end game actions before the player stops moving (to exit)
4700     if (local_player->active && local_player->MovPos)
4701       return;
4702
4703     game.LevelSolved_GameWon = TRUE;
4704     game.LevelSolved_SaveTape = tape.recording;
4705     game.LevelSolved_SaveScore = !tape.playing;
4706
4707     if (!tape.playing)
4708     {
4709       LevelStats_incSolved(level_nr);
4710
4711       SaveLevelSetup_SeriesInfo();
4712     }
4713
4714     if (tape.auto_play)         // tape might already be stopped here
4715       tape.auto_play_level_solved = TRUE;
4716
4717     TapeStop();
4718
4719     game_over_delay_1 = 0;
4720     game_over_delay_2 = 0;
4721     game_over_delay_3 = game_over_delay_value_3;
4722
4723     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4724     score = score_final = game.score_final;
4725     health = health_final = game.health_final;
4726
4727     if (level.score[SC_TIME_BONUS] > 0)
4728     {
4729       if (TimeLeft > 0)
4730       {
4731         time_final = 0;
4732         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4733       }
4734       else if (game.no_time_limit && TimePlayed < 999)
4735       {
4736         time_final = 999;
4737         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4738       }
4739
4740       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4741
4742       game_over_delay_1 = game_over_delay_value_1;
4743
4744       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4745       {
4746         health_final = 0;
4747         score_final += health * level.score[SC_TIME_BONUS];
4748
4749         game_over_delay_2 = game_over_delay_value_2;
4750       }
4751
4752       game.score_final = score_final;
4753       game.health_final = health_final;
4754     }
4755
4756     if (level_editor_test_game)
4757     {
4758       time = time_final;
4759       score = score_final;
4760
4761       game.LevelSolved_CountingTime = time;
4762       game.LevelSolved_CountingScore = score;
4763
4764       game_panel_controls[GAME_PANEL_TIME].value = time;
4765       game_panel_controls[GAME_PANEL_SCORE].value = score;
4766
4767       DisplayGameControlValues();
4768     }
4769
4770     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4771     {
4772       // check if last player has left the level
4773       if (game.exit_x >= 0 &&
4774           game.exit_y >= 0)
4775       {
4776         int x = game.exit_x;
4777         int y = game.exit_y;
4778         int element = Tile[x][y];
4779
4780         // close exit door after last player
4781         if ((game.all_players_gone &&
4782              (element == EL_EXIT_OPEN ||
4783               element == EL_SP_EXIT_OPEN ||
4784               element == EL_STEEL_EXIT_OPEN)) ||
4785             element == EL_EM_EXIT_OPEN ||
4786             element == EL_EM_STEEL_EXIT_OPEN)
4787         {
4788
4789           Tile[x][y] =
4790             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4791              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4792              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4793              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4794              EL_EM_STEEL_EXIT_CLOSING);
4795
4796           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4797         }
4798
4799         // player disappears
4800         DrawLevelField(x, y);
4801       }
4802
4803       for (i = 0; i < MAX_PLAYERS; i++)
4804       {
4805         struct PlayerInfo *player = &stored_player[i];
4806
4807         if (player->present)
4808         {
4809           RemovePlayer(player);
4810
4811           // player disappears
4812           DrawLevelField(player->jx, player->jy);
4813         }
4814       }
4815     }
4816
4817     PlaySound(SND_GAME_WINNING);
4818   }
4819
4820   if (game_over_delay_1 > 0)
4821   {
4822     game_over_delay_1--;
4823
4824     return;
4825   }
4826
4827   if (time != time_final)
4828   {
4829     int time_to_go = ABS(time_final - time);
4830     int time_count_dir = (time < time_final ? +1 : -1);
4831
4832     if (time_to_go < time_count_steps)
4833       time_count_steps = 1;
4834
4835     time  += time_count_steps * time_count_dir;
4836     score += time_count_steps * level.score[SC_TIME_BONUS];
4837
4838     game.LevelSolved_CountingTime = time;
4839     game.LevelSolved_CountingScore = score;
4840
4841     game_panel_controls[GAME_PANEL_TIME].value = time;
4842     game_panel_controls[GAME_PANEL_SCORE].value = score;
4843
4844     DisplayGameControlValues();
4845
4846     if (time == time_final)
4847       StopSound(SND_GAME_LEVELTIME_BONUS);
4848     else if (setup.sound_loops)
4849       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4850     else
4851       PlaySound(SND_GAME_LEVELTIME_BONUS);
4852
4853     return;
4854   }
4855
4856   if (game_over_delay_2 > 0)
4857   {
4858     game_over_delay_2--;
4859
4860     return;
4861   }
4862
4863   if (health != health_final)
4864   {
4865     int health_count_dir = (health < health_final ? +1 : -1);
4866
4867     health += health_count_dir;
4868     score  += level.score[SC_TIME_BONUS];
4869
4870     game.LevelSolved_CountingHealth = health;
4871     game.LevelSolved_CountingScore = score;
4872
4873     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4874     game_panel_controls[GAME_PANEL_SCORE].value = score;
4875
4876     DisplayGameControlValues();
4877
4878     if (health == health_final)
4879       StopSound(SND_GAME_LEVELTIME_BONUS);
4880     else if (setup.sound_loops)
4881       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4882     else
4883       PlaySound(SND_GAME_LEVELTIME_BONUS);
4884
4885     return;
4886   }
4887
4888   game.panel.active = FALSE;
4889
4890   if (game_over_delay_3 > 0)
4891   {
4892     game_over_delay_3--;
4893
4894     return;
4895   }
4896
4897   GameEnd();
4898 }
4899
4900 void GameEnd(void)
4901 {
4902   // used instead of "level_nr" (needed for network games)
4903   int last_level_nr = levelset.level_nr;
4904   int hi_pos;
4905
4906   game.LevelSolved_GameEnd = TRUE;
4907
4908   if (game.LevelSolved_SaveTape)
4909   {
4910     // make sure that request dialog to save tape does not open door again
4911     if (!global.use_envelope_request)
4912       CloseDoor(DOOR_CLOSE_1);
4913
4914     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4915   }
4916
4917   // if no tape is to be saved, close both doors simultaneously
4918   CloseDoor(DOOR_CLOSE_ALL);
4919
4920   if (level_editor_test_game)
4921   {
4922     SetGameStatus(GAME_MODE_MAIN);
4923
4924     DrawMainMenu();
4925
4926     return;
4927   }
4928
4929   if (!game.LevelSolved_SaveScore)
4930   {
4931     SetGameStatus(GAME_MODE_MAIN);
4932
4933     DrawMainMenu();
4934
4935     return;
4936   }
4937
4938   if (level_nr == leveldir_current->handicap_level)
4939   {
4940     leveldir_current->handicap_level++;
4941
4942     SaveLevelSetup_SeriesInfo();
4943   }
4944
4945   if (setup.increment_levels &&
4946       level_nr < leveldir_current->last_level &&
4947       !network_playing)
4948   {
4949     level_nr++;         // advance to next level
4950     TapeErase();        // start with empty tape
4951
4952     if (setup.auto_play_next_level)
4953     {
4954       LoadLevel(level_nr);
4955
4956       SaveLevelSetup_SeriesInfo();
4957     }
4958   }
4959
4960   hi_pos = NewHiScore(last_level_nr);
4961
4962   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4963   {
4964     SetGameStatus(GAME_MODE_SCORES);
4965
4966     DrawHallOfFame(last_level_nr, hi_pos);
4967   }
4968   else if (setup.auto_play_next_level && setup.increment_levels &&
4969            last_level_nr < leveldir_current->last_level &&
4970            !network_playing)
4971   {
4972     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4973   }
4974   else
4975   {
4976     SetGameStatus(GAME_MODE_MAIN);
4977
4978     DrawMainMenu();
4979   }
4980 }
4981
4982 int NewHiScore(int level_nr)
4983 {
4984   int k, l;
4985   int position = -1;
4986   boolean one_score_entry_per_name = !program.many_scores_per_name;
4987
4988   LoadScore(level_nr);
4989
4990   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4991       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4992     return -1;
4993
4994   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4995   {
4996     if (game.score_final > highscore[k].Score)
4997     {
4998       // player has made it to the hall of fame
4999
5000       if (k < MAX_SCORE_ENTRIES - 1)
5001       {
5002         int m = MAX_SCORE_ENTRIES - 1;
5003
5004         if (one_score_entry_per_name)
5005         {
5006           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5007             if (strEqual(setup.player_name, highscore[l].Name))
5008               m = l;
5009
5010           if (m == k)   // player's new highscore overwrites his old one
5011             goto put_into_list;
5012         }
5013
5014         for (l = m; l > k; l--)
5015         {
5016           strcpy(highscore[l].Name, highscore[l - 1].Name);
5017           highscore[l].Score = highscore[l - 1].Score;
5018         }
5019       }
5020
5021       put_into_list:
5022
5023       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5024       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5025       highscore[k].Score = game.score_final;
5026       position = k;
5027
5028       break;
5029     }
5030     else if (one_score_entry_per_name &&
5031              !strncmp(setup.player_name, highscore[k].Name,
5032                       MAX_PLAYER_NAME_LEN))
5033       break;    // player already there with a higher score
5034   }
5035
5036   if (position >= 0) 
5037     SaveScore(level_nr);
5038
5039   return position;
5040 }
5041
5042 static int getElementMoveStepsizeExt(int x, int y, int direction)
5043 {
5044   int element = Tile[x][y];
5045   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5046   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5047   int horiz_move = (dx != 0);
5048   int sign = (horiz_move ? dx : dy);
5049   int step = sign * element_info[element].move_stepsize;
5050
5051   // special values for move stepsize for spring and things on conveyor belt
5052   if (horiz_move)
5053   {
5054     if (CAN_FALL(element) &&
5055         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5056       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5057     else if (element == EL_SPRING)
5058       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5059   }
5060
5061   return step;
5062 }
5063
5064 static int getElementMoveStepsize(int x, int y)
5065 {
5066   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5067 }
5068
5069 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5070 {
5071   if (player->GfxAction != action || player->GfxDir != dir)
5072   {
5073     player->GfxAction = action;
5074     player->GfxDir = dir;
5075     player->Frame = 0;
5076     player->StepFrame = 0;
5077   }
5078 }
5079
5080 static void ResetGfxFrame(int x, int y)
5081 {
5082   // profiling showed that "autotest" spends 10~20% of its time in this function
5083   if (DrawingDeactivatedField())
5084     return;
5085
5086   int element = Tile[x][y];
5087   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5088
5089   if (graphic_info[graphic].anim_global_sync)
5090     GfxFrame[x][y] = FrameCounter;
5091   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5092     GfxFrame[x][y] = CustomValue[x][y];
5093   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5094     GfxFrame[x][y] = element_info[element].collect_score;
5095   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5096     GfxFrame[x][y] = ChangeDelay[x][y];
5097 }
5098
5099 static void ResetGfxAnimation(int x, int y)
5100 {
5101   GfxAction[x][y] = ACTION_DEFAULT;
5102   GfxDir[x][y] = MovDir[x][y];
5103   GfxFrame[x][y] = 0;
5104
5105   ResetGfxFrame(x, y);
5106 }
5107
5108 static void ResetRandomAnimationValue(int x, int y)
5109 {
5110   GfxRandom[x][y] = INIT_GFX_RANDOM();
5111 }
5112
5113 static void InitMovingField(int x, int y, int direction)
5114 {
5115   int element = Tile[x][y];
5116   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5117   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5118   int newx = x + dx;
5119   int newy = y + dy;
5120   boolean is_moving_before, is_moving_after;
5121
5122   // check if element was/is moving or being moved before/after mode change
5123   is_moving_before = (WasJustMoving[x][y] != 0);
5124   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5125
5126   // reset animation only for moving elements which change direction of moving
5127   // or which just started or stopped moving
5128   // (else CEs with property "can move" / "not moving" are reset each frame)
5129   if (is_moving_before != is_moving_after ||
5130       direction != MovDir[x][y])
5131     ResetGfxAnimation(x, y);
5132
5133   MovDir[x][y] = direction;
5134   GfxDir[x][y] = direction;
5135
5136   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5137                      direction == MV_DOWN && CAN_FALL(element) ?
5138                      ACTION_FALLING : ACTION_MOVING);
5139
5140   // this is needed for CEs with property "can move" / "not moving"
5141
5142   if (is_moving_after)
5143   {
5144     if (Tile[newx][newy] == EL_EMPTY)
5145       Tile[newx][newy] = EL_BLOCKED;
5146
5147     MovDir[newx][newy] = MovDir[x][y];
5148
5149     CustomValue[newx][newy] = CustomValue[x][y];
5150
5151     GfxFrame[newx][newy] = GfxFrame[x][y];
5152     GfxRandom[newx][newy] = GfxRandom[x][y];
5153     GfxAction[newx][newy] = GfxAction[x][y];
5154     GfxDir[newx][newy] = GfxDir[x][y];
5155   }
5156 }
5157
5158 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5159 {
5160   int direction = MovDir[x][y];
5161   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5162   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5163
5164   *goes_to_x = newx;
5165   *goes_to_y = newy;
5166 }
5167
5168 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5169 {
5170   int oldx = x, oldy = y;
5171   int direction = MovDir[x][y];
5172
5173   if (direction == MV_LEFT)
5174     oldx++;
5175   else if (direction == MV_RIGHT)
5176     oldx--;
5177   else if (direction == MV_UP)
5178     oldy++;
5179   else if (direction == MV_DOWN)
5180     oldy--;
5181
5182   *comes_from_x = oldx;
5183   *comes_from_y = oldy;
5184 }
5185
5186 static int MovingOrBlocked2Element(int x, int y)
5187 {
5188   int element = Tile[x][y];
5189
5190   if (element == EL_BLOCKED)
5191   {
5192     int oldx, oldy;
5193
5194     Blocked2Moving(x, y, &oldx, &oldy);
5195     return Tile[oldx][oldy];
5196   }
5197   else
5198     return element;
5199 }
5200
5201 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5202 {
5203   // like MovingOrBlocked2Element(), but if element is moving
5204   // and (x,y) is the field the moving element is just leaving,
5205   // return EL_BLOCKED instead of the element value
5206   int element = Tile[x][y];
5207
5208   if (IS_MOVING(x, y))
5209   {
5210     if (element == EL_BLOCKED)
5211     {
5212       int oldx, oldy;
5213
5214       Blocked2Moving(x, y, &oldx, &oldy);
5215       return Tile[oldx][oldy];
5216     }
5217     else
5218       return EL_BLOCKED;
5219   }
5220   else
5221     return element;
5222 }
5223
5224 static void RemoveField(int x, int y)
5225 {
5226   Tile[x][y] = EL_EMPTY;
5227
5228   MovPos[x][y] = 0;
5229   MovDir[x][y] = 0;
5230   MovDelay[x][y] = 0;
5231
5232   CustomValue[x][y] = 0;
5233
5234   AmoebaNr[x][y] = 0;
5235   ChangeDelay[x][y] = 0;
5236   ChangePage[x][y] = -1;
5237   Pushed[x][y] = FALSE;
5238
5239   GfxElement[x][y] = EL_UNDEFINED;
5240   GfxAction[x][y] = ACTION_DEFAULT;
5241   GfxDir[x][y] = MV_NONE;
5242 }
5243
5244 static void RemoveMovingField(int x, int y)
5245 {
5246   int oldx = x, oldy = y, newx = x, newy = y;
5247   int element = Tile[x][y];
5248   int next_element = EL_UNDEFINED;
5249
5250   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5251     return;
5252
5253   if (IS_MOVING(x, y))
5254   {
5255     Moving2Blocked(x, y, &newx, &newy);
5256
5257     if (Tile[newx][newy] != EL_BLOCKED)
5258     {
5259       // element is moving, but target field is not free (blocked), but
5260       // already occupied by something different (example: acid pool);
5261       // in this case, only remove the moving field, but not the target
5262
5263       RemoveField(oldx, oldy);
5264
5265       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5266
5267       TEST_DrawLevelField(oldx, oldy);
5268
5269       return;
5270     }
5271   }
5272   else if (element == EL_BLOCKED)
5273   {
5274     Blocked2Moving(x, y, &oldx, &oldy);
5275     if (!IS_MOVING(oldx, oldy))
5276       return;
5277   }
5278
5279   if (element == EL_BLOCKED &&
5280       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5281        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5282        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5283        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5284        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5285        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5286     next_element = get_next_element(Tile[oldx][oldy]);
5287
5288   RemoveField(oldx, oldy);
5289   RemoveField(newx, newy);
5290
5291   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5292
5293   if (next_element != EL_UNDEFINED)
5294     Tile[oldx][oldy] = next_element;
5295
5296   TEST_DrawLevelField(oldx, oldy);
5297   TEST_DrawLevelField(newx, newy);
5298 }
5299
5300 void DrawDynamite(int x, int y)
5301 {
5302   int sx = SCREENX(x), sy = SCREENY(y);
5303   int graphic = el2img(Tile[x][y]);
5304   int frame;
5305
5306   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5307     return;
5308
5309   if (IS_WALKABLE_INSIDE(Back[x][y]))
5310     return;
5311
5312   if (Back[x][y])
5313     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5314   else if (Store[x][y])
5315     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5316
5317   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5318
5319   if (Back[x][y] || Store[x][y])
5320     DrawGraphicThruMask(sx, sy, graphic, frame);
5321   else
5322     DrawGraphic(sx, sy, graphic, frame);
5323 }
5324
5325 static void CheckDynamite(int x, int y)
5326 {
5327   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5328   {
5329     MovDelay[x][y]--;
5330
5331     if (MovDelay[x][y] != 0)
5332     {
5333       DrawDynamite(x, y);
5334       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5335
5336       return;
5337     }
5338   }
5339
5340   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5341
5342   Bang(x, y);
5343 }
5344
5345 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5346 {
5347   boolean num_checked_players = 0;
5348   int i;
5349
5350   for (i = 0; i < MAX_PLAYERS; i++)
5351   {
5352     if (stored_player[i].active)
5353     {
5354       int sx = stored_player[i].jx;
5355       int sy = stored_player[i].jy;
5356
5357       if (num_checked_players == 0)
5358       {
5359         *sx1 = *sx2 = sx;
5360         *sy1 = *sy2 = sy;
5361       }
5362       else
5363       {
5364         *sx1 = MIN(*sx1, sx);
5365         *sy1 = MIN(*sy1, sy);
5366         *sx2 = MAX(*sx2, sx);
5367         *sy2 = MAX(*sy2, sy);
5368       }
5369
5370       num_checked_players++;
5371     }
5372   }
5373 }
5374
5375 static boolean checkIfAllPlayersFitToScreen_RND(void)
5376 {
5377   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5378
5379   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5380
5381   return (sx2 - sx1 < SCR_FIELDX &&
5382           sy2 - sy1 < SCR_FIELDY);
5383 }
5384
5385 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5386 {
5387   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5388
5389   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5390
5391   *sx = (sx1 + sx2) / 2;
5392   *sy = (sy1 + sy2) / 2;
5393 }
5394
5395 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5396                                boolean center_screen, boolean quick_relocation)
5397 {
5398   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5399   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400   boolean no_delay = (tape.warp_forward);
5401   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5403   int new_scroll_x, new_scroll_y;
5404
5405   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5406   {
5407     // case 1: quick relocation inside visible screen (without scrolling)
5408
5409     RedrawPlayfield();
5410
5411     return;
5412   }
5413
5414   if (!level.shifted_relocation || center_screen)
5415   {
5416     // relocation _with_ centering of screen
5417
5418     new_scroll_x = SCROLL_POSITION_X(x);
5419     new_scroll_y = SCROLL_POSITION_Y(y);
5420   }
5421   else
5422   {
5423     // relocation _without_ centering of screen
5424
5425     int center_scroll_x = SCROLL_POSITION_X(old_x);
5426     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5427     int offset_x = x + (scroll_x - center_scroll_x);
5428     int offset_y = y + (scroll_y - center_scroll_y);
5429
5430     // for new screen position, apply previous offset to center position
5431     new_scroll_x = SCROLL_POSITION_X(offset_x);
5432     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5433   }
5434
5435   if (quick_relocation)
5436   {
5437     // case 2: quick relocation (redraw without visible scrolling)
5438
5439     scroll_x = new_scroll_x;
5440     scroll_y = new_scroll_y;
5441
5442     RedrawPlayfield();
5443
5444     return;
5445   }
5446
5447   // case 3: visible relocation (with scrolling to new position)
5448
5449   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5450
5451   SetVideoFrameDelay(wait_delay_value);
5452
5453   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5454   {
5455     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5456     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5457
5458     if (dx == 0 && dy == 0)             // no scrolling needed at all
5459       break;
5460
5461     scroll_x -= dx;
5462     scroll_y -= dy;
5463
5464     // set values for horizontal/vertical screen scrolling (half tile size)
5465     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5466     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5467     int pos_x = dx * TILEX / 2;
5468     int pos_y = dy * TILEY / 2;
5469     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5470     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5471
5472     ScrollLevel(dx, dy);
5473     DrawAllPlayers();
5474
5475     // scroll in two steps of half tile size to make things smoother
5476     BlitScreenToBitmapExt_RND(window, fx, fy);
5477
5478     // scroll second step to align at full tile size
5479     BlitScreenToBitmap(window);
5480   }
5481
5482   DrawAllPlayers();
5483   BackToFront();
5484
5485   SetVideoFrameDelay(frame_delay_value_old);
5486 }
5487
5488 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5489 {
5490   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5491   int player_nr = GET_PLAYER_NR(el_player);
5492   struct PlayerInfo *player = &stored_player[player_nr];
5493   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5494   boolean no_delay = (tape.warp_forward);
5495   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5496   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5497   int old_jx = player->jx;
5498   int old_jy = player->jy;
5499   int old_element = Tile[old_jx][old_jy];
5500   int element = Tile[jx][jy];
5501   boolean player_relocated = (old_jx != jx || old_jy != jy);
5502
5503   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5504   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5505   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5506   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5507   int leave_side_horiz = move_dir_horiz;
5508   int leave_side_vert  = move_dir_vert;
5509   int enter_side = enter_side_horiz | enter_side_vert;
5510   int leave_side = leave_side_horiz | leave_side_vert;
5511
5512   if (player->buried)           // do not reanimate dead player
5513     return;
5514
5515   if (!player_relocated)        // no need to relocate the player
5516     return;
5517
5518   if (IS_PLAYER(jx, jy))        // player already placed at new position
5519   {
5520     RemoveField(jx, jy);        // temporarily remove newly placed player
5521     DrawLevelField(jx, jy);
5522   }
5523
5524   if (player->present)
5525   {
5526     while (player->MovPos)
5527     {
5528       ScrollPlayer(player, SCROLL_GO_ON);
5529       ScrollScreen(NULL, SCROLL_GO_ON);
5530
5531       AdvanceFrameAndPlayerCounters(player->index_nr);
5532
5533       DrawPlayer(player);
5534
5535       BackToFront_WithFrameDelay(wait_delay_value);
5536     }
5537
5538     DrawPlayer(player);         // needed here only to cleanup last field
5539     DrawLevelField(player->jx, player->jy);     // remove player graphic
5540
5541     player->is_moving = FALSE;
5542   }
5543
5544   if (IS_CUSTOM_ELEMENT(old_element))
5545     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5546                                CE_LEFT_BY_PLAYER,
5547                                player->index_bit, leave_side);
5548
5549   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5550                                       CE_PLAYER_LEAVES_X,
5551                                       player->index_bit, leave_side);
5552
5553   Tile[jx][jy] = el_player;
5554   InitPlayerField(jx, jy, el_player, TRUE);
5555
5556   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5557      possible that the relocation target field did not contain a player element,
5558      but a walkable element, to which the new player was relocated -- in this
5559      case, restore that (already initialized!) element on the player field */
5560   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5561   {
5562     Tile[jx][jy] = element;     // restore previously existing element
5563   }
5564
5565   // only visually relocate centered player
5566   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5567                      FALSE, level.instant_relocation);
5568
5569   TestIfPlayerTouchesBadThing(jx, jy);
5570   TestIfPlayerTouchesCustomElement(jx, jy);
5571
5572   if (IS_CUSTOM_ELEMENT(element))
5573     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5574                                player->index_bit, enter_side);
5575
5576   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5577                                       player->index_bit, enter_side);
5578
5579   if (player->is_switching)
5580   {
5581     /* ensure that relocation while still switching an element does not cause
5582        a new element to be treated as also switched directly after relocation
5583        (this is important for teleporter switches that teleport the player to
5584        a place where another teleporter switch is in the same direction, which
5585        would then incorrectly be treated as immediately switched before the
5586        direction key that caused the switch was released) */
5587
5588     player->switch_x += jx - old_jx;
5589     player->switch_y += jy - old_jy;
5590   }
5591 }
5592
5593 static void Explode(int ex, int ey, int phase, int mode)
5594 {
5595   int x, y;
5596   int last_phase;
5597   int border_element;
5598
5599   // !!! eliminate this variable !!!
5600   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5601
5602   if (game.explosions_delayed)
5603   {
5604     ExplodeField[ex][ey] = mode;
5605     return;
5606   }
5607
5608   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5609   {
5610     int center_element = Tile[ex][ey];
5611     int artwork_element, explosion_element;     // set these values later
5612
5613     // remove things displayed in background while burning dynamite
5614     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5615       Back[ex][ey] = 0;
5616
5617     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5618     {
5619       // put moving element to center field (and let it explode there)
5620       center_element = MovingOrBlocked2Element(ex, ey);
5621       RemoveMovingField(ex, ey);
5622       Tile[ex][ey] = center_element;
5623     }
5624
5625     // now "center_element" is finally determined -- set related values now
5626     artwork_element = center_element;           // for custom player artwork
5627     explosion_element = center_element;         // for custom player artwork
5628
5629     if (IS_PLAYER(ex, ey))
5630     {
5631       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5632
5633       artwork_element = stored_player[player_nr].artwork_element;
5634
5635       if (level.use_explosion_element[player_nr])
5636       {
5637         explosion_element = level.explosion_element[player_nr];
5638         artwork_element = explosion_element;
5639       }
5640     }
5641
5642     if (mode == EX_TYPE_NORMAL ||
5643         mode == EX_TYPE_CENTER ||
5644         mode == EX_TYPE_CROSS)
5645       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5646
5647     last_phase = element_info[explosion_element].explosion_delay + 1;
5648
5649     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5650     {
5651       int xx = x - ex + 1;
5652       int yy = y - ey + 1;
5653       int element;
5654
5655       if (!IN_LEV_FIELD(x, y) ||
5656           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5657           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5658         continue;
5659
5660       element = Tile[x][y];
5661
5662       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5663       {
5664         element = MovingOrBlocked2Element(x, y);
5665
5666         if (!IS_EXPLOSION_PROOF(element))
5667           RemoveMovingField(x, y);
5668       }
5669
5670       // indestructible elements can only explode in center (but not flames)
5671       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5672                                            mode == EX_TYPE_BORDER)) ||
5673           element == EL_FLAMES)
5674         continue;
5675
5676       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5677          behaviour, for example when touching a yamyam that explodes to rocks
5678          with active deadly shield, a rock is created under the player !!! */
5679       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5680 #if 0
5681       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5682           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5683            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5684 #else
5685       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5686 #endif
5687       {
5688         if (IS_ACTIVE_BOMB(element))
5689         {
5690           // re-activate things under the bomb like gate or penguin
5691           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5692           Back[x][y] = 0;
5693         }
5694
5695         continue;
5696       }
5697
5698       // save walkable background elements while explosion on same tile
5699       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5700           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5701         Back[x][y] = element;
5702
5703       // ignite explodable elements reached by other explosion
5704       if (element == EL_EXPLOSION)
5705         element = Store2[x][y];
5706
5707       if (AmoebaNr[x][y] &&
5708           (element == EL_AMOEBA_FULL ||
5709            element == EL_BD_AMOEBA ||
5710            element == EL_AMOEBA_GROWING))
5711       {
5712         AmoebaCnt[AmoebaNr[x][y]]--;
5713         AmoebaCnt2[AmoebaNr[x][y]]--;
5714       }
5715
5716       RemoveField(x, y);
5717
5718       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5719       {
5720         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5721
5722         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5723
5724         if (PLAYERINFO(ex, ey)->use_murphy)
5725           Store[x][y] = EL_EMPTY;
5726       }
5727
5728       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5729       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5730       else if (ELEM_IS_PLAYER(center_element))
5731         Store[x][y] = EL_EMPTY;
5732       else if (center_element == EL_YAMYAM)
5733         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5734       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5735         Store[x][y] = element_info[center_element].content.e[xx][yy];
5736 #if 1
5737       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5738       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5739       // otherwise) -- FIX THIS !!!
5740       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5741         Store[x][y] = element_info[element].content.e[1][1];
5742 #else
5743       else if (!CAN_EXPLODE(element))
5744         Store[x][y] = element_info[element].content.e[1][1];
5745 #endif
5746       else
5747         Store[x][y] = EL_EMPTY;
5748
5749       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5750           center_element == EL_AMOEBA_TO_DIAMOND)
5751         Store2[x][y] = element;
5752
5753       Tile[x][y] = EL_EXPLOSION;
5754       GfxElement[x][y] = artwork_element;
5755
5756       ExplodePhase[x][y] = 1;
5757       ExplodeDelay[x][y] = last_phase;
5758
5759       Stop[x][y] = TRUE;
5760     }
5761
5762     if (center_element == EL_YAMYAM)
5763       game.yamyam_content_nr =
5764         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5765
5766     return;
5767   }
5768
5769   if (Stop[ex][ey])
5770     return;
5771
5772   x = ex;
5773   y = ey;
5774
5775   if (phase == 1)
5776     GfxFrame[x][y] = 0;         // restart explosion animation
5777
5778   last_phase = ExplodeDelay[x][y];
5779
5780   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5781
5782   // this can happen if the player leaves an explosion just in time
5783   if (GfxElement[x][y] == EL_UNDEFINED)
5784     GfxElement[x][y] = EL_EMPTY;
5785
5786   border_element = Store2[x][y];
5787   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5788     border_element = StorePlayer[x][y];
5789
5790   if (phase == element_info[border_element].ignition_delay ||
5791       phase == last_phase)
5792   {
5793     boolean border_explosion = FALSE;
5794
5795     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5796         !PLAYER_EXPLOSION_PROTECTED(x, y))
5797     {
5798       KillPlayerUnlessExplosionProtected(x, y);
5799       border_explosion = TRUE;
5800     }
5801     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5802     {
5803       Tile[x][y] = Store2[x][y];
5804       Store2[x][y] = 0;
5805       Bang(x, y);
5806       border_explosion = TRUE;
5807     }
5808     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5809     {
5810       AmoebaToDiamond(x, y);
5811       Store2[x][y] = 0;
5812       border_explosion = TRUE;
5813     }
5814
5815     // if an element just explodes due to another explosion (chain-reaction),
5816     // do not immediately end the new explosion when it was the last frame of
5817     // the explosion (as it would be done in the following "if"-statement!)
5818     if (border_explosion && phase == last_phase)
5819       return;
5820   }
5821
5822   if (phase == last_phase)
5823   {
5824     int element;
5825
5826     element = Tile[x][y] = Store[x][y];
5827     Store[x][y] = Store2[x][y] = 0;
5828     GfxElement[x][y] = EL_UNDEFINED;
5829
5830     // player can escape from explosions and might therefore be still alive
5831     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5832         element <= EL_PLAYER_IS_EXPLODING_4)
5833     {
5834       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5835       int explosion_element = EL_PLAYER_1 + player_nr;
5836       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5837       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5838
5839       if (level.use_explosion_element[player_nr])
5840         explosion_element = level.explosion_element[player_nr];
5841
5842       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5843                     element_info[explosion_element].content.e[xx][yy]);
5844     }
5845
5846     // restore probably existing indestructible background element
5847     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5848       element = Tile[x][y] = Back[x][y];
5849     Back[x][y] = 0;
5850
5851     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5852     GfxDir[x][y] = MV_NONE;
5853     ChangeDelay[x][y] = 0;
5854     ChangePage[x][y] = -1;
5855
5856     CustomValue[x][y] = 0;
5857
5858     InitField_WithBug2(x, y, FALSE);
5859
5860     TEST_DrawLevelField(x, y);
5861
5862     TestIfElementTouchesCustomElement(x, y);
5863
5864     if (GFX_CRUMBLED(element))
5865       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5866
5867     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5868       StorePlayer[x][y] = 0;
5869
5870     if (ELEM_IS_PLAYER(element))
5871       RelocatePlayer(x, y, element);
5872   }
5873   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5874   {
5875     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5876     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5877
5878     if (phase == delay)
5879       TEST_DrawLevelFieldCrumbled(x, y);
5880
5881     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5882     {
5883       DrawLevelElement(x, y, Back[x][y]);
5884       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5885     }
5886     else if (IS_WALKABLE_UNDER(Back[x][y]))
5887     {
5888       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5889       DrawLevelElementThruMask(x, y, Back[x][y]);
5890     }
5891     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5892       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5893   }
5894 }
5895
5896 static void DynaExplode(int ex, int ey)
5897 {
5898   int i, j;
5899   int dynabomb_element = Tile[ex][ey];
5900   int dynabomb_size = 1;
5901   boolean dynabomb_xl = FALSE;
5902   struct PlayerInfo *player;
5903   static int xy[4][2] =
5904   {
5905     { 0, -1 },
5906     { -1, 0 },
5907     { +1, 0 },
5908     { 0, +1 }
5909   };
5910
5911   if (IS_ACTIVE_BOMB(dynabomb_element))
5912   {
5913     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5914     dynabomb_size = player->dynabomb_size;
5915     dynabomb_xl = player->dynabomb_xl;
5916     player->dynabombs_left++;
5917   }
5918
5919   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5920
5921   for (i = 0; i < NUM_DIRECTIONS; i++)
5922   {
5923     for (j = 1; j <= dynabomb_size; j++)
5924     {
5925       int x = ex + j * xy[i][0];
5926       int y = ey + j * xy[i][1];
5927       int element;
5928
5929       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5930         break;
5931
5932       element = Tile[x][y];
5933
5934       // do not restart explosions of fields with active bombs
5935       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5936         continue;
5937
5938       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5939
5940       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5941           !IS_DIGGABLE(element) && !dynabomb_xl)
5942         break;
5943     }
5944   }
5945 }
5946
5947 void Bang(int x, int y)
5948 {
5949   int element = MovingOrBlocked2Element(x, y);
5950   int explosion_type = EX_TYPE_NORMAL;
5951
5952   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5953   {
5954     struct PlayerInfo *player = PLAYERINFO(x, y);
5955
5956     element = Tile[x][y] = player->initial_element;
5957
5958     if (level.use_explosion_element[player->index_nr])
5959     {
5960       int explosion_element = level.explosion_element[player->index_nr];
5961
5962       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5963         explosion_type = EX_TYPE_CROSS;
5964       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5965         explosion_type = EX_TYPE_CENTER;
5966     }
5967   }
5968
5969   switch (element)
5970   {
5971     case EL_BUG:
5972     case EL_SPACESHIP:
5973     case EL_BD_BUTTERFLY:
5974     case EL_BD_FIREFLY:
5975     case EL_YAMYAM:
5976     case EL_DARK_YAMYAM:
5977     case EL_ROBOT:
5978     case EL_PACMAN:
5979     case EL_MOLE:
5980       RaiseScoreElement(element);
5981       break;
5982
5983     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5984     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5985     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5986     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5987     case EL_DYNABOMB_INCREASE_NUMBER:
5988     case EL_DYNABOMB_INCREASE_SIZE:
5989     case EL_DYNABOMB_INCREASE_POWER:
5990       explosion_type = EX_TYPE_DYNA;
5991       break;
5992
5993     case EL_DC_LANDMINE:
5994       explosion_type = EX_TYPE_CENTER;
5995       break;
5996
5997     case EL_PENGUIN:
5998     case EL_LAMP:
5999     case EL_LAMP_ACTIVE:
6000     case EL_AMOEBA_TO_DIAMOND:
6001       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6002         explosion_type = EX_TYPE_CENTER;
6003       break;
6004
6005     default:
6006       if (element_info[element].explosion_type == EXPLODES_CROSS)
6007         explosion_type = EX_TYPE_CROSS;
6008       else if (element_info[element].explosion_type == EXPLODES_1X1)
6009         explosion_type = EX_TYPE_CENTER;
6010       break;
6011   }
6012
6013   if (explosion_type == EX_TYPE_DYNA)
6014     DynaExplode(x, y);
6015   else
6016     Explode(x, y, EX_PHASE_START, explosion_type);
6017
6018   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6019 }
6020
6021 static void SplashAcid(int x, int y)
6022 {
6023   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6024       (!IN_LEV_FIELD(x - 1, y - 2) ||
6025        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6026     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6027
6028   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6029       (!IN_LEV_FIELD(x + 1, y - 2) ||
6030        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6031     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6032
6033   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6034 }
6035
6036 static void InitBeltMovement(void)
6037 {
6038   static int belt_base_element[4] =
6039   {
6040     EL_CONVEYOR_BELT_1_LEFT,
6041     EL_CONVEYOR_BELT_2_LEFT,
6042     EL_CONVEYOR_BELT_3_LEFT,
6043     EL_CONVEYOR_BELT_4_LEFT
6044   };
6045   static int belt_base_active_element[4] =
6046   {
6047     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6048     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6049     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6050     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6051   };
6052
6053   int x, y, i, j;
6054
6055   // set frame order for belt animation graphic according to belt direction
6056   for (i = 0; i < NUM_BELTS; i++)
6057   {
6058     int belt_nr = i;
6059
6060     for (j = 0; j < NUM_BELT_PARTS; j++)
6061     {
6062       int element = belt_base_active_element[belt_nr] + j;
6063       int graphic_1 = el2img(element);
6064       int graphic_2 = el2panelimg(element);
6065
6066       if (game.belt_dir[i] == MV_LEFT)
6067       {
6068         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6069         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6070       }
6071       else
6072       {
6073         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6074         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6075       }
6076     }
6077   }
6078
6079   SCAN_PLAYFIELD(x, y)
6080   {
6081     int element = Tile[x][y];
6082
6083     for (i = 0; i < NUM_BELTS; i++)
6084     {
6085       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6086       {
6087         int e_belt_nr = getBeltNrFromBeltElement(element);
6088         int belt_nr = i;
6089
6090         if (e_belt_nr == belt_nr)
6091         {
6092           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6093
6094           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6095         }
6096       }
6097     }
6098   }
6099 }
6100
6101 static void ToggleBeltSwitch(int x, int y)
6102 {
6103   static int belt_base_element[4] =
6104   {
6105     EL_CONVEYOR_BELT_1_LEFT,
6106     EL_CONVEYOR_BELT_2_LEFT,
6107     EL_CONVEYOR_BELT_3_LEFT,
6108     EL_CONVEYOR_BELT_4_LEFT
6109   };
6110   static int belt_base_active_element[4] =
6111   {
6112     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6113     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6114     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6115     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6116   };
6117   static int belt_base_switch_element[4] =
6118   {
6119     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6120     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6121     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6122     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6123   };
6124   static int belt_move_dir[4] =
6125   {
6126     MV_LEFT,
6127     MV_NONE,
6128     MV_RIGHT,
6129     MV_NONE,
6130   };
6131
6132   int element = Tile[x][y];
6133   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6134   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6135   int belt_dir = belt_move_dir[belt_dir_nr];
6136   int xx, yy, i;
6137
6138   if (!IS_BELT_SWITCH(element))
6139     return;
6140
6141   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6142   game.belt_dir[belt_nr] = belt_dir;
6143
6144   if (belt_dir_nr == 3)
6145     belt_dir_nr = 1;
6146
6147   // set frame order for belt animation graphic according to belt direction
6148   for (i = 0; i < NUM_BELT_PARTS; i++)
6149   {
6150     int element = belt_base_active_element[belt_nr] + i;
6151     int graphic_1 = el2img(element);
6152     int graphic_2 = el2panelimg(element);
6153
6154     if (belt_dir == MV_LEFT)
6155     {
6156       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6157       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6158     }
6159     else
6160     {
6161       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6162       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6163     }
6164   }
6165
6166   SCAN_PLAYFIELD(xx, yy)
6167   {
6168     int element = Tile[xx][yy];
6169
6170     if (IS_BELT_SWITCH(element))
6171     {
6172       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6173
6174       if (e_belt_nr == belt_nr)
6175       {
6176         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6177         TEST_DrawLevelField(xx, yy);
6178       }
6179     }
6180     else if (IS_BELT(element) && belt_dir != MV_NONE)
6181     {
6182       int e_belt_nr = getBeltNrFromBeltElement(element);
6183
6184       if (e_belt_nr == belt_nr)
6185       {
6186         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6187
6188         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6189         TEST_DrawLevelField(xx, yy);
6190       }
6191     }
6192     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6193     {
6194       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6195
6196       if (e_belt_nr == belt_nr)
6197       {
6198         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6199
6200         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6201         TEST_DrawLevelField(xx, yy);
6202       }
6203     }
6204   }
6205 }
6206
6207 static void ToggleSwitchgateSwitch(int x, int y)
6208 {
6209   int xx, yy;
6210
6211   game.switchgate_pos = !game.switchgate_pos;
6212
6213   SCAN_PLAYFIELD(xx, yy)
6214   {
6215     int element = Tile[xx][yy];
6216
6217     if (element == EL_SWITCHGATE_SWITCH_UP)
6218     {
6219       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6220       TEST_DrawLevelField(xx, yy);
6221     }
6222     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6223     {
6224       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6225       TEST_DrawLevelField(xx, yy);
6226     }
6227     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6228     {
6229       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6230       TEST_DrawLevelField(xx, yy);
6231     }
6232     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6233     {
6234       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6235       TEST_DrawLevelField(xx, yy);
6236     }
6237     else if (element == EL_SWITCHGATE_OPEN ||
6238              element == EL_SWITCHGATE_OPENING)
6239     {
6240       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6241
6242       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6243     }
6244     else if (element == EL_SWITCHGATE_CLOSED ||
6245              element == EL_SWITCHGATE_CLOSING)
6246     {
6247       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6248
6249       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6250     }
6251   }
6252 }
6253
6254 static int getInvisibleActiveFromInvisibleElement(int element)
6255 {
6256   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6257           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6258           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6259           element);
6260 }
6261
6262 static int getInvisibleFromInvisibleActiveElement(int element)
6263 {
6264   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6265           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6266           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6267           element);
6268 }
6269
6270 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6271 {
6272   int x, y;
6273
6274   SCAN_PLAYFIELD(x, y)
6275   {
6276     int element = Tile[x][y];
6277
6278     if (element == EL_LIGHT_SWITCH &&
6279         game.light_time_left > 0)
6280     {
6281       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6282       TEST_DrawLevelField(x, y);
6283     }
6284     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6285              game.light_time_left == 0)
6286     {
6287       Tile[x][y] = EL_LIGHT_SWITCH;
6288       TEST_DrawLevelField(x, y);
6289     }
6290     else if (element == EL_EMC_DRIPPER &&
6291              game.light_time_left > 0)
6292     {
6293       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6294       TEST_DrawLevelField(x, y);
6295     }
6296     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6297              game.light_time_left == 0)
6298     {
6299       Tile[x][y] = EL_EMC_DRIPPER;
6300       TEST_DrawLevelField(x, y);
6301     }
6302     else if (element == EL_INVISIBLE_STEELWALL ||
6303              element == EL_INVISIBLE_WALL ||
6304              element == EL_INVISIBLE_SAND)
6305     {
6306       if (game.light_time_left > 0)
6307         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6308
6309       TEST_DrawLevelField(x, y);
6310
6311       // uncrumble neighbour fields, if needed
6312       if (element == EL_INVISIBLE_SAND)
6313         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6314     }
6315     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6316              element == EL_INVISIBLE_WALL_ACTIVE ||
6317              element == EL_INVISIBLE_SAND_ACTIVE)
6318     {
6319       if (game.light_time_left == 0)
6320         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6321
6322       TEST_DrawLevelField(x, y);
6323
6324       // re-crumble neighbour fields, if needed
6325       if (element == EL_INVISIBLE_SAND)
6326         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6327     }
6328   }
6329 }
6330
6331 static void RedrawAllInvisibleElementsForLenses(void)
6332 {
6333   int x, y;
6334
6335   SCAN_PLAYFIELD(x, y)
6336   {
6337     int element = Tile[x][y];
6338
6339     if (element == EL_EMC_DRIPPER &&
6340         game.lenses_time_left > 0)
6341     {
6342       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6346              game.lenses_time_left == 0)
6347     {
6348       Tile[x][y] = EL_EMC_DRIPPER;
6349       TEST_DrawLevelField(x, y);
6350     }
6351     else if (element == EL_INVISIBLE_STEELWALL ||
6352              element == EL_INVISIBLE_WALL ||
6353              element == EL_INVISIBLE_SAND)
6354     {
6355       if (game.lenses_time_left > 0)
6356         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6357
6358       TEST_DrawLevelField(x, y);
6359
6360       // uncrumble neighbour fields, if needed
6361       if (element == EL_INVISIBLE_SAND)
6362         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6363     }
6364     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6365              element == EL_INVISIBLE_WALL_ACTIVE ||
6366              element == EL_INVISIBLE_SAND_ACTIVE)
6367     {
6368       if (game.lenses_time_left == 0)
6369         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6370
6371       TEST_DrawLevelField(x, y);
6372
6373       // re-crumble neighbour fields, if needed
6374       if (element == EL_INVISIBLE_SAND)
6375         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6376     }
6377   }
6378 }
6379
6380 static void RedrawAllInvisibleElementsForMagnifier(void)
6381 {
6382   int x, y;
6383
6384   SCAN_PLAYFIELD(x, y)
6385   {
6386     int element = Tile[x][y];
6387
6388     if (element == EL_EMC_FAKE_GRASS &&
6389         game.magnify_time_left > 0)
6390     {
6391       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6395              game.magnify_time_left == 0)
6396     {
6397       Tile[x][y] = EL_EMC_FAKE_GRASS;
6398       TEST_DrawLevelField(x, y);
6399     }
6400     else if (IS_GATE_GRAY(element) &&
6401              game.magnify_time_left > 0)
6402     {
6403       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6404                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6405                     IS_EM_GATE_GRAY(element) ?
6406                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6407                     IS_EMC_GATE_GRAY(element) ?
6408                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6409                     IS_DC_GATE_GRAY(element) ?
6410                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6411                     element);
6412       TEST_DrawLevelField(x, y);
6413     }
6414     else if (IS_GATE_GRAY_ACTIVE(element) &&
6415              game.magnify_time_left == 0)
6416     {
6417       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6418                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6419                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6420                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6421                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6422                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6423                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6424                     EL_DC_GATE_WHITE_GRAY :
6425                     element);
6426       TEST_DrawLevelField(x, y);
6427     }
6428   }
6429 }
6430
6431 static void ToggleLightSwitch(int x, int y)
6432 {
6433   int element = Tile[x][y];
6434
6435   game.light_time_left =
6436     (element == EL_LIGHT_SWITCH ?
6437      level.time_light * FRAMES_PER_SECOND : 0);
6438
6439   RedrawAllLightSwitchesAndInvisibleElements();
6440 }
6441
6442 static void ActivateTimegateSwitch(int x, int y)
6443 {
6444   int xx, yy;
6445
6446   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6447
6448   SCAN_PLAYFIELD(xx, yy)
6449   {
6450     int element = Tile[xx][yy];
6451
6452     if (element == EL_TIMEGATE_CLOSED ||
6453         element == EL_TIMEGATE_CLOSING)
6454     {
6455       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6456       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6457     }
6458
6459     /*
6460     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6461     {
6462       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6463       TEST_DrawLevelField(xx, yy);
6464     }
6465     */
6466
6467   }
6468
6469   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6470                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6471 }
6472
6473 static void Impact(int x, int y)
6474 {
6475   boolean last_line = (y == lev_fieldy - 1);
6476   boolean object_hit = FALSE;
6477   boolean impact = (last_line || object_hit);
6478   int element = Tile[x][y];
6479   int smashed = EL_STEELWALL;
6480
6481   if (!last_line)       // check if element below was hit
6482   {
6483     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6484       return;
6485
6486     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6487                                          MovDir[x][y + 1] != MV_DOWN ||
6488                                          MovPos[x][y + 1] <= TILEY / 2));
6489
6490     // do not smash moving elements that left the smashed field in time
6491     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6492         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6493       object_hit = FALSE;
6494
6495 #if USE_QUICKSAND_IMPACT_BUGFIX
6496     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6497     {
6498       RemoveMovingField(x, y + 1);
6499       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6500       Tile[x][y + 2] = EL_ROCK;
6501       TEST_DrawLevelField(x, y + 2);
6502
6503       object_hit = TRUE;
6504     }
6505
6506     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6507     {
6508       RemoveMovingField(x, y + 1);
6509       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6510       Tile[x][y + 2] = EL_ROCK;
6511       TEST_DrawLevelField(x, y + 2);
6512
6513       object_hit = TRUE;
6514     }
6515 #endif
6516
6517     if (object_hit)
6518       smashed = MovingOrBlocked2Element(x, y + 1);
6519
6520     impact = (last_line || object_hit);
6521   }
6522
6523   if (!last_line && smashed == EL_ACID) // element falls into acid
6524   {
6525     SplashAcid(x, y + 1);
6526     return;
6527   }
6528
6529   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6530   // only reset graphic animation if graphic really changes after impact
6531   if (impact &&
6532       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6533   {
6534     ResetGfxAnimation(x, y);
6535     TEST_DrawLevelField(x, y);
6536   }
6537
6538   if (impact && CAN_EXPLODE_IMPACT(element))
6539   {
6540     Bang(x, y);
6541     return;
6542   }
6543   else if (impact && element == EL_PEARL &&
6544            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6545   {
6546     ResetGfxAnimation(x, y);
6547
6548     Tile[x][y] = EL_PEARL_BREAKING;
6549     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6550     return;
6551   }
6552   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6553   {
6554     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6555
6556     return;
6557   }
6558
6559   if (impact && element == EL_AMOEBA_DROP)
6560   {
6561     if (object_hit && IS_PLAYER(x, y + 1))
6562       KillPlayerUnlessEnemyProtected(x, y + 1);
6563     else if (object_hit && smashed == EL_PENGUIN)
6564       Bang(x, y + 1);
6565     else
6566     {
6567       Tile[x][y] = EL_AMOEBA_GROWING;
6568       Store[x][y] = EL_AMOEBA_WET;
6569
6570       ResetRandomAnimationValue(x, y);
6571     }
6572     return;
6573   }
6574
6575   if (object_hit)               // check which object was hit
6576   {
6577     if ((CAN_PASS_MAGIC_WALL(element) && 
6578          (smashed == EL_MAGIC_WALL ||
6579           smashed == EL_BD_MAGIC_WALL)) ||
6580         (CAN_PASS_DC_MAGIC_WALL(element) &&
6581          smashed == EL_DC_MAGIC_WALL))
6582     {
6583       int xx, yy;
6584       int activated_magic_wall =
6585         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6586          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6587          EL_DC_MAGIC_WALL_ACTIVE);
6588
6589       // activate magic wall / mill
6590       SCAN_PLAYFIELD(xx, yy)
6591       {
6592         if (Tile[xx][yy] == smashed)
6593           Tile[xx][yy] = activated_magic_wall;
6594       }
6595
6596       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6597       game.magic_wall_active = TRUE;
6598
6599       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6600                             SND_MAGIC_WALL_ACTIVATING :
6601                             smashed == EL_BD_MAGIC_WALL ?
6602                             SND_BD_MAGIC_WALL_ACTIVATING :
6603                             SND_DC_MAGIC_WALL_ACTIVATING));
6604     }
6605
6606     if (IS_PLAYER(x, y + 1))
6607     {
6608       if (CAN_SMASH_PLAYER(element))
6609       {
6610         KillPlayerUnlessEnemyProtected(x, y + 1);
6611         return;
6612       }
6613     }
6614     else if (smashed == EL_PENGUIN)
6615     {
6616       if (CAN_SMASH_PLAYER(element))
6617       {
6618         Bang(x, y + 1);
6619         return;
6620       }
6621     }
6622     else if (element == EL_BD_DIAMOND)
6623     {
6624       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6625       {
6626         Bang(x, y + 1);
6627         return;
6628       }
6629     }
6630     else if (((element == EL_SP_INFOTRON ||
6631                element == EL_SP_ZONK) &&
6632               (smashed == EL_SP_SNIKSNAK ||
6633                smashed == EL_SP_ELECTRON ||
6634                smashed == EL_SP_DISK_ORANGE)) ||
6635              (element == EL_SP_INFOTRON &&
6636               smashed == EL_SP_DISK_YELLOW))
6637     {
6638       Bang(x, y + 1);
6639       return;
6640     }
6641     else if (CAN_SMASH_EVERYTHING(element))
6642     {
6643       if (IS_CLASSIC_ENEMY(smashed) ||
6644           CAN_EXPLODE_SMASHED(smashed))
6645       {
6646         Bang(x, y + 1);
6647         return;
6648       }
6649       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6650       {
6651         if (smashed == EL_LAMP ||
6652             smashed == EL_LAMP_ACTIVE)
6653         {
6654           Bang(x, y + 1);
6655           return;
6656         }
6657         else if (smashed == EL_NUT)
6658         {
6659           Tile[x][y + 1] = EL_NUT_BREAKING;
6660           PlayLevelSound(x, y, SND_NUT_BREAKING);
6661           RaiseScoreElement(EL_NUT);
6662           return;
6663         }
6664         else if (smashed == EL_PEARL)
6665         {
6666           ResetGfxAnimation(x, y);
6667
6668           Tile[x][y + 1] = EL_PEARL_BREAKING;
6669           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6670           return;
6671         }
6672         else if (smashed == EL_DIAMOND)
6673         {
6674           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6675           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6676           return;
6677         }
6678         else if (IS_BELT_SWITCH(smashed))
6679         {
6680           ToggleBeltSwitch(x, y + 1);
6681         }
6682         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6683                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6684                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6685                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6686         {
6687           ToggleSwitchgateSwitch(x, y + 1);
6688         }
6689         else if (smashed == EL_LIGHT_SWITCH ||
6690                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6691         {
6692           ToggleLightSwitch(x, y + 1);
6693         }
6694         else
6695         {
6696           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6697
6698           CheckElementChangeBySide(x, y + 1, smashed, element,
6699                                    CE_SWITCHED, CH_SIDE_TOP);
6700           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6701                                             CH_SIDE_TOP);
6702         }
6703       }
6704       else
6705       {
6706         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6707       }
6708     }
6709   }
6710
6711   // play sound of magic wall / mill
6712   if (!last_line &&
6713       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6714        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6715        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6716   {
6717     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6718       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6719     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6720       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6721     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6722       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6723
6724     return;
6725   }
6726
6727   // play sound of object that hits the ground
6728   if (last_line || object_hit)
6729     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6730 }
6731
6732 static void TurnRoundExt(int x, int y)
6733 {
6734   static struct
6735   {
6736     int dx, dy;
6737   } move_xy[] =
6738   {
6739     {  0,  0 },
6740     { -1,  0 },
6741     { +1,  0 },
6742     {  0,  0 },
6743     {  0, -1 },
6744     {  0,  0 }, { 0, 0 }, { 0, 0 },
6745     {  0, +1 }
6746   };
6747   static struct
6748   {
6749     int left, right, back;
6750   } turn[] =
6751   {
6752     { 0,        0,              0        },
6753     { MV_DOWN,  MV_UP,          MV_RIGHT },
6754     { MV_UP,    MV_DOWN,        MV_LEFT  },
6755     { 0,        0,              0        },
6756     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6757     { 0,        0,              0        },
6758     { 0,        0,              0        },
6759     { 0,        0,              0        },
6760     { MV_RIGHT, MV_LEFT,        MV_UP    }
6761   };
6762
6763   int element = Tile[x][y];
6764   int move_pattern = element_info[element].move_pattern;
6765
6766   int old_move_dir = MovDir[x][y];
6767   int left_dir  = turn[old_move_dir].left;
6768   int right_dir = turn[old_move_dir].right;
6769   int back_dir  = turn[old_move_dir].back;
6770
6771   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6772   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6773   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6774   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6775
6776   int left_x  = x + left_dx,  left_y  = y + left_dy;
6777   int right_x = x + right_dx, right_y = y + right_dy;
6778   int move_x  = x + move_dx,  move_y  = y + move_dy;
6779
6780   int xx, yy;
6781
6782   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6783   {
6784     TestIfBadThingTouchesOtherBadThing(x, y);
6785
6786     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6787       MovDir[x][y] = right_dir;
6788     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6789       MovDir[x][y] = left_dir;
6790
6791     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6792       MovDelay[x][y] = 9;
6793     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6794       MovDelay[x][y] = 1;
6795   }
6796   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6797   {
6798     TestIfBadThingTouchesOtherBadThing(x, y);
6799
6800     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6801       MovDir[x][y] = left_dir;
6802     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6803       MovDir[x][y] = right_dir;
6804
6805     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6806       MovDelay[x][y] = 9;
6807     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6808       MovDelay[x][y] = 1;
6809   }
6810   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6811   {
6812     TestIfBadThingTouchesOtherBadThing(x, y);
6813
6814     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6815       MovDir[x][y] = left_dir;
6816     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6817       MovDir[x][y] = right_dir;
6818
6819     if (MovDir[x][y] != old_move_dir)
6820       MovDelay[x][y] = 9;
6821   }
6822   else if (element == EL_YAMYAM)
6823   {
6824     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6825     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6826
6827     if (can_turn_left && can_turn_right)
6828       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6829     else if (can_turn_left)
6830       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6831     else if (can_turn_right)
6832       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6833     else
6834       MovDir[x][y] = back_dir;
6835
6836     MovDelay[x][y] = 16 + 16 * RND(3);
6837   }
6838   else if (element == EL_DARK_YAMYAM)
6839   {
6840     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6841                                                          left_x, left_y);
6842     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6843                                                          right_x, right_y);
6844
6845     if (can_turn_left && can_turn_right)
6846       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6847     else if (can_turn_left)
6848       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6849     else if (can_turn_right)
6850       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6851     else
6852       MovDir[x][y] = back_dir;
6853
6854     MovDelay[x][y] = 16 + 16 * RND(3);
6855   }
6856   else if (element == EL_PACMAN)
6857   {
6858     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6859     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6860
6861     if (can_turn_left && can_turn_right)
6862       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863     else if (can_turn_left)
6864       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865     else if (can_turn_right)
6866       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6867     else
6868       MovDir[x][y] = back_dir;
6869
6870     MovDelay[x][y] = 6 + RND(40);
6871   }
6872   else if (element == EL_PIG)
6873   {
6874     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6875     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6876     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6877     boolean should_turn_left, should_turn_right, should_move_on;
6878     int rnd_value = 24;
6879     int rnd = RND(rnd_value);
6880
6881     should_turn_left = (can_turn_left &&
6882                         (!can_move_on ||
6883                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6884                                                    y + back_dy + left_dy)));
6885     should_turn_right = (can_turn_right &&
6886                          (!can_move_on ||
6887                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6888                                                     y + back_dy + right_dy)));
6889     should_move_on = (can_move_on &&
6890                       (!can_turn_left ||
6891                        !can_turn_right ||
6892                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6893                                                  y + move_dy + left_dy) ||
6894                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6895                                                  y + move_dy + right_dy)));
6896
6897     if (should_turn_left || should_turn_right || should_move_on)
6898     {
6899       if (should_turn_left && should_turn_right && should_move_on)
6900         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6901                         rnd < 2 * rnd_value / 3 ? right_dir :
6902                         old_move_dir);
6903       else if (should_turn_left && should_turn_right)
6904         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6905       else if (should_turn_left && should_move_on)
6906         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6907       else if (should_turn_right && should_move_on)
6908         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6909       else if (should_turn_left)
6910         MovDir[x][y] = left_dir;
6911       else if (should_turn_right)
6912         MovDir[x][y] = right_dir;
6913       else if (should_move_on)
6914         MovDir[x][y] = old_move_dir;
6915     }
6916     else if (can_move_on && rnd > rnd_value / 8)
6917       MovDir[x][y] = old_move_dir;
6918     else if (can_turn_left && can_turn_right)
6919       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6920     else if (can_turn_left && rnd > rnd_value / 8)
6921       MovDir[x][y] = left_dir;
6922     else if (can_turn_right && rnd > rnd_value/8)
6923       MovDir[x][y] = right_dir;
6924     else
6925       MovDir[x][y] = back_dir;
6926
6927     xx = x + move_xy[MovDir[x][y]].dx;
6928     yy = y + move_xy[MovDir[x][y]].dy;
6929
6930     if (!IN_LEV_FIELD(xx, yy) ||
6931         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6932       MovDir[x][y] = old_move_dir;
6933
6934     MovDelay[x][y] = 0;
6935   }
6936   else if (element == EL_DRAGON)
6937   {
6938     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6939     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6940     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6941     int rnd_value = 24;
6942     int rnd = RND(rnd_value);
6943
6944     if (can_move_on && rnd > rnd_value / 8)
6945       MovDir[x][y] = old_move_dir;
6946     else if (can_turn_left && can_turn_right)
6947       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6948     else if (can_turn_left && rnd > rnd_value / 8)
6949       MovDir[x][y] = left_dir;
6950     else if (can_turn_right && rnd > rnd_value / 8)
6951       MovDir[x][y] = right_dir;
6952     else
6953       MovDir[x][y] = back_dir;
6954
6955     xx = x + move_xy[MovDir[x][y]].dx;
6956     yy = y + move_xy[MovDir[x][y]].dy;
6957
6958     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6959       MovDir[x][y] = old_move_dir;
6960
6961     MovDelay[x][y] = 0;
6962   }
6963   else if (element == EL_MOLE)
6964   {
6965     boolean can_move_on =
6966       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6967                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6968                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6969     if (!can_move_on)
6970     {
6971       boolean can_turn_left =
6972         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6973                               IS_AMOEBOID(Tile[left_x][left_y])));
6974
6975       boolean can_turn_right =
6976         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6977                               IS_AMOEBOID(Tile[right_x][right_y])));
6978
6979       if (can_turn_left && can_turn_right)
6980         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6981       else if (can_turn_left)
6982         MovDir[x][y] = left_dir;
6983       else
6984         MovDir[x][y] = right_dir;
6985     }
6986
6987     if (MovDir[x][y] != old_move_dir)
6988       MovDelay[x][y] = 9;
6989   }
6990   else if (element == EL_BALLOON)
6991   {
6992     MovDir[x][y] = game.wind_direction;
6993     MovDelay[x][y] = 0;
6994   }
6995   else if (element == EL_SPRING)
6996   {
6997     if (MovDir[x][y] & MV_HORIZONTAL)
6998     {
6999       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7000           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7001       {
7002         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7003         ResetGfxAnimation(move_x, move_y);
7004         TEST_DrawLevelField(move_x, move_y);
7005
7006         MovDir[x][y] = back_dir;
7007       }
7008       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7009                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7010         MovDir[x][y] = MV_NONE;
7011     }
7012
7013     MovDelay[x][y] = 0;
7014   }
7015   else if (element == EL_ROBOT ||
7016            element == EL_SATELLITE ||
7017            element == EL_PENGUIN ||
7018            element == EL_EMC_ANDROID)
7019   {
7020     int attr_x = -1, attr_y = -1;
7021
7022     if (game.all_players_gone)
7023     {
7024       attr_x = game.exit_x;
7025       attr_y = game.exit_y;
7026     }
7027     else
7028     {
7029       int i;
7030
7031       for (i = 0; i < MAX_PLAYERS; i++)
7032       {
7033         struct PlayerInfo *player = &stored_player[i];
7034         int jx = player->jx, jy = player->jy;
7035
7036         if (!player->active)
7037           continue;
7038
7039         if (attr_x == -1 ||
7040             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7041         {
7042           attr_x = jx;
7043           attr_y = jy;
7044         }
7045       }
7046     }
7047
7048     if (element == EL_ROBOT &&
7049         game.robot_wheel_x >= 0 &&
7050         game.robot_wheel_y >= 0 &&
7051         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7052          game.engine_version < VERSION_IDENT(3,1,0,0)))
7053     {
7054       attr_x = game.robot_wheel_x;
7055       attr_y = game.robot_wheel_y;
7056     }
7057
7058     if (element == EL_PENGUIN)
7059     {
7060       int i;
7061       static int xy[4][2] =
7062       {
7063         { 0, -1 },
7064         { -1, 0 },
7065         { +1, 0 },
7066         { 0, +1 }
7067       };
7068
7069       for (i = 0; i < NUM_DIRECTIONS; i++)
7070       {
7071         int ex = x + xy[i][0];
7072         int ey = y + xy[i][1];
7073
7074         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7075                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7076                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7077                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7078         {
7079           attr_x = ex;
7080           attr_y = ey;
7081           break;
7082         }
7083       }
7084     }
7085
7086     MovDir[x][y] = MV_NONE;
7087     if (attr_x < x)
7088       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7089     else if (attr_x > x)
7090       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7091     if (attr_y < y)
7092       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7093     else if (attr_y > y)
7094       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7095
7096     if (element == EL_ROBOT)
7097     {
7098       int newx, newy;
7099
7100       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7101         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7102       Moving2Blocked(x, y, &newx, &newy);
7103
7104       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7105         MovDelay[x][y] = 8 + 8 * !RND(3);
7106       else
7107         MovDelay[x][y] = 16;
7108     }
7109     else if (element == EL_PENGUIN)
7110     {
7111       int newx, newy;
7112
7113       MovDelay[x][y] = 1;
7114
7115       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7116       {
7117         boolean first_horiz = RND(2);
7118         int new_move_dir = MovDir[x][y];
7119
7120         MovDir[x][y] =
7121           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7122         Moving2Blocked(x, y, &newx, &newy);
7123
7124         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7125           return;
7126
7127         MovDir[x][y] =
7128           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7129         Moving2Blocked(x, y, &newx, &newy);
7130
7131         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7132           return;
7133
7134         MovDir[x][y] = old_move_dir;
7135         return;
7136       }
7137     }
7138     else if (element == EL_SATELLITE)
7139     {
7140       int newx, newy;
7141
7142       MovDelay[x][y] = 1;
7143
7144       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7145       {
7146         boolean first_horiz = RND(2);
7147         int new_move_dir = MovDir[x][y];
7148
7149         MovDir[x][y] =
7150           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7151         Moving2Blocked(x, y, &newx, &newy);
7152
7153         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7154           return;
7155
7156         MovDir[x][y] =
7157           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7158         Moving2Blocked(x, y, &newx, &newy);
7159
7160         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7161           return;
7162
7163         MovDir[x][y] = old_move_dir;
7164         return;
7165       }
7166     }
7167     else if (element == EL_EMC_ANDROID)
7168     {
7169       static int check_pos[16] =
7170       {
7171         -1,             //  0 => (invalid)
7172         7,              //  1 => MV_LEFT
7173         3,              //  2 => MV_RIGHT
7174         -1,             //  3 => (invalid)
7175         1,              //  4 =>            MV_UP
7176         0,              //  5 => MV_LEFT  | MV_UP
7177         2,              //  6 => MV_RIGHT | MV_UP
7178         -1,             //  7 => (invalid)
7179         5,              //  8 =>            MV_DOWN
7180         6,              //  9 => MV_LEFT  | MV_DOWN
7181         4,              // 10 => MV_RIGHT | MV_DOWN
7182         -1,             // 11 => (invalid)
7183         -1,             // 12 => (invalid)
7184         -1,             // 13 => (invalid)
7185         -1,             // 14 => (invalid)
7186         -1,             // 15 => (invalid)
7187       };
7188       static struct
7189       {
7190         int dx, dy;
7191         int dir;
7192       } check_xy[8] =
7193       {
7194         { -1, -1,       MV_LEFT  | MV_UP   },
7195         {  0, -1,                  MV_UP   },
7196         { +1, -1,       MV_RIGHT | MV_UP   },
7197         { +1,  0,       MV_RIGHT           },
7198         { +1, +1,       MV_RIGHT | MV_DOWN },
7199         {  0, +1,                  MV_DOWN },
7200         { -1, +1,       MV_LEFT  | MV_DOWN },
7201         { -1,  0,       MV_LEFT            },
7202       };
7203       int start_pos, check_order;
7204       boolean can_clone = FALSE;
7205       int i;
7206
7207       // check if there is any free field around current position
7208       for (i = 0; i < 8; i++)
7209       {
7210         int newx = x + check_xy[i].dx;
7211         int newy = y + check_xy[i].dy;
7212
7213         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7214         {
7215           can_clone = TRUE;
7216
7217           break;
7218         }
7219       }
7220
7221       if (can_clone)            // randomly find an element to clone
7222       {
7223         can_clone = FALSE;
7224
7225         start_pos = check_pos[RND(8)];
7226         check_order = (RND(2) ? -1 : +1);
7227
7228         for (i = 0; i < 8; i++)
7229         {
7230           int pos_raw = start_pos + i * check_order;
7231           int pos = (pos_raw + 8) % 8;
7232           int newx = x + check_xy[pos].dx;
7233           int newy = y + check_xy[pos].dy;
7234
7235           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7236           {
7237             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7238             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7239
7240             Store[x][y] = Tile[newx][newy];
7241
7242             can_clone = TRUE;
7243
7244             break;
7245           }
7246         }
7247       }
7248
7249       if (can_clone)            // randomly find a direction to move
7250       {
7251         can_clone = FALSE;
7252
7253         start_pos = check_pos[RND(8)];
7254         check_order = (RND(2) ? -1 : +1);
7255
7256         for (i = 0; i < 8; i++)
7257         {
7258           int pos_raw = start_pos + i * check_order;
7259           int pos = (pos_raw + 8) % 8;
7260           int newx = x + check_xy[pos].dx;
7261           int newy = y + check_xy[pos].dy;
7262           int new_move_dir = check_xy[pos].dir;
7263
7264           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7265           {
7266             MovDir[x][y] = new_move_dir;
7267             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7268
7269             can_clone = TRUE;
7270
7271             break;
7272           }
7273         }
7274       }
7275
7276       if (can_clone)            // cloning and moving successful
7277         return;
7278
7279       // cannot clone -- try to move towards player
7280
7281       start_pos = check_pos[MovDir[x][y] & 0x0f];
7282       check_order = (RND(2) ? -1 : +1);
7283
7284       for (i = 0; i < 3; i++)
7285       {
7286         // first check start_pos, then previous/next or (next/previous) pos
7287         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7288         int pos = (pos_raw + 8) % 8;
7289         int newx = x + check_xy[pos].dx;
7290         int newy = y + check_xy[pos].dy;
7291         int new_move_dir = check_xy[pos].dir;
7292
7293         if (IS_PLAYER(newx, newy))
7294           break;
7295
7296         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7297         {
7298           MovDir[x][y] = new_move_dir;
7299           MovDelay[x][y] = level.android_move_time * 8 + 1;
7300
7301           break;
7302         }
7303       }
7304     }
7305   }
7306   else if (move_pattern == MV_TURNING_LEFT ||
7307            move_pattern == MV_TURNING_RIGHT ||
7308            move_pattern == MV_TURNING_LEFT_RIGHT ||
7309            move_pattern == MV_TURNING_RIGHT_LEFT ||
7310            move_pattern == MV_TURNING_RANDOM ||
7311            move_pattern == MV_ALL_DIRECTIONS)
7312   {
7313     boolean can_turn_left =
7314       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7315     boolean can_turn_right =
7316       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7317
7318     if (element_info[element].move_stepsize == 0)       // "not moving"
7319       return;
7320
7321     if (move_pattern == MV_TURNING_LEFT)
7322       MovDir[x][y] = left_dir;
7323     else if (move_pattern == MV_TURNING_RIGHT)
7324       MovDir[x][y] = right_dir;
7325     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7326       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7327     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7328       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7329     else if (move_pattern == MV_TURNING_RANDOM)
7330       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7331                       can_turn_right && !can_turn_left ? right_dir :
7332                       RND(2) ? left_dir : right_dir);
7333     else if (can_turn_left && can_turn_right)
7334       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7335     else if (can_turn_left)
7336       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7337     else if (can_turn_right)
7338       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7339     else
7340       MovDir[x][y] = back_dir;
7341
7342     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7343   }
7344   else if (move_pattern == MV_HORIZONTAL ||
7345            move_pattern == MV_VERTICAL)
7346   {
7347     if (move_pattern & old_move_dir)
7348       MovDir[x][y] = back_dir;
7349     else if (move_pattern == MV_HORIZONTAL)
7350       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7351     else if (move_pattern == MV_VERTICAL)
7352       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7353
7354     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7355   }
7356   else if (move_pattern & MV_ANY_DIRECTION)
7357   {
7358     MovDir[x][y] = move_pattern;
7359     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7360   }
7361   else if (move_pattern & MV_WIND_DIRECTION)
7362   {
7363     MovDir[x][y] = game.wind_direction;
7364     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7365   }
7366   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7367   {
7368     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7369       MovDir[x][y] = left_dir;
7370     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7371       MovDir[x][y] = right_dir;
7372
7373     if (MovDir[x][y] != old_move_dir)
7374       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7375   }
7376   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7377   {
7378     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7379       MovDir[x][y] = right_dir;
7380     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7381       MovDir[x][y] = left_dir;
7382
7383     if (MovDir[x][y] != old_move_dir)
7384       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7385   }
7386   else if (move_pattern == MV_TOWARDS_PLAYER ||
7387            move_pattern == MV_AWAY_FROM_PLAYER)
7388   {
7389     int attr_x = -1, attr_y = -1;
7390     int newx, newy;
7391     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7392
7393     if (game.all_players_gone)
7394     {
7395       attr_x = game.exit_x;
7396       attr_y = game.exit_y;
7397     }
7398     else
7399     {
7400       int i;
7401
7402       for (i = 0; i < MAX_PLAYERS; i++)
7403       {
7404         struct PlayerInfo *player = &stored_player[i];
7405         int jx = player->jx, jy = player->jy;
7406
7407         if (!player->active)
7408           continue;
7409
7410         if (attr_x == -1 ||
7411             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7412         {
7413           attr_x = jx;
7414           attr_y = jy;
7415         }
7416       }
7417     }
7418
7419     MovDir[x][y] = MV_NONE;
7420     if (attr_x < x)
7421       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7422     else if (attr_x > x)
7423       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7424     if (attr_y < y)
7425       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7426     else if (attr_y > y)
7427       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7428
7429     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7430
7431     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7432     {
7433       boolean first_horiz = RND(2);
7434       int new_move_dir = MovDir[x][y];
7435
7436       if (element_info[element].move_stepsize == 0)     // "not moving"
7437       {
7438         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7439         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7440
7441         return;
7442       }
7443
7444       MovDir[x][y] =
7445         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7446       Moving2Blocked(x, y, &newx, &newy);
7447
7448       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7449         return;
7450
7451       MovDir[x][y] =
7452         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453       Moving2Blocked(x, y, &newx, &newy);
7454
7455       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7456         return;
7457
7458       MovDir[x][y] = old_move_dir;
7459     }
7460   }
7461   else if (move_pattern == MV_WHEN_PUSHED ||
7462            move_pattern == MV_WHEN_DROPPED)
7463   {
7464     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7465       MovDir[x][y] = MV_NONE;
7466
7467     MovDelay[x][y] = 0;
7468   }
7469   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7470   {
7471     static int test_xy[7][2] =
7472     {
7473       { 0, -1 },
7474       { -1, 0 },
7475       { +1, 0 },
7476       { 0, +1 },
7477       { 0, -1 },
7478       { -1, 0 },
7479       { +1, 0 },
7480     };
7481     static int test_dir[7] =
7482     {
7483       MV_UP,
7484       MV_LEFT,
7485       MV_RIGHT,
7486       MV_DOWN,
7487       MV_UP,
7488       MV_LEFT,
7489       MV_RIGHT,
7490     };
7491     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7492     int move_preference = -1000000;     // start with very low preference
7493     int new_move_dir = MV_NONE;
7494     int start_test = RND(4);
7495     int i;
7496
7497     for (i = 0; i < NUM_DIRECTIONS; i++)
7498     {
7499       int move_dir = test_dir[start_test + i];
7500       int move_dir_preference;
7501
7502       xx = x + test_xy[start_test + i][0];
7503       yy = y + test_xy[start_test + i][1];
7504
7505       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7506           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7507       {
7508         new_move_dir = move_dir;
7509
7510         break;
7511       }
7512
7513       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7514         continue;
7515
7516       move_dir_preference = -1 * RunnerVisit[xx][yy];
7517       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7518         move_dir_preference = PlayerVisit[xx][yy];
7519
7520       if (move_dir_preference > move_preference)
7521       {
7522         // prefer field that has not been visited for the longest time
7523         move_preference = move_dir_preference;
7524         new_move_dir = move_dir;
7525       }
7526       else if (move_dir_preference == move_preference &&
7527                move_dir == old_move_dir)
7528       {
7529         // prefer last direction when all directions are preferred equally
7530         move_preference = move_dir_preference;
7531         new_move_dir = move_dir;
7532       }
7533     }
7534
7535     MovDir[x][y] = new_move_dir;
7536     if (old_move_dir != new_move_dir)
7537       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7538   }
7539 }
7540
7541 static void TurnRound(int x, int y)
7542 {
7543   int direction = MovDir[x][y];
7544
7545   TurnRoundExt(x, y);
7546
7547   GfxDir[x][y] = MovDir[x][y];
7548
7549   if (direction != MovDir[x][y])
7550     GfxFrame[x][y] = 0;
7551
7552   if (MovDelay[x][y])
7553     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7554
7555   ResetGfxFrame(x, y);
7556 }
7557
7558 static boolean JustBeingPushed(int x, int y)
7559 {
7560   int i;
7561
7562   for (i = 0; i < MAX_PLAYERS; i++)
7563   {
7564     struct PlayerInfo *player = &stored_player[i];
7565
7566     if (player->active && player->is_pushing && player->MovPos)
7567     {
7568       int next_jx = player->jx + (player->jx - player->last_jx);
7569       int next_jy = player->jy + (player->jy - player->last_jy);
7570
7571       if (x == next_jx && y == next_jy)
7572         return TRUE;
7573     }
7574   }
7575
7576   return FALSE;
7577 }
7578
7579 static void StartMoving(int x, int y)
7580 {
7581   boolean started_moving = FALSE;       // some elements can fall _and_ move
7582   int element = Tile[x][y];
7583
7584   if (Stop[x][y])
7585     return;
7586
7587   if (MovDelay[x][y] == 0)
7588     GfxAction[x][y] = ACTION_DEFAULT;
7589
7590   if (CAN_FALL(element) && y < lev_fieldy - 1)
7591   {
7592     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7593         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7594       if (JustBeingPushed(x, y))
7595         return;
7596
7597     if (element == EL_QUICKSAND_FULL)
7598     {
7599       if (IS_FREE(x, y + 1))
7600       {
7601         InitMovingField(x, y, MV_DOWN);
7602         started_moving = TRUE;
7603
7604         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7605 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7606         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7607           Store[x][y] = EL_ROCK;
7608 #else
7609         Store[x][y] = EL_ROCK;
7610 #endif
7611
7612         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7613       }
7614       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7615       {
7616         if (!MovDelay[x][y])
7617         {
7618           MovDelay[x][y] = TILEY + 1;
7619
7620           ResetGfxAnimation(x, y);
7621           ResetGfxAnimation(x, y + 1);
7622         }
7623
7624         if (MovDelay[x][y])
7625         {
7626           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7627           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7628
7629           MovDelay[x][y]--;
7630           if (MovDelay[x][y])
7631             return;
7632         }
7633
7634         Tile[x][y] = EL_QUICKSAND_EMPTY;
7635         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7636         Store[x][y + 1] = Store[x][y];
7637         Store[x][y] = 0;
7638
7639         PlayLevelSoundAction(x, y, ACTION_FILLING);
7640       }
7641       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7642       {
7643         if (!MovDelay[x][y])
7644         {
7645           MovDelay[x][y] = TILEY + 1;
7646
7647           ResetGfxAnimation(x, y);
7648           ResetGfxAnimation(x, y + 1);
7649         }
7650
7651         if (MovDelay[x][y])
7652         {
7653           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7654           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7655
7656           MovDelay[x][y]--;
7657           if (MovDelay[x][y])
7658             return;
7659         }
7660
7661         Tile[x][y] = EL_QUICKSAND_EMPTY;
7662         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7663         Store[x][y + 1] = Store[x][y];
7664         Store[x][y] = 0;
7665
7666         PlayLevelSoundAction(x, y, ACTION_FILLING);
7667       }
7668     }
7669     else if (element == EL_QUICKSAND_FAST_FULL)
7670     {
7671       if (IS_FREE(x, y + 1))
7672       {
7673         InitMovingField(x, y, MV_DOWN);
7674         started_moving = TRUE;
7675
7676         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7677 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7678         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7679           Store[x][y] = EL_ROCK;
7680 #else
7681         Store[x][y] = EL_ROCK;
7682 #endif
7683
7684         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7685       }
7686       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7687       {
7688         if (!MovDelay[x][y])
7689         {
7690           MovDelay[x][y] = TILEY + 1;
7691
7692           ResetGfxAnimation(x, y);
7693           ResetGfxAnimation(x, y + 1);
7694         }
7695
7696         if (MovDelay[x][y])
7697         {
7698           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7699           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7700
7701           MovDelay[x][y]--;
7702           if (MovDelay[x][y])
7703             return;
7704         }
7705
7706         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7707         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7708         Store[x][y + 1] = Store[x][y];
7709         Store[x][y] = 0;
7710
7711         PlayLevelSoundAction(x, y, ACTION_FILLING);
7712       }
7713       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7714       {
7715         if (!MovDelay[x][y])
7716         {
7717           MovDelay[x][y] = TILEY + 1;
7718
7719           ResetGfxAnimation(x, y);
7720           ResetGfxAnimation(x, y + 1);
7721         }
7722
7723         if (MovDelay[x][y])
7724         {
7725           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7726           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7727
7728           MovDelay[x][y]--;
7729           if (MovDelay[x][y])
7730             return;
7731         }
7732
7733         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7734         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7735         Store[x][y + 1] = Store[x][y];
7736         Store[x][y] = 0;
7737
7738         PlayLevelSoundAction(x, y, ACTION_FILLING);
7739       }
7740     }
7741     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7742              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7743     {
7744       InitMovingField(x, y, MV_DOWN);
7745       started_moving = TRUE;
7746
7747       Tile[x][y] = EL_QUICKSAND_FILLING;
7748       Store[x][y] = element;
7749
7750       PlayLevelSoundAction(x, y, ACTION_FILLING);
7751     }
7752     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7753              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7754     {
7755       InitMovingField(x, y, MV_DOWN);
7756       started_moving = TRUE;
7757
7758       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7759       Store[x][y] = element;
7760
7761       PlayLevelSoundAction(x, y, ACTION_FILLING);
7762     }
7763     else if (element == EL_MAGIC_WALL_FULL)
7764     {
7765       if (IS_FREE(x, y + 1))
7766       {
7767         InitMovingField(x, y, MV_DOWN);
7768         started_moving = TRUE;
7769
7770         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7771         Store[x][y] = EL_CHANGED(Store[x][y]);
7772       }
7773       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7774       {
7775         if (!MovDelay[x][y])
7776           MovDelay[x][y] = TILEY / 4 + 1;
7777
7778         if (MovDelay[x][y])
7779         {
7780           MovDelay[x][y]--;
7781           if (MovDelay[x][y])
7782             return;
7783         }
7784
7785         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7786         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7787         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7788         Store[x][y] = 0;
7789       }
7790     }
7791     else if (element == EL_BD_MAGIC_WALL_FULL)
7792     {
7793       if (IS_FREE(x, y + 1))
7794       {
7795         InitMovingField(x, y, MV_DOWN);
7796         started_moving = TRUE;
7797
7798         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7799         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7800       }
7801       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7802       {
7803         if (!MovDelay[x][y])
7804           MovDelay[x][y] = TILEY / 4 + 1;
7805
7806         if (MovDelay[x][y])
7807         {
7808           MovDelay[x][y]--;
7809           if (MovDelay[x][y])
7810             return;
7811         }
7812
7813         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7814         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7815         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7816         Store[x][y] = 0;
7817       }
7818     }
7819     else if (element == EL_DC_MAGIC_WALL_FULL)
7820     {
7821       if (IS_FREE(x, y + 1))
7822       {
7823         InitMovingField(x, y, MV_DOWN);
7824         started_moving = TRUE;
7825
7826         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7827         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7828       }
7829       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7830       {
7831         if (!MovDelay[x][y])
7832           MovDelay[x][y] = TILEY / 4 + 1;
7833
7834         if (MovDelay[x][y])
7835         {
7836           MovDelay[x][y]--;
7837           if (MovDelay[x][y])
7838             return;
7839         }
7840
7841         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7842         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7843         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7844         Store[x][y] = 0;
7845       }
7846     }
7847     else if ((CAN_PASS_MAGIC_WALL(element) &&
7848               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7849                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7850              (CAN_PASS_DC_MAGIC_WALL(element) &&
7851               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7852
7853     {
7854       InitMovingField(x, y, MV_DOWN);
7855       started_moving = TRUE;
7856
7857       Tile[x][y] =
7858         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7859          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7860          EL_DC_MAGIC_WALL_FILLING);
7861       Store[x][y] = element;
7862     }
7863     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7864     {
7865       SplashAcid(x, y + 1);
7866
7867       InitMovingField(x, y, MV_DOWN);
7868       started_moving = TRUE;
7869
7870       Store[x][y] = EL_ACID;
7871     }
7872     else if (
7873              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7874               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7875              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7876               CAN_FALL(element) && WasJustFalling[x][y] &&
7877               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7878
7879              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7880               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7881               (Tile[x][y + 1] == EL_BLOCKED)))
7882     {
7883       /* this is needed for a special case not covered by calling "Impact()"
7884          from "ContinueMoving()": if an element moves to a tile directly below
7885          another element which was just falling on that tile (which was empty
7886          in the previous frame), the falling element above would just stop
7887          instead of smashing the element below (in previous version, the above
7888          element was just checked for "moving" instead of "falling", resulting
7889          in incorrect smashes caused by horizontal movement of the above
7890          element; also, the case of the player being the element to smash was
7891          simply not covered here... :-/ ) */
7892
7893       CheckCollision[x][y] = 0;
7894       CheckImpact[x][y] = 0;
7895
7896       Impact(x, y);
7897     }
7898     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7899     {
7900       if (MovDir[x][y] == MV_NONE)
7901       {
7902         InitMovingField(x, y, MV_DOWN);
7903         started_moving = TRUE;
7904       }
7905     }
7906     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7907     {
7908       if (WasJustFalling[x][y]) // prevent animation from being restarted
7909         MovDir[x][y] = MV_DOWN;
7910
7911       InitMovingField(x, y, MV_DOWN);
7912       started_moving = TRUE;
7913     }
7914     else if (element == EL_AMOEBA_DROP)
7915     {
7916       Tile[x][y] = EL_AMOEBA_GROWING;
7917       Store[x][y] = EL_AMOEBA_WET;
7918     }
7919     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7920               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7921              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7922              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7923     {
7924       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7925                                 (IS_FREE(x - 1, y + 1) ||
7926                                  Tile[x - 1][y + 1] == EL_ACID));
7927       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7928                                 (IS_FREE(x + 1, y + 1) ||
7929                                  Tile[x + 1][y + 1] == EL_ACID));
7930       boolean can_fall_any  = (can_fall_left || can_fall_right);
7931       boolean can_fall_both = (can_fall_left && can_fall_right);
7932       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7933
7934       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7935       {
7936         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7937           can_fall_right = FALSE;
7938         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7939           can_fall_left = FALSE;
7940         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7941           can_fall_right = FALSE;
7942         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7943           can_fall_left = FALSE;
7944
7945         can_fall_any  = (can_fall_left || can_fall_right);
7946         can_fall_both = FALSE;
7947       }
7948
7949       if (can_fall_both)
7950       {
7951         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7952           can_fall_right = FALSE;       // slip down on left side
7953         else
7954           can_fall_left = !(can_fall_right = RND(2));
7955
7956         can_fall_both = FALSE;
7957       }
7958
7959       if (can_fall_any)
7960       {
7961         // if not determined otherwise, prefer left side for slipping down
7962         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7963         started_moving = TRUE;
7964       }
7965     }
7966     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7967     {
7968       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7969       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7970       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7971       int belt_dir = game.belt_dir[belt_nr];
7972
7973       if ((belt_dir == MV_LEFT  && left_is_free) ||
7974           (belt_dir == MV_RIGHT && right_is_free))
7975       {
7976         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7977
7978         InitMovingField(x, y, belt_dir);
7979         started_moving = TRUE;
7980
7981         Pushed[x][y] = TRUE;
7982         Pushed[nextx][y] = TRUE;
7983
7984         GfxAction[x][y] = ACTION_DEFAULT;
7985       }
7986       else
7987       {
7988         MovDir[x][y] = 0;       // if element was moving, stop it
7989       }
7990     }
7991   }
7992
7993   // not "else if" because of elements that can fall and move (EL_SPRING)
7994   if (CAN_MOVE(element) && !started_moving)
7995   {
7996     int move_pattern = element_info[element].move_pattern;
7997     int newx, newy;
7998
7999     Moving2Blocked(x, y, &newx, &newy);
8000
8001     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8002       return;
8003
8004     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8005         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8006     {
8007       WasJustMoving[x][y] = 0;
8008       CheckCollision[x][y] = 0;
8009
8010       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8011
8012       if (Tile[x][y] != element)        // element has changed
8013         return;
8014     }
8015
8016     if (!MovDelay[x][y])        // start new movement phase
8017     {
8018       // all objects that can change their move direction after each step
8019       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8020
8021       if (element != EL_YAMYAM &&
8022           element != EL_DARK_YAMYAM &&
8023           element != EL_PACMAN &&
8024           !(move_pattern & MV_ANY_DIRECTION) &&
8025           move_pattern != MV_TURNING_LEFT &&
8026           move_pattern != MV_TURNING_RIGHT &&
8027           move_pattern != MV_TURNING_LEFT_RIGHT &&
8028           move_pattern != MV_TURNING_RIGHT_LEFT &&
8029           move_pattern != MV_TURNING_RANDOM)
8030       {
8031         TurnRound(x, y);
8032
8033         if (MovDelay[x][y] && (element == EL_BUG ||
8034                                element == EL_SPACESHIP ||
8035                                element == EL_SP_SNIKSNAK ||
8036                                element == EL_SP_ELECTRON ||
8037                                element == EL_MOLE))
8038           TEST_DrawLevelField(x, y);
8039       }
8040     }
8041
8042     if (MovDelay[x][y])         // wait some time before next movement
8043     {
8044       MovDelay[x][y]--;
8045
8046       if (element == EL_ROBOT ||
8047           element == EL_YAMYAM ||
8048           element == EL_DARK_YAMYAM)
8049       {
8050         DrawLevelElementAnimationIfNeeded(x, y, element);
8051         PlayLevelSoundAction(x, y, ACTION_WAITING);
8052       }
8053       else if (element == EL_SP_ELECTRON)
8054         DrawLevelElementAnimationIfNeeded(x, y, element);
8055       else if (element == EL_DRAGON)
8056       {
8057         int i;
8058         int dir = MovDir[x][y];
8059         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8060         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8061         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8062                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8063                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8064                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8065         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8066
8067         GfxAction[x][y] = ACTION_ATTACKING;
8068
8069         if (IS_PLAYER(x, y))
8070           DrawPlayerField(x, y);
8071         else
8072           TEST_DrawLevelField(x, y);
8073
8074         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8075
8076         for (i = 1; i <= 3; i++)
8077         {
8078           int xx = x + i * dx;
8079           int yy = y + i * dy;
8080           int sx = SCREENX(xx);
8081           int sy = SCREENY(yy);
8082           int flame_graphic = graphic + (i - 1);
8083
8084           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8085             break;
8086
8087           if (MovDelay[x][y])
8088           {
8089             int flamed = MovingOrBlocked2Element(xx, yy);
8090
8091             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8092               Bang(xx, yy);
8093             else
8094               RemoveMovingField(xx, yy);
8095
8096             ChangeDelay[xx][yy] = 0;
8097
8098             Tile[xx][yy] = EL_FLAMES;
8099
8100             if (IN_SCR_FIELD(sx, sy))
8101             {
8102               TEST_DrawLevelFieldCrumbled(xx, yy);
8103               DrawGraphic(sx, sy, flame_graphic, frame);
8104             }
8105           }
8106           else
8107           {
8108             if (Tile[xx][yy] == EL_FLAMES)
8109               Tile[xx][yy] = EL_EMPTY;
8110             TEST_DrawLevelField(xx, yy);
8111           }
8112         }
8113       }
8114
8115       if (MovDelay[x][y])       // element still has to wait some time
8116       {
8117         PlayLevelSoundAction(x, y, ACTION_WAITING);
8118
8119         return;
8120       }
8121     }
8122
8123     // now make next step
8124
8125     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8126
8127     if (DONT_COLLIDE_WITH(element) &&
8128         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8129         !PLAYER_ENEMY_PROTECTED(newx, newy))
8130     {
8131       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8132
8133       return;
8134     }
8135
8136     else if (CAN_MOVE_INTO_ACID(element) &&
8137              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8138              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8139              (MovDir[x][y] == MV_DOWN ||
8140               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8141     {
8142       SplashAcid(newx, newy);
8143       Store[x][y] = EL_ACID;
8144     }
8145     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8146     {
8147       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8148           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8149           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8150           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8151       {
8152         RemoveField(x, y);
8153         TEST_DrawLevelField(x, y);
8154
8155         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8156         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8157           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8158
8159         game.friends_still_needed--;
8160         if (!game.friends_still_needed &&
8161             !game.GameOver &&
8162             game.all_players_gone)
8163           LevelSolved();
8164
8165         return;
8166       }
8167       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8168       {
8169         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8170           TEST_DrawLevelField(newx, newy);
8171         else
8172           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8173       }
8174       else if (!IS_FREE(newx, newy))
8175       {
8176         GfxAction[x][y] = ACTION_WAITING;
8177
8178         if (IS_PLAYER(x, y))
8179           DrawPlayerField(x, y);
8180         else
8181           TEST_DrawLevelField(x, y);
8182
8183         return;
8184       }
8185     }
8186     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8187     {
8188       if (IS_FOOD_PIG(Tile[newx][newy]))
8189       {
8190         if (IS_MOVING(newx, newy))
8191           RemoveMovingField(newx, newy);
8192         else
8193         {
8194           Tile[newx][newy] = EL_EMPTY;
8195           TEST_DrawLevelField(newx, newy);
8196         }
8197
8198         PlayLevelSound(x, y, SND_PIG_DIGGING);
8199       }
8200       else if (!IS_FREE(newx, newy))
8201       {
8202         if (IS_PLAYER(x, y))
8203           DrawPlayerField(x, y);
8204         else
8205           TEST_DrawLevelField(x, y);
8206
8207         return;
8208       }
8209     }
8210     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8211     {
8212       if (Store[x][y] != EL_EMPTY)
8213       {
8214         boolean can_clone = FALSE;
8215         int xx, yy;
8216
8217         // check if element to clone is still there
8218         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8219         {
8220           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8221           {
8222             can_clone = TRUE;
8223
8224             break;
8225           }
8226         }
8227
8228         // cannot clone or target field not free anymore -- do not clone
8229         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8230           Store[x][y] = EL_EMPTY;
8231       }
8232
8233       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8234       {
8235         if (IS_MV_DIAGONAL(MovDir[x][y]))
8236         {
8237           int diagonal_move_dir = MovDir[x][y];
8238           int stored = Store[x][y];
8239           int change_delay = 8;
8240           int graphic;
8241
8242           // android is moving diagonally
8243
8244           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8245
8246           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8247           GfxElement[x][y] = EL_EMC_ANDROID;
8248           GfxAction[x][y] = ACTION_SHRINKING;
8249           GfxDir[x][y] = diagonal_move_dir;
8250           ChangeDelay[x][y] = change_delay;
8251
8252           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8253                                    GfxDir[x][y]);
8254
8255           DrawLevelGraphicAnimation(x, y, graphic);
8256           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8257
8258           if (Tile[newx][newy] == EL_ACID)
8259           {
8260             SplashAcid(newx, newy);
8261
8262             return;
8263           }
8264
8265           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8266
8267           Store[newx][newy] = EL_EMC_ANDROID;
8268           GfxElement[newx][newy] = EL_EMC_ANDROID;
8269           GfxAction[newx][newy] = ACTION_GROWING;
8270           GfxDir[newx][newy] = diagonal_move_dir;
8271           ChangeDelay[newx][newy] = change_delay;
8272
8273           graphic = el_act_dir2img(GfxElement[newx][newy],
8274                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8275
8276           DrawLevelGraphicAnimation(newx, newy, graphic);
8277           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8278
8279           return;
8280         }
8281         else
8282         {
8283           Tile[newx][newy] = EL_EMPTY;
8284           TEST_DrawLevelField(newx, newy);
8285
8286           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8287         }
8288       }
8289       else if (!IS_FREE(newx, newy))
8290       {
8291         return;
8292       }
8293     }
8294     else if (IS_CUSTOM_ELEMENT(element) &&
8295              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8296     {
8297       if (!DigFieldByCE(newx, newy, element))
8298         return;
8299
8300       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8301       {
8302         RunnerVisit[x][y] = FrameCounter;
8303         PlayerVisit[x][y] /= 8;         // expire player visit path
8304       }
8305     }
8306     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8307     {
8308       if (!IS_FREE(newx, newy))
8309       {
8310         if (IS_PLAYER(x, y))
8311           DrawPlayerField(x, y);
8312         else
8313           TEST_DrawLevelField(x, y);
8314
8315         return;
8316       }
8317       else
8318       {
8319         boolean wanna_flame = !RND(10);
8320         int dx = newx - x, dy = newy - y;
8321         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8322         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8323         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8324                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8325         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8326                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8327
8328         if ((wanna_flame ||
8329              IS_CLASSIC_ENEMY(element1) ||
8330              IS_CLASSIC_ENEMY(element2)) &&
8331             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8332             element1 != EL_FLAMES && element2 != EL_FLAMES)
8333         {
8334           ResetGfxAnimation(x, y);
8335           GfxAction[x][y] = ACTION_ATTACKING;
8336
8337           if (IS_PLAYER(x, y))
8338             DrawPlayerField(x, y);
8339           else
8340             TEST_DrawLevelField(x, y);
8341
8342           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8343
8344           MovDelay[x][y] = 50;
8345
8346           Tile[newx][newy] = EL_FLAMES;
8347           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8348             Tile[newx1][newy1] = EL_FLAMES;
8349           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8350             Tile[newx2][newy2] = EL_FLAMES;
8351
8352           return;
8353         }
8354       }
8355     }
8356     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8357              Tile[newx][newy] == EL_DIAMOND)
8358     {
8359       if (IS_MOVING(newx, newy))
8360         RemoveMovingField(newx, newy);
8361       else
8362       {
8363         Tile[newx][newy] = EL_EMPTY;
8364         TEST_DrawLevelField(newx, newy);
8365       }
8366
8367       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8368     }
8369     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8370              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8371     {
8372       if (AmoebaNr[newx][newy])
8373       {
8374         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8375         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8376             Tile[newx][newy] == EL_BD_AMOEBA)
8377           AmoebaCnt[AmoebaNr[newx][newy]]--;
8378       }
8379
8380       if (IS_MOVING(newx, newy))
8381       {
8382         RemoveMovingField(newx, newy);
8383       }
8384       else
8385       {
8386         Tile[newx][newy] = EL_EMPTY;
8387         TEST_DrawLevelField(newx, newy);
8388       }
8389
8390       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8391     }
8392     else if ((element == EL_PACMAN || element == EL_MOLE)
8393              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8394     {
8395       if (AmoebaNr[newx][newy])
8396       {
8397         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8398         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8399             Tile[newx][newy] == EL_BD_AMOEBA)
8400           AmoebaCnt[AmoebaNr[newx][newy]]--;
8401       }
8402
8403       if (element == EL_MOLE)
8404       {
8405         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8406         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8407
8408         ResetGfxAnimation(x, y);
8409         GfxAction[x][y] = ACTION_DIGGING;
8410         TEST_DrawLevelField(x, y);
8411
8412         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8413
8414         return;                         // wait for shrinking amoeba
8415       }
8416       else      // element == EL_PACMAN
8417       {
8418         Tile[newx][newy] = EL_EMPTY;
8419         TEST_DrawLevelField(newx, newy);
8420         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8421       }
8422     }
8423     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8424              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8425               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8426     {
8427       // wait for shrinking amoeba to completely disappear
8428       return;
8429     }
8430     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8431     {
8432       // object was running against a wall
8433
8434       TurnRound(x, y);
8435
8436       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8437         DrawLevelElementAnimation(x, y, element);
8438
8439       if (DONT_TOUCH(element))
8440         TestIfBadThingTouchesPlayer(x, y);
8441
8442       return;
8443     }
8444
8445     InitMovingField(x, y, MovDir[x][y]);
8446
8447     PlayLevelSoundAction(x, y, ACTION_MOVING);
8448   }
8449
8450   if (MovDir[x][y])
8451     ContinueMoving(x, y);
8452 }
8453
8454 void ContinueMoving(int x, int y)
8455 {
8456   int element = Tile[x][y];
8457   struct ElementInfo *ei = &element_info[element];
8458   int direction = MovDir[x][y];
8459   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8460   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8461   int newx = x + dx, newy = y + dy;
8462   int stored = Store[x][y];
8463   int stored_new = Store[newx][newy];
8464   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8465   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8466   boolean last_line = (newy == lev_fieldy - 1);
8467
8468   MovPos[x][y] += getElementMoveStepsize(x, y);
8469
8470   if (pushed_by_player) // special case: moving object pushed by player
8471     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8472
8473   if (ABS(MovPos[x][y]) < TILEX)
8474   {
8475     TEST_DrawLevelField(x, y);
8476
8477     return;     // element is still moving
8478   }
8479
8480   // element reached destination field
8481
8482   Tile[x][y] = EL_EMPTY;
8483   Tile[newx][newy] = element;
8484   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8485
8486   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8487   {
8488     element = Tile[newx][newy] = EL_ACID;
8489   }
8490   else if (element == EL_MOLE)
8491   {
8492     Tile[x][y] = EL_SAND;
8493
8494     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8495   }
8496   else if (element == EL_QUICKSAND_FILLING)
8497   {
8498     element = Tile[newx][newy] = get_next_element(element);
8499     Store[newx][newy] = Store[x][y];
8500   }
8501   else if (element == EL_QUICKSAND_EMPTYING)
8502   {
8503     Tile[x][y] = get_next_element(element);
8504     element = Tile[newx][newy] = Store[x][y];
8505   }
8506   else if (element == EL_QUICKSAND_FAST_FILLING)
8507   {
8508     element = Tile[newx][newy] = get_next_element(element);
8509     Store[newx][newy] = Store[x][y];
8510   }
8511   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8512   {
8513     Tile[x][y] = get_next_element(element);
8514     element = Tile[newx][newy] = Store[x][y];
8515   }
8516   else if (element == EL_MAGIC_WALL_FILLING)
8517   {
8518     element = Tile[newx][newy] = get_next_element(element);
8519     if (!game.magic_wall_active)
8520       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8521     Store[newx][newy] = Store[x][y];
8522   }
8523   else if (element == EL_MAGIC_WALL_EMPTYING)
8524   {
8525     Tile[x][y] = get_next_element(element);
8526     if (!game.magic_wall_active)
8527       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8528     element = Tile[newx][newy] = Store[x][y];
8529
8530     InitField(newx, newy, FALSE);
8531   }
8532   else if (element == EL_BD_MAGIC_WALL_FILLING)
8533   {
8534     element = Tile[newx][newy] = get_next_element(element);
8535     if (!game.magic_wall_active)
8536       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8537     Store[newx][newy] = Store[x][y];
8538   }
8539   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8540   {
8541     Tile[x][y] = get_next_element(element);
8542     if (!game.magic_wall_active)
8543       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8544     element = Tile[newx][newy] = Store[x][y];
8545
8546     InitField(newx, newy, FALSE);
8547   }
8548   else if (element == EL_DC_MAGIC_WALL_FILLING)
8549   {
8550     element = Tile[newx][newy] = get_next_element(element);
8551     if (!game.magic_wall_active)
8552       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8553     Store[newx][newy] = Store[x][y];
8554   }
8555   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8556   {
8557     Tile[x][y] = get_next_element(element);
8558     if (!game.magic_wall_active)
8559       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8560     element = Tile[newx][newy] = Store[x][y];
8561
8562     InitField(newx, newy, FALSE);
8563   }
8564   else if (element == EL_AMOEBA_DROPPING)
8565   {
8566     Tile[x][y] = get_next_element(element);
8567     element = Tile[newx][newy] = Store[x][y];
8568   }
8569   else if (element == EL_SOKOBAN_OBJECT)
8570   {
8571     if (Back[x][y])
8572       Tile[x][y] = Back[x][y];
8573
8574     if (Back[newx][newy])
8575       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8576
8577     Back[x][y] = Back[newx][newy] = 0;
8578   }
8579
8580   Store[x][y] = EL_EMPTY;
8581   MovPos[x][y] = 0;
8582   MovDir[x][y] = 0;
8583   MovDelay[x][y] = 0;
8584
8585   MovDelay[newx][newy] = 0;
8586
8587   if (CAN_CHANGE_OR_HAS_ACTION(element))
8588   {
8589     // copy element change control values to new field
8590     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8591     ChangePage[newx][newy]  = ChangePage[x][y];
8592     ChangeCount[newx][newy] = ChangeCount[x][y];
8593     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8594   }
8595
8596   CustomValue[newx][newy] = CustomValue[x][y];
8597
8598   ChangeDelay[x][y] = 0;
8599   ChangePage[x][y] = -1;
8600   ChangeCount[x][y] = 0;
8601   ChangeEvent[x][y] = -1;
8602
8603   CustomValue[x][y] = 0;
8604
8605   // copy animation control values to new field
8606   GfxFrame[newx][newy]  = GfxFrame[x][y];
8607   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8608   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8609   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8610
8611   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8612
8613   // some elements can leave other elements behind after moving
8614   if (ei->move_leave_element != EL_EMPTY &&
8615       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8616       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8617   {
8618     int move_leave_element = ei->move_leave_element;
8619
8620     // this makes it possible to leave the removed element again
8621     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8622       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8623
8624     Tile[x][y] = move_leave_element;
8625
8626     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8627       MovDir[x][y] = direction;
8628
8629     InitField(x, y, FALSE);
8630
8631     if (GFX_CRUMBLED(Tile[x][y]))
8632       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8633
8634     if (ELEM_IS_PLAYER(move_leave_element))
8635       RelocatePlayer(x, y, move_leave_element);
8636   }
8637
8638   // do this after checking for left-behind element
8639   ResetGfxAnimation(x, y);      // reset animation values for old field
8640
8641   if (!CAN_MOVE(element) ||
8642       (CAN_FALL(element) && direction == MV_DOWN &&
8643        (element == EL_SPRING ||
8644         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8645         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8646     GfxDir[x][y] = MovDir[newx][newy] = 0;
8647
8648   TEST_DrawLevelField(x, y);
8649   TEST_DrawLevelField(newx, newy);
8650
8651   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8652
8653   // prevent pushed element from moving on in pushed direction
8654   if (pushed_by_player && CAN_MOVE(element) &&
8655       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8656       !(element_info[element].move_pattern & direction))
8657     TurnRound(newx, newy);
8658
8659   // prevent elements on conveyor belt from moving on in last direction
8660   if (pushed_by_conveyor && CAN_FALL(element) &&
8661       direction & MV_HORIZONTAL)
8662     MovDir[newx][newy] = 0;
8663
8664   if (!pushed_by_player)
8665   {
8666     int nextx = newx + dx, nexty = newy + dy;
8667     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8668
8669     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8670
8671     if (CAN_FALL(element) && direction == MV_DOWN)
8672       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8673
8674     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8675       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8676
8677     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8678       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8679   }
8680
8681   if (DONT_TOUCH(element))      // object may be nasty to player or others
8682   {
8683     TestIfBadThingTouchesPlayer(newx, newy);
8684     TestIfBadThingTouchesFriend(newx, newy);
8685
8686     if (!IS_CUSTOM_ELEMENT(element))
8687       TestIfBadThingTouchesOtherBadThing(newx, newy);
8688   }
8689   else if (element == EL_PENGUIN)
8690     TestIfFriendTouchesBadThing(newx, newy);
8691
8692   if (DONT_GET_HIT_BY(element))
8693   {
8694     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8695   }
8696
8697   // give the player one last chance (one more frame) to move away
8698   if (CAN_FALL(element) && direction == MV_DOWN &&
8699       (last_line || (!IS_FREE(x, newy + 1) &&
8700                      (!IS_PLAYER(x, newy + 1) ||
8701                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8702     Impact(x, newy);
8703
8704   if (pushed_by_player && !game.use_change_when_pushing_bug)
8705   {
8706     int push_side = MV_DIR_OPPOSITE(direction);
8707     struct PlayerInfo *player = PLAYERINFO(x, y);
8708
8709     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8710                                player->index_bit, push_side);
8711     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8712                                         player->index_bit, push_side);
8713   }
8714
8715   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8716     MovDelay[newx][newy] = 1;
8717
8718   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8719
8720   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8721   TestIfElementHitsCustomElement(newx, newy, direction);
8722   TestIfPlayerTouchesCustomElement(newx, newy);
8723   TestIfElementTouchesCustomElement(newx, newy);
8724
8725   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8726       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8727     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8728                              MV_DIR_OPPOSITE(direction));
8729 }
8730
8731 int AmoebaNeighbourNr(int ax, int ay)
8732 {
8733   int i;
8734   int element = Tile[ax][ay];
8735   int group_nr = 0;
8736   static int xy[4][2] =
8737   {
8738     { 0, -1 },
8739     { -1, 0 },
8740     { +1, 0 },
8741     { 0, +1 }
8742   };
8743
8744   for (i = 0; i < NUM_DIRECTIONS; i++)
8745   {
8746     int x = ax + xy[i][0];
8747     int y = ay + xy[i][1];
8748
8749     if (!IN_LEV_FIELD(x, y))
8750       continue;
8751
8752     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8753       group_nr = AmoebaNr[x][y];
8754   }
8755
8756   return group_nr;
8757 }
8758
8759 static void AmoebaMerge(int ax, int ay)
8760 {
8761   int i, x, y, xx, yy;
8762   int new_group_nr = AmoebaNr[ax][ay];
8763   static int xy[4][2] =
8764   {
8765     { 0, -1 },
8766     { -1, 0 },
8767     { +1, 0 },
8768     { 0, +1 }
8769   };
8770
8771   if (new_group_nr == 0)
8772     return;
8773
8774   for (i = 0; i < NUM_DIRECTIONS; i++)
8775   {
8776     x = ax + xy[i][0];
8777     y = ay + xy[i][1];
8778
8779     if (!IN_LEV_FIELD(x, y))
8780       continue;
8781
8782     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8783          Tile[x][y] == EL_BD_AMOEBA ||
8784          Tile[x][y] == EL_AMOEBA_DEAD) &&
8785         AmoebaNr[x][y] != new_group_nr)
8786     {
8787       int old_group_nr = AmoebaNr[x][y];
8788
8789       if (old_group_nr == 0)
8790         return;
8791
8792       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8793       AmoebaCnt[old_group_nr] = 0;
8794       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8795       AmoebaCnt2[old_group_nr] = 0;
8796
8797       SCAN_PLAYFIELD(xx, yy)
8798       {
8799         if (AmoebaNr[xx][yy] == old_group_nr)
8800           AmoebaNr[xx][yy] = new_group_nr;
8801       }
8802     }
8803   }
8804 }
8805
8806 void AmoebaToDiamond(int ax, int ay)
8807 {
8808   int i, x, y;
8809
8810   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8811   {
8812     int group_nr = AmoebaNr[ax][ay];
8813
8814 #ifdef DEBUG
8815     if (group_nr == 0)
8816     {
8817       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8818       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8819
8820       return;
8821     }
8822 #endif
8823
8824     SCAN_PLAYFIELD(x, y)
8825     {
8826       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8827       {
8828         AmoebaNr[x][y] = 0;
8829         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8830       }
8831     }
8832
8833     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8834                             SND_AMOEBA_TURNING_TO_GEM :
8835                             SND_AMOEBA_TURNING_TO_ROCK));
8836     Bang(ax, ay);
8837   }
8838   else
8839   {
8840     static int xy[4][2] =
8841     {
8842       { 0, -1 },
8843       { -1, 0 },
8844       { +1, 0 },
8845       { 0, +1 }
8846     };
8847
8848     for (i = 0; i < NUM_DIRECTIONS; i++)
8849     {
8850       x = ax + xy[i][0];
8851       y = ay + xy[i][1];
8852
8853       if (!IN_LEV_FIELD(x, y))
8854         continue;
8855
8856       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8857       {
8858         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8859                               SND_AMOEBA_TURNING_TO_GEM :
8860                               SND_AMOEBA_TURNING_TO_ROCK));
8861         Bang(x, y);
8862       }
8863     }
8864   }
8865 }
8866
8867 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8868 {
8869   int x, y;
8870   int group_nr = AmoebaNr[ax][ay];
8871   boolean done = FALSE;
8872
8873 #ifdef DEBUG
8874   if (group_nr == 0)
8875   {
8876     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8877     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8878
8879     return;
8880   }
8881 #endif
8882
8883   SCAN_PLAYFIELD(x, y)
8884   {
8885     if (AmoebaNr[x][y] == group_nr &&
8886         (Tile[x][y] == EL_AMOEBA_DEAD ||
8887          Tile[x][y] == EL_BD_AMOEBA ||
8888          Tile[x][y] == EL_AMOEBA_GROWING))
8889     {
8890       AmoebaNr[x][y] = 0;
8891       Tile[x][y] = new_element;
8892       InitField(x, y, FALSE);
8893       TEST_DrawLevelField(x, y);
8894       done = TRUE;
8895     }
8896   }
8897
8898   if (done)
8899     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8900                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8901                             SND_BD_AMOEBA_TURNING_TO_GEM));
8902 }
8903
8904 static void AmoebaGrowing(int x, int y)
8905 {
8906   static unsigned int sound_delay = 0;
8907   static unsigned int sound_delay_value = 0;
8908
8909   if (!MovDelay[x][y])          // start new growing cycle
8910   {
8911     MovDelay[x][y] = 7;
8912
8913     if (DelayReached(&sound_delay, sound_delay_value))
8914     {
8915       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8916       sound_delay_value = 30;
8917     }
8918   }
8919
8920   if (MovDelay[x][y])           // wait some time before growing bigger
8921   {
8922     MovDelay[x][y]--;
8923     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8924     {
8925       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8926                                            6 - MovDelay[x][y]);
8927
8928       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8929     }
8930
8931     if (!MovDelay[x][y])
8932     {
8933       Tile[x][y] = Store[x][y];
8934       Store[x][y] = 0;
8935       TEST_DrawLevelField(x, y);
8936     }
8937   }
8938 }
8939
8940 static void AmoebaShrinking(int x, int y)
8941 {
8942   static unsigned int sound_delay = 0;
8943   static unsigned int sound_delay_value = 0;
8944
8945   if (!MovDelay[x][y])          // start new shrinking cycle
8946   {
8947     MovDelay[x][y] = 7;
8948
8949     if (DelayReached(&sound_delay, sound_delay_value))
8950       sound_delay_value = 30;
8951   }
8952
8953   if (MovDelay[x][y])           // wait some time before shrinking
8954   {
8955     MovDelay[x][y]--;
8956     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8957     {
8958       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8959                                            6 - MovDelay[x][y]);
8960
8961       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8962     }
8963
8964     if (!MovDelay[x][y])
8965     {
8966       Tile[x][y] = EL_EMPTY;
8967       TEST_DrawLevelField(x, y);
8968
8969       // don't let mole enter this field in this cycle;
8970       // (give priority to objects falling to this field from above)
8971       Stop[x][y] = TRUE;
8972     }
8973   }
8974 }
8975
8976 static void AmoebaReproduce(int ax, int ay)
8977 {
8978   int i;
8979   int element = Tile[ax][ay];
8980   int graphic = el2img(element);
8981   int newax = ax, neway = ay;
8982   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8983   static int xy[4][2] =
8984   {
8985     { 0, -1 },
8986     { -1, 0 },
8987     { +1, 0 },
8988     { 0, +1 }
8989   };
8990
8991   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8992   {
8993     Tile[ax][ay] = EL_AMOEBA_DEAD;
8994     TEST_DrawLevelField(ax, ay);
8995     return;
8996   }
8997
8998   if (IS_ANIMATED(graphic))
8999     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9000
9001   if (!MovDelay[ax][ay])        // start making new amoeba field
9002     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9003
9004   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9005   {
9006     MovDelay[ax][ay]--;
9007     if (MovDelay[ax][ay])
9008       return;
9009   }
9010
9011   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9012   {
9013     int start = RND(4);
9014     int x = ax + xy[start][0];
9015     int y = ay + xy[start][1];
9016
9017     if (!IN_LEV_FIELD(x, y))
9018       return;
9019
9020     if (IS_FREE(x, y) ||
9021         CAN_GROW_INTO(Tile[x][y]) ||
9022         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9023         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9024     {
9025       newax = x;
9026       neway = y;
9027     }
9028
9029     if (newax == ax && neway == ay)
9030       return;
9031   }
9032   else                          // normal or "filled" (BD style) amoeba
9033   {
9034     int start = RND(4);
9035     boolean waiting_for_player = FALSE;
9036
9037     for (i = 0; i < NUM_DIRECTIONS; i++)
9038     {
9039       int j = (start + i) % 4;
9040       int x = ax + xy[j][0];
9041       int y = ay + xy[j][1];
9042
9043       if (!IN_LEV_FIELD(x, y))
9044         continue;
9045
9046       if (IS_FREE(x, y) ||
9047           CAN_GROW_INTO(Tile[x][y]) ||
9048           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9049           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9050       {
9051         newax = x;
9052         neway = y;
9053         break;
9054       }
9055       else if (IS_PLAYER(x, y))
9056         waiting_for_player = TRUE;
9057     }
9058
9059     if (newax == ax && neway == ay)             // amoeba cannot grow
9060     {
9061       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9062       {
9063         Tile[ax][ay] = EL_AMOEBA_DEAD;
9064         TEST_DrawLevelField(ax, ay);
9065         AmoebaCnt[AmoebaNr[ax][ay]]--;
9066
9067         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9068         {
9069           if (element == EL_AMOEBA_FULL)
9070             AmoebaToDiamond(ax, ay);
9071           else if (element == EL_BD_AMOEBA)
9072             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9073         }
9074       }
9075       return;
9076     }
9077     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9078     {
9079       // amoeba gets larger by growing in some direction
9080
9081       int new_group_nr = AmoebaNr[ax][ay];
9082
9083 #ifdef DEBUG
9084   if (new_group_nr == 0)
9085   {
9086     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9087           newax, neway);
9088     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9089
9090     return;
9091   }
9092 #endif
9093
9094       AmoebaNr[newax][neway] = new_group_nr;
9095       AmoebaCnt[new_group_nr]++;
9096       AmoebaCnt2[new_group_nr]++;
9097
9098       // if amoeba touches other amoeba(s) after growing, unify them
9099       AmoebaMerge(newax, neway);
9100
9101       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9102       {
9103         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9104         return;
9105       }
9106     }
9107   }
9108
9109   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9110       (neway == lev_fieldy - 1 && newax != ax))
9111   {
9112     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9113     Store[newax][neway] = element;
9114   }
9115   else if (neway == ay || element == EL_EMC_DRIPPER)
9116   {
9117     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9118
9119     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9120   }
9121   else
9122   {
9123     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9124     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9125     Store[ax][ay] = EL_AMOEBA_DROP;
9126     ContinueMoving(ax, ay);
9127     return;
9128   }
9129
9130   TEST_DrawLevelField(newax, neway);
9131 }
9132
9133 static void Life(int ax, int ay)
9134 {
9135   int x1, y1, x2, y2;
9136   int life_time = 40;
9137   int element = Tile[ax][ay];
9138   int graphic = el2img(element);
9139   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9140                          level.biomaze);
9141   boolean changed = FALSE;
9142
9143   if (IS_ANIMATED(graphic))
9144     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9145
9146   if (Stop[ax][ay])
9147     return;
9148
9149   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9150     MovDelay[ax][ay] = life_time;
9151
9152   if (MovDelay[ax][ay])         // wait some time before next cycle
9153   {
9154     MovDelay[ax][ay]--;
9155     if (MovDelay[ax][ay])
9156       return;
9157   }
9158
9159   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9160   {
9161     int xx = ax+x1, yy = ay+y1;
9162     int old_element = Tile[xx][yy];
9163     int num_neighbours = 0;
9164
9165     if (!IN_LEV_FIELD(xx, yy))
9166       continue;
9167
9168     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9169     {
9170       int x = xx+x2, y = yy+y2;
9171
9172       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9173         continue;
9174
9175       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9176       boolean is_neighbour = FALSE;
9177
9178       if (level.use_life_bugs)
9179         is_neighbour =
9180           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9181            (IS_FREE(x, y)                             &&  Stop[x][y]));
9182       else
9183         is_neighbour =
9184           (Last[x][y] == element || is_player_cell);
9185
9186       if (is_neighbour)
9187         num_neighbours++;
9188     }
9189
9190     boolean is_free = FALSE;
9191
9192     if (level.use_life_bugs)
9193       is_free = (IS_FREE(xx, yy));
9194     else
9195       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9196
9197     if (xx == ax && yy == ay)           // field in the middle
9198     {
9199       if (num_neighbours < life_parameter[0] ||
9200           num_neighbours > life_parameter[1])
9201       {
9202         Tile[xx][yy] = EL_EMPTY;
9203         if (Tile[xx][yy] != old_element)
9204           TEST_DrawLevelField(xx, yy);
9205         Stop[xx][yy] = TRUE;
9206         changed = TRUE;
9207       }
9208     }
9209     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9210     {                                   // free border field
9211       if (num_neighbours >= life_parameter[2] &&
9212           num_neighbours <= life_parameter[3])
9213       {
9214         Tile[xx][yy] = element;
9215         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9216         if (Tile[xx][yy] != old_element)
9217           TEST_DrawLevelField(xx, yy);
9218         Stop[xx][yy] = TRUE;
9219         changed = TRUE;
9220       }
9221     }
9222   }
9223
9224   if (changed)
9225     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9226                    SND_GAME_OF_LIFE_GROWING);
9227 }
9228
9229 static void InitRobotWheel(int x, int y)
9230 {
9231   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9232 }
9233
9234 static void RunRobotWheel(int x, int y)
9235 {
9236   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9237 }
9238
9239 static void StopRobotWheel(int x, int y)
9240 {
9241   if (game.robot_wheel_x == x &&
9242       game.robot_wheel_y == y)
9243   {
9244     game.robot_wheel_x = -1;
9245     game.robot_wheel_y = -1;
9246     game.robot_wheel_active = FALSE;
9247   }
9248 }
9249
9250 static void InitTimegateWheel(int x, int y)
9251 {
9252   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9253 }
9254
9255 static void RunTimegateWheel(int x, int y)
9256 {
9257   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9258 }
9259
9260 static void InitMagicBallDelay(int x, int y)
9261 {
9262   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9263 }
9264
9265 static void ActivateMagicBall(int bx, int by)
9266 {
9267   int x, y;
9268
9269   if (level.ball_random)
9270   {
9271     int pos_border = RND(8);    // select one of the eight border elements
9272     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9273     int xx = pos_content % 3;
9274     int yy = pos_content / 3;
9275
9276     x = bx - 1 + xx;
9277     y = by - 1 + yy;
9278
9279     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9280       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9281   }
9282   else
9283   {
9284     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9285     {
9286       int xx = x - bx + 1;
9287       int yy = y - by + 1;
9288
9289       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9290         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9291     }
9292   }
9293
9294   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9295 }
9296
9297 static void CheckExit(int x, int y)
9298 {
9299   if (game.gems_still_needed > 0 ||
9300       game.sokoban_fields_still_needed > 0 ||
9301       game.sokoban_objects_still_needed > 0 ||
9302       game.lights_still_needed > 0)
9303   {
9304     int element = Tile[x][y];
9305     int graphic = el2img(element);
9306
9307     if (IS_ANIMATED(graphic))
9308       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9309
9310     return;
9311   }
9312
9313   // do not re-open exit door closed after last player
9314   if (game.all_players_gone)
9315     return;
9316
9317   Tile[x][y] = EL_EXIT_OPENING;
9318
9319   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9320 }
9321
9322 static void CheckExitEM(int x, int y)
9323 {
9324   if (game.gems_still_needed > 0 ||
9325       game.sokoban_fields_still_needed > 0 ||
9326       game.sokoban_objects_still_needed > 0 ||
9327       game.lights_still_needed > 0)
9328   {
9329     int element = Tile[x][y];
9330     int graphic = el2img(element);
9331
9332     if (IS_ANIMATED(graphic))
9333       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9334
9335     return;
9336   }
9337
9338   // do not re-open exit door closed after last player
9339   if (game.all_players_gone)
9340     return;
9341
9342   Tile[x][y] = EL_EM_EXIT_OPENING;
9343
9344   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9345 }
9346
9347 static void CheckExitSteel(int x, int y)
9348 {
9349   if (game.gems_still_needed > 0 ||
9350       game.sokoban_fields_still_needed > 0 ||
9351       game.sokoban_objects_still_needed > 0 ||
9352       game.lights_still_needed > 0)
9353   {
9354     int element = Tile[x][y];
9355     int graphic = el2img(element);
9356
9357     if (IS_ANIMATED(graphic))
9358       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9359
9360     return;
9361   }
9362
9363   // do not re-open exit door closed after last player
9364   if (game.all_players_gone)
9365     return;
9366
9367   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9368
9369   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9370 }
9371
9372 static void CheckExitSteelEM(int x, int y)
9373 {
9374   if (game.gems_still_needed > 0 ||
9375       game.sokoban_fields_still_needed > 0 ||
9376       game.sokoban_objects_still_needed > 0 ||
9377       game.lights_still_needed > 0)
9378   {
9379     int element = Tile[x][y];
9380     int graphic = el2img(element);
9381
9382     if (IS_ANIMATED(graphic))
9383       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9384
9385     return;
9386   }
9387
9388   // do not re-open exit door closed after last player
9389   if (game.all_players_gone)
9390     return;
9391
9392   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9393
9394   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9395 }
9396
9397 static void CheckExitSP(int x, int y)
9398 {
9399   if (game.gems_still_needed > 0)
9400   {
9401     int element = Tile[x][y];
9402     int graphic = el2img(element);
9403
9404     if (IS_ANIMATED(graphic))
9405       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9406
9407     return;
9408   }
9409
9410   // do not re-open exit door closed after last player
9411   if (game.all_players_gone)
9412     return;
9413
9414   Tile[x][y] = EL_SP_EXIT_OPENING;
9415
9416   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9417 }
9418
9419 static void CloseAllOpenTimegates(void)
9420 {
9421   int x, y;
9422
9423   SCAN_PLAYFIELD(x, y)
9424   {
9425     int element = Tile[x][y];
9426
9427     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9428     {
9429       Tile[x][y] = EL_TIMEGATE_CLOSING;
9430
9431       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9432     }
9433   }
9434 }
9435
9436 static void DrawTwinkleOnField(int x, int y)
9437 {
9438   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9439     return;
9440
9441   if (Tile[x][y] == EL_BD_DIAMOND)
9442     return;
9443
9444   if (MovDelay[x][y] == 0)      // next animation frame
9445     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9446
9447   if (MovDelay[x][y] != 0)      // wait some time before next frame
9448   {
9449     MovDelay[x][y]--;
9450
9451     DrawLevelElementAnimation(x, y, Tile[x][y]);
9452
9453     if (MovDelay[x][y] != 0)
9454     {
9455       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9456                                            10 - MovDelay[x][y]);
9457
9458       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9459     }
9460   }
9461 }
9462
9463 static void MauerWaechst(int x, int y)
9464 {
9465   int delay = 6;
9466
9467   if (!MovDelay[x][y])          // next animation frame
9468     MovDelay[x][y] = 3 * delay;
9469
9470   if (MovDelay[x][y])           // wait some time before next frame
9471   {
9472     MovDelay[x][y]--;
9473
9474     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9475     {
9476       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9477       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9478
9479       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9480     }
9481
9482     if (!MovDelay[x][y])
9483     {
9484       if (MovDir[x][y] == MV_LEFT)
9485       {
9486         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9487           TEST_DrawLevelField(x - 1, y);
9488       }
9489       else if (MovDir[x][y] == MV_RIGHT)
9490       {
9491         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9492           TEST_DrawLevelField(x + 1, y);
9493       }
9494       else if (MovDir[x][y] == MV_UP)
9495       {
9496         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9497           TEST_DrawLevelField(x, y - 1);
9498       }
9499       else
9500       {
9501         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9502           TEST_DrawLevelField(x, y + 1);
9503       }
9504
9505       Tile[x][y] = Store[x][y];
9506       Store[x][y] = 0;
9507       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9508       TEST_DrawLevelField(x, y);
9509     }
9510   }
9511 }
9512
9513 static void MauerAbleger(int ax, int ay)
9514 {
9515   int element = Tile[ax][ay];
9516   int graphic = el2img(element);
9517   boolean oben_frei = FALSE, unten_frei = FALSE;
9518   boolean links_frei = FALSE, rechts_frei = FALSE;
9519   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9520   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9521   boolean new_wall = FALSE;
9522
9523   if (IS_ANIMATED(graphic))
9524     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9525
9526   if (!MovDelay[ax][ay])        // start building new wall
9527     MovDelay[ax][ay] = 6;
9528
9529   if (MovDelay[ax][ay])         // wait some time before building new wall
9530   {
9531     MovDelay[ax][ay]--;
9532     if (MovDelay[ax][ay])
9533       return;
9534   }
9535
9536   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9537     oben_frei = TRUE;
9538   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9539     unten_frei = TRUE;
9540   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9541     links_frei = TRUE;
9542   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9543     rechts_frei = TRUE;
9544
9545   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9546       element == EL_EXPANDABLE_WALL_ANY)
9547   {
9548     if (oben_frei)
9549     {
9550       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9551       Store[ax][ay-1] = element;
9552       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9553       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9554         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9555                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9556       new_wall = TRUE;
9557     }
9558     if (unten_frei)
9559     {
9560       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9561       Store[ax][ay+1] = element;
9562       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9563       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9564         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9565                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9566       new_wall = TRUE;
9567     }
9568   }
9569
9570   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9571       element == EL_EXPANDABLE_WALL_ANY ||
9572       element == EL_EXPANDABLE_WALL ||
9573       element == EL_BD_EXPANDABLE_WALL)
9574   {
9575     if (links_frei)
9576     {
9577       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9578       Store[ax-1][ay] = element;
9579       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9580       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9581         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9582                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9583       new_wall = TRUE;
9584     }
9585
9586     if (rechts_frei)
9587     {
9588       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9589       Store[ax+1][ay] = element;
9590       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9591       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9592         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9593                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9594       new_wall = TRUE;
9595     }
9596   }
9597
9598   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9599     TEST_DrawLevelField(ax, ay);
9600
9601   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9602     oben_massiv = TRUE;
9603   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9604     unten_massiv = TRUE;
9605   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9606     links_massiv = TRUE;
9607   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9608     rechts_massiv = TRUE;
9609
9610   if (((oben_massiv && unten_massiv) ||
9611        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9612        element == EL_EXPANDABLE_WALL) &&
9613       ((links_massiv && rechts_massiv) ||
9614        element == EL_EXPANDABLE_WALL_VERTICAL))
9615     Tile[ax][ay] = EL_WALL;
9616
9617   if (new_wall)
9618     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9619 }
9620
9621 static void MauerAblegerStahl(int ax, int ay)
9622 {
9623   int element = Tile[ax][ay];
9624   int graphic = el2img(element);
9625   boolean oben_frei = FALSE, unten_frei = FALSE;
9626   boolean links_frei = FALSE, rechts_frei = FALSE;
9627   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9628   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9629   boolean new_wall = FALSE;
9630
9631   if (IS_ANIMATED(graphic))
9632     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9633
9634   if (!MovDelay[ax][ay])        // start building new wall
9635     MovDelay[ax][ay] = 6;
9636
9637   if (MovDelay[ax][ay])         // wait some time before building new wall
9638   {
9639     MovDelay[ax][ay]--;
9640     if (MovDelay[ax][ay])
9641       return;
9642   }
9643
9644   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9645     oben_frei = TRUE;
9646   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9647     unten_frei = TRUE;
9648   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9649     links_frei = TRUE;
9650   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9651     rechts_frei = TRUE;
9652
9653   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9654       element == EL_EXPANDABLE_STEELWALL_ANY)
9655   {
9656     if (oben_frei)
9657     {
9658       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9659       Store[ax][ay-1] = element;
9660       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9661       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9662         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9663                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9664       new_wall = TRUE;
9665     }
9666     if (unten_frei)
9667     {
9668       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9669       Store[ax][ay+1] = element;
9670       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9671       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9672         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9673                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9674       new_wall = TRUE;
9675     }
9676   }
9677
9678   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9679       element == EL_EXPANDABLE_STEELWALL_ANY)
9680   {
9681     if (links_frei)
9682     {
9683       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9684       Store[ax-1][ay] = element;
9685       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9686       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9687         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9688                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9689       new_wall = TRUE;
9690     }
9691
9692     if (rechts_frei)
9693     {
9694       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9695       Store[ax+1][ay] = element;
9696       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9697       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9698         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9699                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9700       new_wall = TRUE;
9701     }
9702   }
9703
9704   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9705     oben_massiv = TRUE;
9706   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9707     unten_massiv = TRUE;
9708   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9709     links_massiv = TRUE;
9710   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9711     rechts_massiv = TRUE;
9712
9713   if (((oben_massiv && unten_massiv) ||
9714        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9715       ((links_massiv && rechts_massiv) ||
9716        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9717     Tile[ax][ay] = EL_STEELWALL;
9718
9719   if (new_wall)
9720     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9721 }
9722
9723 static void CheckForDragon(int x, int y)
9724 {
9725   int i, j;
9726   boolean dragon_found = FALSE;
9727   static int xy[4][2] =
9728   {
9729     { 0, -1 },
9730     { -1, 0 },
9731     { +1, 0 },
9732     { 0, +1 }
9733   };
9734
9735   for (i = 0; i < NUM_DIRECTIONS; i++)
9736   {
9737     for (j = 0; j < 4; j++)
9738     {
9739       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9740
9741       if (IN_LEV_FIELD(xx, yy) &&
9742           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9743       {
9744         if (Tile[xx][yy] == EL_DRAGON)
9745           dragon_found = TRUE;
9746       }
9747       else
9748         break;
9749     }
9750   }
9751
9752   if (!dragon_found)
9753   {
9754     for (i = 0; i < NUM_DIRECTIONS; i++)
9755     {
9756       for (j = 0; j < 3; j++)
9757       {
9758         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9759   
9760         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9761         {
9762           Tile[xx][yy] = EL_EMPTY;
9763           TEST_DrawLevelField(xx, yy);
9764         }
9765         else
9766           break;
9767       }
9768     }
9769   }
9770 }
9771
9772 static void InitBuggyBase(int x, int y)
9773 {
9774   int element = Tile[x][y];
9775   int activating_delay = FRAMES_PER_SECOND / 4;
9776
9777   ChangeDelay[x][y] =
9778     (element == EL_SP_BUGGY_BASE ?
9779      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9780      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9781      activating_delay :
9782      element == EL_SP_BUGGY_BASE_ACTIVE ?
9783      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9784 }
9785
9786 static void WarnBuggyBase(int x, int y)
9787 {
9788   int i;
9789   static int xy[4][2] =
9790   {
9791     { 0, -1 },
9792     { -1, 0 },
9793     { +1, 0 },
9794     { 0, +1 }
9795   };
9796
9797   for (i = 0; i < NUM_DIRECTIONS; i++)
9798   {
9799     int xx = x + xy[i][0];
9800     int yy = y + xy[i][1];
9801
9802     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9803     {
9804       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9805
9806       break;
9807     }
9808   }
9809 }
9810
9811 static void InitTrap(int x, int y)
9812 {
9813   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9814 }
9815
9816 static void ActivateTrap(int x, int y)
9817 {
9818   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9819 }
9820
9821 static void ChangeActiveTrap(int x, int y)
9822 {
9823   int graphic = IMG_TRAP_ACTIVE;
9824
9825   // if new animation frame was drawn, correct crumbled sand border
9826   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9827     TEST_DrawLevelFieldCrumbled(x, y);
9828 }
9829
9830 static int getSpecialActionElement(int element, int number, int base_element)
9831 {
9832   return (element != EL_EMPTY ? element :
9833           number != -1 ? base_element + number - 1 :
9834           EL_EMPTY);
9835 }
9836
9837 static int getModifiedActionNumber(int value_old, int operator, int operand,
9838                                    int value_min, int value_max)
9839 {
9840   int value_new = (operator == CA_MODE_SET      ? operand :
9841                    operator == CA_MODE_ADD      ? value_old + operand :
9842                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9843                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9844                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9845                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9846                    value_old);
9847
9848   return (value_new < value_min ? value_min :
9849           value_new > value_max ? value_max :
9850           value_new);
9851 }
9852
9853 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9854 {
9855   struct ElementInfo *ei = &element_info[element];
9856   struct ElementChangeInfo *change = &ei->change_page[page];
9857   int target_element = change->target_element;
9858   int action_type = change->action_type;
9859   int action_mode = change->action_mode;
9860   int action_arg = change->action_arg;
9861   int action_element = change->action_element;
9862   int i;
9863
9864   if (!change->has_action)
9865     return;
9866
9867   // ---------- determine action paramater values -----------------------------
9868
9869   int level_time_value =
9870     (level.time > 0 ? TimeLeft :
9871      TimePlayed);
9872
9873   int action_arg_element_raw =
9874     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9875      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9876      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9877      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9878      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9879      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9880      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9881      EL_EMPTY);
9882   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9883
9884   int action_arg_direction =
9885     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9886      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9887      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9888      change->actual_trigger_side :
9889      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9890      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9891      MV_NONE);
9892
9893   int action_arg_number_min =
9894     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9895      CA_ARG_MIN);
9896
9897   int action_arg_number_max =
9898     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9899      action_type == CA_SET_LEVEL_GEMS ? 999 :
9900      action_type == CA_SET_LEVEL_TIME ? 9999 :
9901      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9902      action_type == CA_SET_CE_VALUE ? 9999 :
9903      action_type == CA_SET_CE_SCORE ? 9999 :
9904      CA_ARG_MAX);
9905
9906   int action_arg_number_reset =
9907     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9908      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9909      action_type == CA_SET_LEVEL_TIME ? level.time :
9910      action_type == CA_SET_LEVEL_SCORE ? 0 :
9911      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9912      action_type == CA_SET_CE_SCORE ? 0 :
9913      0);
9914
9915   int action_arg_number =
9916     (action_arg <= CA_ARG_MAX ? action_arg :
9917      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9918      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9919      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9920      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9921      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9922      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9923      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9924      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9925      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9926      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9927      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9928      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9929      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9930      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9931      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9932      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9933      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9934      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9935      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9936      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9937      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9938      -1);
9939
9940   int action_arg_number_old =
9941     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9942      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9943      action_type == CA_SET_LEVEL_SCORE ? game.score :
9944      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9945      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9946      0);
9947
9948   int action_arg_number_new =
9949     getModifiedActionNumber(action_arg_number_old,
9950                             action_mode, action_arg_number,
9951                             action_arg_number_min, action_arg_number_max);
9952
9953   int trigger_player_bits =
9954     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9955      change->actual_trigger_player_bits : change->trigger_player);
9956
9957   int action_arg_player_bits =
9958     (action_arg >= CA_ARG_PLAYER_1 &&
9959      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9960      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9961      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9962      PLAYER_BITS_ANY);
9963
9964   // ---------- execute action  -----------------------------------------------
9965
9966   switch (action_type)
9967   {
9968     case CA_NO_ACTION:
9969     {
9970       return;
9971     }
9972
9973     // ---------- level actions  ----------------------------------------------
9974
9975     case CA_RESTART_LEVEL:
9976     {
9977       game.restart_level = TRUE;
9978
9979       break;
9980     }
9981
9982     case CA_SHOW_ENVELOPE:
9983     {
9984       int element = getSpecialActionElement(action_arg_element,
9985                                             action_arg_number, EL_ENVELOPE_1);
9986
9987       if (IS_ENVELOPE(element))
9988         local_player->show_envelope = element;
9989
9990       break;
9991     }
9992
9993     case CA_SET_LEVEL_TIME:
9994     {
9995       if (level.time > 0)       // only modify limited time value
9996       {
9997         TimeLeft = action_arg_number_new;
9998
9999         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10000
10001         DisplayGameControlValues();
10002
10003         if (!TimeLeft && setup.time_limit)
10004           for (i = 0; i < MAX_PLAYERS; i++)
10005             KillPlayer(&stored_player[i]);
10006       }
10007
10008       break;
10009     }
10010
10011     case CA_SET_LEVEL_SCORE:
10012     {
10013       game.score = action_arg_number_new;
10014
10015       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10016
10017       DisplayGameControlValues();
10018
10019       break;
10020     }
10021
10022     case CA_SET_LEVEL_GEMS:
10023     {
10024       game.gems_still_needed = action_arg_number_new;
10025
10026       game.snapshot.collected_item = TRUE;
10027
10028       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10029
10030       DisplayGameControlValues();
10031
10032       break;
10033     }
10034
10035     case CA_SET_LEVEL_WIND:
10036     {
10037       game.wind_direction = action_arg_direction;
10038
10039       break;
10040     }
10041
10042     case CA_SET_LEVEL_RANDOM_SEED:
10043     {
10044       // ensure that setting a new random seed while playing is predictable
10045       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10046
10047       break;
10048     }
10049
10050     // ---------- player actions  ---------------------------------------------
10051
10052     case CA_MOVE_PLAYER:
10053     case CA_MOVE_PLAYER_NEW:
10054     {
10055       // automatically move to the next field in specified direction
10056       for (i = 0; i < MAX_PLAYERS; i++)
10057         if (trigger_player_bits & (1 << i))
10058           if (action_type == CA_MOVE_PLAYER ||
10059               stored_player[i].MovPos == 0)
10060             stored_player[i].programmed_action = action_arg_direction;
10061
10062       break;
10063     }
10064
10065     case CA_EXIT_PLAYER:
10066     {
10067       for (i = 0; i < MAX_PLAYERS; i++)
10068         if (action_arg_player_bits & (1 << i))
10069           ExitPlayer(&stored_player[i]);
10070
10071       if (game.players_still_needed == 0)
10072         LevelSolved();
10073
10074       break;
10075     }
10076
10077     case CA_KILL_PLAYER:
10078     {
10079       for (i = 0; i < MAX_PLAYERS; i++)
10080         if (action_arg_player_bits & (1 << i))
10081           KillPlayer(&stored_player[i]);
10082
10083       break;
10084     }
10085
10086     case CA_SET_PLAYER_KEYS:
10087     {
10088       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10089       int element = getSpecialActionElement(action_arg_element,
10090                                             action_arg_number, EL_KEY_1);
10091
10092       if (IS_KEY(element))
10093       {
10094         for (i = 0; i < MAX_PLAYERS; i++)
10095         {
10096           if (trigger_player_bits & (1 << i))
10097           {
10098             stored_player[i].key[KEY_NR(element)] = key_state;
10099
10100             DrawGameDoorValues();
10101           }
10102         }
10103       }
10104
10105       break;
10106     }
10107
10108     case CA_SET_PLAYER_SPEED:
10109     {
10110       for (i = 0; i < MAX_PLAYERS; i++)
10111       {
10112         if (trigger_player_bits & (1 << i))
10113         {
10114           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10115
10116           if (action_arg == CA_ARG_SPEED_FASTER &&
10117               stored_player[i].cannot_move)
10118           {
10119             action_arg_number = STEPSIZE_VERY_SLOW;
10120           }
10121           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10122                    action_arg == CA_ARG_SPEED_FASTER)
10123           {
10124             action_arg_number = 2;
10125             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10126                            CA_MODE_MULTIPLY);
10127           }
10128           else if (action_arg == CA_ARG_NUMBER_RESET)
10129           {
10130             action_arg_number = level.initial_player_stepsize[i];
10131           }
10132
10133           move_stepsize =
10134             getModifiedActionNumber(move_stepsize,
10135                                     action_mode,
10136                                     action_arg_number,
10137                                     action_arg_number_min,
10138                                     action_arg_number_max);
10139
10140           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10141         }
10142       }
10143
10144       break;
10145     }
10146
10147     case CA_SET_PLAYER_SHIELD:
10148     {
10149       for (i = 0; i < MAX_PLAYERS; i++)
10150       {
10151         if (trigger_player_bits & (1 << i))
10152         {
10153           if (action_arg == CA_ARG_SHIELD_OFF)
10154           {
10155             stored_player[i].shield_normal_time_left = 0;
10156             stored_player[i].shield_deadly_time_left = 0;
10157           }
10158           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10159           {
10160             stored_player[i].shield_normal_time_left = 999999;
10161           }
10162           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10163           {
10164             stored_player[i].shield_normal_time_left = 999999;
10165             stored_player[i].shield_deadly_time_left = 999999;
10166           }
10167         }
10168       }
10169
10170       break;
10171     }
10172
10173     case CA_SET_PLAYER_GRAVITY:
10174     {
10175       for (i = 0; i < MAX_PLAYERS; i++)
10176       {
10177         if (trigger_player_bits & (1 << i))
10178         {
10179           stored_player[i].gravity =
10180             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10181              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10182              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10183              stored_player[i].gravity);
10184         }
10185       }
10186
10187       break;
10188     }
10189
10190     case CA_SET_PLAYER_ARTWORK:
10191     {
10192       for (i = 0; i < MAX_PLAYERS; i++)
10193       {
10194         if (trigger_player_bits & (1 << i))
10195         {
10196           int artwork_element = action_arg_element;
10197
10198           if (action_arg == CA_ARG_ELEMENT_RESET)
10199             artwork_element =
10200               (level.use_artwork_element[i] ? level.artwork_element[i] :
10201                stored_player[i].element_nr);
10202
10203           if (stored_player[i].artwork_element != artwork_element)
10204             stored_player[i].Frame = 0;
10205
10206           stored_player[i].artwork_element = artwork_element;
10207
10208           SetPlayerWaiting(&stored_player[i], FALSE);
10209
10210           // set number of special actions for bored and sleeping animation
10211           stored_player[i].num_special_action_bored =
10212             get_num_special_action(artwork_element,
10213                                    ACTION_BORING_1, ACTION_BORING_LAST);
10214           stored_player[i].num_special_action_sleeping =
10215             get_num_special_action(artwork_element,
10216                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10217         }
10218       }
10219
10220       break;
10221     }
10222
10223     case CA_SET_PLAYER_INVENTORY:
10224     {
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226       {
10227         struct PlayerInfo *player = &stored_player[i];
10228         int j, k;
10229
10230         if (trigger_player_bits & (1 << i))
10231         {
10232           int inventory_element = action_arg_element;
10233
10234           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10235               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10236               action_arg == CA_ARG_ELEMENT_ACTION)
10237           {
10238             int element = inventory_element;
10239             int collect_count = element_info[element].collect_count_initial;
10240
10241             if (!IS_CUSTOM_ELEMENT(element))
10242               collect_count = 1;
10243
10244             if (collect_count == 0)
10245               player->inventory_infinite_element = element;
10246             else
10247               for (k = 0; k < collect_count; k++)
10248                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10249                   player->inventory_element[player->inventory_size++] =
10250                     element;
10251           }
10252           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10253                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10254                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10255           {
10256             if (player->inventory_infinite_element != EL_UNDEFINED &&
10257                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10258                                      action_arg_element_raw))
10259               player->inventory_infinite_element = EL_UNDEFINED;
10260
10261             for (k = 0, j = 0; j < player->inventory_size; j++)
10262             {
10263               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10264                                         action_arg_element_raw))
10265                 player->inventory_element[k++] = player->inventory_element[j];
10266             }
10267
10268             player->inventory_size = k;
10269           }
10270           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10271           {
10272             if (player->inventory_size > 0)
10273             {
10274               for (j = 0; j < player->inventory_size - 1; j++)
10275                 player->inventory_element[j] = player->inventory_element[j + 1];
10276
10277               player->inventory_size--;
10278             }
10279           }
10280           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10281           {
10282             if (player->inventory_size > 0)
10283               player->inventory_size--;
10284           }
10285           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10286           {
10287             player->inventory_infinite_element = EL_UNDEFINED;
10288             player->inventory_size = 0;
10289           }
10290           else if (action_arg == CA_ARG_INVENTORY_RESET)
10291           {
10292             player->inventory_infinite_element = EL_UNDEFINED;
10293             player->inventory_size = 0;
10294
10295             if (level.use_initial_inventory[i])
10296             {
10297               for (j = 0; j < level.initial_inventory_size[i]; j++)
10298               {
10299                 int element = level.initial_inventory_content[i][j];
10300                 int collect_count = element_info[element].collect_count_initial;
10301
10302                 if (!IS_CUSTOM_ELEMENT(element))
10303                   collect_count = 1;
10304
10305                 if (collect_count == 0)
10306                   player->inventory_infinite_element = element;
10307                 else
10308                   for (k = 0; k < collect_count; k++)
10309                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10310                       player->inventory_element[player->inventory_size++] =
10311                         element;
10312               }
10313             }
10314           }
10315         }
10316       }
10317
10318       break;
10319     }
10320
10321     // ---------- CE actions  -------------------------------------------------
10322
10323     case CA_SET_CE_VALUE:
10324     {
10325       int last_ce_value = CustomValue[x][y];
10326
10327       CustomValue[x][y] = action_arg_number_new;
10328
10329       if (CustomValue[x][y] != last_ce_value)
10330       {
10331         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10332         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10333
10334         if (CustomValue[x][y] == 0)
10335         {
10336           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10337           ChangeCount[x][y] = 0;        // allow at least one more change
10338
10339           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10340           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10341         }
10342       }
10343
10344       break;
10345     }
10346
10347     case CA_SET_CE_SCORE:
10348     {
10349       int last_ce_score = ei->collect_score;
10350
10351       ei->collect_score = action_arg_number_new;
10352
10353       if (ei->collect_score != last_ce_score)
10354       {
10355         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10356         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10357
10358         if (ei->collect_score == 0)
10359         {
10360           int xx, yy;
10361
10362           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10363           ChangeCount[x][y] = 0;        // allow at least one more change
10364
10365           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10366           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10367
10368           /*
10369             This is a very special case that seems to be a mixture between
10370             CheckElementChange() and CheckTriggeredElementChange(): while
10371             the first one only affects single elements that are triggered
10372             directly, the second one affects multiple elements in the playfield
10373             that are triggered indirectly by another element. This is a third
10374             case: Changing the CE score always affects multiple identical CEs,
10375             so every affected CE must be checked, not only the single CE for
10376             which the CE score was changed in the first place (as every instance
10377             of that CE shares the same CE score, and therefore also can change)!
10378           */
10379           SCAN_PLAYFIELD(xx, yy)
10380           {
10381             if (Tile[xx][yy] == element)
10382               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10383                                  CE_SCORE_GETS_ZERO);
10384           }
10385         }
10386       }
10387
10388       break;
10389     }
10390
10391     case CA_SET_CE_ARTWORK:
10392     {
10393       int artwork_element = action_arg_element;
10394       boolean reset_frame = FALSE;
10395       int xx, yy;
10396
10397       if (action_arg == CA_ARG_ELEMENT_RESET)
10398         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10399                            element);
10400
10401       if (ei->gfx_element != artwork_element)
10402         reset_frame = TRUE;
10403
10404       ei->gfx_element = artwork_element;
10405
10406       SCAN_PLAYFIELD(xx, yy)
10407       {
10408         if (Tile[xx][yy] == element)
10409         {
10410           if (reset_frame)
10411           {
10412             ResetGfxAnimation(xx, yy);
10413             ResetRandomAnimationValue(xx, yy);
10414           }
10415
10416           TEST_DrawLevelField(xx, yy);
10417         }
10418       }
10419
10420       break;
10421     }
10422
10423     // ---------- engine actions  ---------------------------------------------
10424
10425     case CA_SET_ENGINE_SCAN_MODE:
10426     {
10427       InitPlayfieldScanMode(action_arg);
10428
10429       break;
10430     }
10431
10432     default:
10433       break;
10434   }
10435 }
10436
10437 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10438 {
10439   int old_element = Tile[x][y];
10440   int new_element = GetElementFromGroupElement(element);
10441   int previous_move_direction = MovDir[x][y];
10442   int last_ce_value = CustomValue[x][y];
10443   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10444   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10445   boolean add_player_onto_element = (new_element_is_player &&
10446                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10447                                      IS_WALKABLE(old_element));
10448
10449   if (!add_player_onto_element)
10450   {
10451     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10452       RemoveMovingField(x, y);
10453     else
10454       RemoveField(x, y);
10455
10456     Tile[x][y] = new_element;
10457
10458     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10459       MovDir[x][y] = previous_move_direction;
10460
10461     if (element_info[new_element].use_last_ce_value)
10462       CustomValue[x][y] = last_ce_value;
10463
10464     InitField_WithBug1(x, y, FALSE);
10465
10466     new_element = Tile[x][y];   // element may have changed
10467
10468     ResetGfxAnimation(x, y);
10469     ResetRandomAnimationValue(x, y);
10470
10471     TEST_DrawLevelField(x, y);
10472
10473     if (GFX_CRUMBLED(new_element))
10474       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10475   }
10476
10477   // check if element under the player changes from accessible to unaccessible
10478   // (needed for special case of dropping element which then changes)
10479   // (must be checked after creating new element for walkable group elements)
10480   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10481       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10482   {
10483     Bang(x, y);
10484
10485     return;
10486   }
10487
10488   // "ChangeCount" not set yet to allow "entered by player" change one time
10489   if (new_element_is_player)
10490     RelocatePlayer(x, y, new_element);
10491
10492   if (is_change)
10493     ChangeCount[x][y]++;        // count number of changes in the same frame
10494
10495   TestIfBadThingTouchesPlayer(x, y);
10496   TestIfPlayerTouchesCustomElement(x, y);
10497   TestIfElementTouchesCustomElement(x, y);
10498 }
10499
10500 static void CreateField(int x, int y, int element)
10501 {
10502   CreateFieldExt(x, y, element, FALSE);
10503 }
10504
10505 static void CreateElementFromChange(int x, int y, int element)
10506 {
10507   element = GET_VALID_RUNTIME_ELEMENT(element);
10508
10509   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10510   {
10511     int old_element = Tile[x][y];
10512
10513     // prevent changed element from moving in same engine frame
10514     // unless both old and new element can either fall or move
10515     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10516         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10517       Stop[x][y] = TRUE;
10518   }
10519
10520   CreateFieldExt(x, y, element, TRUE);
10521 }
10522
10523 static boolean ChangeElement(int x, int y, int element, int page)
10524 {
10525   struct ElementInfo *ei = &element_info[element];
10526   struct ElementChangeInfo *change = &ei->change_page[page];
10527   int ce_value = CustomValue[x][y];
10528   int ce_score = ei->collect_score;
10529   int target_element;
10530   int old_element = Tile[x][y];
10531
10532   // always use default change event to prevent running into a loop
10533   if (ChangeEvent[x][y] == -1)
10534     ChangeEvent[x][y] = CE_DELAY;
10535
10536   if (ChangeEvent[x][y] == CE_DELAY)
10537   {
10538     // reset actual trigger element, trigger player and action element
10539     change->actual_trigger_element = EL_EMPTY;
10540     change->actual_trigger_player = EL_EMPTY;
10541     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10542     change->actual_trigger_side = CH_SIDE_NONE;
10543     change->actual_trigger_ce_value = 0;
10544     change->actual_trigger_ce_score = 0;
10545   }
10546
10547   // do not change elements more than a specified maximum number of changes
10548   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10549     return FALSE;
10550
10551   ChangeCount[x][y]++;          // count number of changes in the same frame
10552
10553   if (change->explode)
10554   {
10555     Bang(x, y);
10556
10557     return TRUE;
10558   }
10559
10560   if (change->use_target_content)
10561   {
10562     boolean complete_replace = TRUE;
10563     boolean can_replace[3][3];
10564     int xx, yy;
10565
10566     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10567     {
10568       boolean is_empty;
10569       boolean is_walkable;
10570       boolean is_diggable;
10571       boolean is_collectible;
10572       boolean is_removable;
10573       boolean is_destructible;
10574       int ex = x + xx - 1;
10575       int ey = y + yy - 1;
10576       int content_element = change->target_content.e[xx][yy];
10577       int e;
10578
10579       can_replace[xx][yy] = TRUE;
10580
10581       if (ex == x && ey == y)   // do not check changing element itself
10582         continue;
10583
10584       if (content_element == EL_EMPTY_SPACE)
10585       {
10586         can_replace[xx][yy] = FALSE;    // do not replace border with space
10587
10588         continue;
10589       }
10590
10591       if (!IN_LEV_FIELD(ex, ey))
10592       {
10593         can_replace[xx][yy] = FALSE;
10594         complete_replace = FALSE;
10595
10596         continue;
10597       }
10598
10599       e = Tile[ex][ey];
10600
10601       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10602         e = MovingOrBlocked2Element(ex, ey);
10603
10604       is_empty = (IS_FREE(ex, ey) ||
10605                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10606
10607       is_walkable     = (is_empty || IS_WALKABLE(e));
10608       is_diggable     = (is_empty || IS_DIGGABLE(e));
10609       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10610       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10611       is_removable    = (is_diggable || is_collectible);
10612
10613       can_replace[xx][yy] =
10614         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10615           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10616           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10617           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10618           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10619           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10620          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10621
10622       if (!can_replace[xx][yy])
10623         complete_replace = FALSE;
10624     }
10625
10626     if (!change->only_if_complete || complete_replace)
10627     {
10628       boolean something_has_changed = FALSE;
10629
10630       if (change->only_if_complete && change->use_random_replace &&
10631           RND(100) < change->random_percentage)
10632         return FALSE;
10633
10634       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10635       {
10636         int ex = x + xx - 1;
10637         int ey = y + yy - 1;
10638         int content_element;
10639
10640         if (can_replace[xx][yy] && (!change->use_random_replace ||
10641                                     RND(100) < change->random_percentage))
10642         {
10643           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10644             RemoveMovingField(ex, ey);
10645
10646           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10647
10648           content_element = change->target_content.e[xx][yy];
10649           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10650                                               ce_value, ce_score);
10651
10652           CreateElementFromChange(ex, ey, target_element);
10653
10654           something_has_changed = TRUE;
10655
10656           // for symmetry reasons, freeze newly created border elements
10657           if (ex != x || ey != y)
10658             Stop[ex][ey] = TRUE;        // no more moving in this frame
10659         }
10660       }
10661
10662       if (something_has_changed)
10663       {
10664         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10665         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10666       }
10667     }
10668   }
10669   else
10670   {
10671     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10672                                         ce_value, ce_score);
10673
10674     if (element == EL_DIAGONAL_GROWING ||
10675         element == EL_DIAGONAL_SHRINKING)
10676     {
10677       target_element = Store[x][y];
10678
10679       Store[x][y] = EL_EMPTY;
10680     }
10681
10682     CreateElementFromChange(x, y, target_element);
10683
10684     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10685     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10686   }
10687
10688   // this uses direct change before indirect change
10689   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10690
10691   return TRUE;
10692 }
10693
10694 static void HandleElementChange(int x, int y, int page)
10695 {
10696   int element = MovingOrBlocked2Element(x, y);
10697   struct ElementInfo *ei = &element_info[element];
10698   struct ElementChangeInfo *change = &ei->change_page[page];
10699   boolean handle_action_before_change = FALSE;
10700
10701 #ifdef DEBUG
10702   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10703       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10704   {
10705     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10706           x, y, element, element_info[element].token_name);
10707     Debug("game:playing:HandleElementChange", "This should never happen!");
10708   }
10709 #endif
10710
10711   // this can happen with classic bombs on walkable, changing elements
10712   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10713   {
10714     return;
10715   }
10716
10717   if (ChangeDelay[x][y] == 0)           // initialize element change
10718   {
10719     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10720
10721     if (change->can_change)
10722     {
10723       // !!! not clear why graphic animation should be reset at all here !!!
10724       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10725       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10726
10727       /*
10728         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10729
10730         When using an animation frame delay of 1 (this only happens with
10731         "sp_zonk.moving.left/right" in the classic graphics), the default
10732         (non-moving) animation shows wrong animation frames (while the
10733         moving animation, like "sp_zonk.moving.left/right", is correct,
10734         so this graphical bug never shows up with the classic graphics).
10735         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10736         be drawn instead of the correct frames 0,1,2,3. This is caused by
10737         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10738         an element change: First when the change delay ("ChangeDelay[][]")
10739         counter has reached zero after decrementing, then a second time in
10740         the next frame (after "GfxFrame[][]" was already incremented) when
10741         "ChangeDelay[][]" is reset to the initial delay value again.
10742
10743         This causes frame 0 to be drawn twice, while the last frame won't
10744         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10745
10746         As some animations may already be cleverly designed around this bug
10747         (at least the "Snake Bite" snake tail animation does this), it cannot
10748         simply be fixed here without breaking such existing animations.
10749         Unfortunately, it cannot easily be detected if a graphics set was
10750         designed "before" or "after" the bug was fixed. As a workaround,
10751         a new graphics set option "game.graphics_engine_version" was added
10752         to be able to specify the game's major release version for which the
10753         graphics set was designed, which can then be used to decide if the
10754         bugfix should be used (version 4 and above) or not (version 3 or
10755         below, or if no version was specified at all, as with old sets).
10756
10757         (The wrong/fixed animation frames can be tested with the test level set
10758         "test_gfxframe" and level "000", which contains a specially prepared
10759         custom element at level position (x/y) == (11/9) which uses the zonk
10760         animation mentioned above. Using "game.graphics_engine_version: 4"
10761         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10762         This can also be seen from the debug output for this test element.)
10763       */
10764
10765       // when a custom element is about to change (for example by change delay),
10766       // do not reset graphic animation when the custom element is moving
10767       if (game.graphics_engine_version < 4 &&
10768           !IS_MOVING(x, y))
10769       {
10770         ResetGfxAnimation(x, y);
10771         ResetRandomAnimationValue(x, y);
10772       }
10773
10774       if (change->pre_change_function)
10775         change->pre_change_function(x, y);
10776     }
10777   }
10778
10779   ChangeDelay[x][y]--;
10780
10781   if (ChangeDelay[x][y] != 0)           // continue element change
10782   {
10783     if (change->can_change)
10784     {
10785       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10786
10787       if (IS_ANIMATED(graphic))
10788         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10789
10790       if (change->change_function)
10791         change->change_function(x, y);
10792     }
10793   }
10794   else                                  // finish element change
10795   {
10796     if (ChangePage[x][y] != -1)         // remember page from delayed change
10797     {
10798       page = ChangePage[x][y];
10799       ChangePage[x][y] = -1;
10800
10801       change = &ei->change_page[page];
10802     }
10803
10804     if (IS_MOVING(x, y))                // never change a running system ;-)
10805     {
10806       ChangeDelay[x][y] = 1;            // try change after next move step
10807       ChangePage[x][y] = page;          // remember page to use for change
10808
10809       return;
10810     }
10811
10812     // special case: set new level random seed before changing element
10813     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10814       handle_action_before_change = TRUE;
10815
10816     if (change->has_action && handle_action_before_change)
10817       ExecuteCustomElementAction(x, y, element, page);
10818
10819     if (change->can_change)
10820     {
10821       if (ChangeElement(x, y, element, page))
10822       {
10823         if (change->post_change_function)
10824           change->post_change_function(x, y);
10825       }
10826     }
10827
10828     if (change->has_action && !handle_action_before_change)
10829       ExecuteCustomElementAction(x, y, element, page);
10830   }
10831 }
10832
10833 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10834                                               int trigger_element,
10835                                               int trigger_event,
10836                                               int trigger_player,
10837                                               int trigger_side,
10838                                               int trigger_page)
10839 {
10840   boolean change_done_any = FALSE;
10841   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10842   int i;
10843
10844   if (!(trigger_events[trigger_element][trigger_event]))
10845     return FALSE;
10846
10847   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10848
10849   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10850   {
10851     int element = EL_CUSTOM_START + i;
10852     boolean change_done = FALSE;
10853     int p;
10854
10855     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10856         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10857       continue;
10858
10859     for (p = 0; p < element_info[element].num_change_pages; p++)
10860     {
10861       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10862
10863       if (change->can_change_or_has_action &&
10864           change->has_event[trigger_event] &&
10865           change->trigger_side & trigger_side &&
10866           change->trigger_player & trigger_player &&
10867           change->trigger_page & trigger_page_bits &&
10868           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10869       {
10870         change->actual_trigger_element = trigger_element;
10871         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10872         change->actual_trigger_player_bits = trigger_player;
10873         change->actual_trigger_side = trigger_side;
10874         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10875         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10876
10877         if ((change->can_change && !change_done) || change->has_action)
10878         {
10879           int x, y;
10880
10881           SCAN_PLAYFIELD(x, y)
10882           {
10883             if (Tile[x][y] == element)
10884             {
10885               if (change->can_change && !change_done)
10886               {
10887                 // if element already changed in this frame, not only prevent
10888                 // another element change (checked in ChangeElement()), but
10889                 // also prevent additional element actions for this element
10890
10891                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10892                     !level.use_action_after_change_bug)
10893                   continue;
10894
10895                 ChangeDelay[x][y] = 1;
10896                 ChangeEvent[x][y] = trigger_event;
10897
10898                 HandleElementChange(x, y, p);
10899               }
10900               else if (change->has_action)
10901               {
10902                 // if element already changed in this frame, not only prevent
10903                 // another element change (checked in ChangeElement()), but
10904                 // also prevent additional element actions for this element
10905
10906                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10907                     !level.use_action_after_change_bug)
10908                   continue;
10909
10910                 ExecuteCustomElementAction(x, y, element, p);
10911                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10912               }
10913             }
10914           }
10915
10916           if (change->can_change)
10917           {
10918             change_done = TRUE;
10919             change_done_any = TRUE;
10920           }
10921         }
10922       }
10923     }
10924   }
10925
10926   RECURSION_LOOP_DETECTION_END();
10927
10928   return change_done_any;
10929 }
10930
10931 static boolean CheckElementChangeExt(int x, int y,
10932                                      int element,
10933                                      int trigger_element,
10934                                      int trigger_event,
10935                                      int trigger_player,
10936                                      int trigger_side)
10937 {
10938   boolean change_done = FALSE;
10939   int p;
10940
10941   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10942       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10943     return FALSE;
10944
10945   if (Tile[x][y] == EL_BLOCKED)
10946   {
10947     Blocked2Moving(x, y, &x, &y);
10948     element = Tile[x][y];
10949   }
10950
10951   // check if element has already changed or is about to change after moving
10952   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10953        Tile[x][y] != element) ||
10954
10955       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10956        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10957         ChangePage[x][y] != -1)))
10958     return FALSE;
10959
10960   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10961
10962   for (p = 0; p < element_info[element].num_change_pages; p++)
10963   {
10964     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10965
10966     /* check trigger element for all events where the element that is checked
10967        for changing interacts with a directly adjacent element -- this is
10968        different to element changes that affect other elements to change on the
10969        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10970     boolean check_trigger_element =
10971       (trigger_event == CE_TOUCHING_X ||
10972        trigger_event == CE_HITTING_X ||
10973        trigger_event == CE_HIT_BY_X ||
10974        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10975
10976     if (change->can_change_or_has_action &&
10977         change->has_event[trigger_event] &&
10978         change->trigger_side & trigger_side &&
10979         change->trigger_player & trigger_player &&
10980         (!check_trigger_element ||
10981          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10982     {
10983       change->actual_trigger_element = trigger_element;
10984       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10985       change->actual_trigger_player_bits = trigger_player;
10986       change->actual_trigger_side = trigger_side;
10987       change->actual_trigger_ce_value = CustomValue[x][y];
10988       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10989
10990       // special case: trigger element not at (x,y) position for some events
10991       if (check_trigger_element)
10992       {
10993         static struct
10994         {
10995           int dx, dy;
10996         } move_xy[] =
10997           {
10998             {  0,  0 },
10999             { -1,  0 },
11000             { +1,  0 },
11001             {  0,  0 },
11002             {  0, -1 },
11003             {  0,  0 }, { 0, 0 }, { 0, 0 },
11004             {  0, +1 }
11005           };
11006
11007         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11008         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11009
11010         change->actual_trigger_ce_value = CustomValue[xx][yy];
11011         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11012       }
11013
11014       if (change->can_change && !change_done)
11015       {
11016         ChangeDelay[x][y] = 1;
11017         ChangeEvent[x][y] = trigger_event;
11018
11019         HandleElementChange(x, y, p);
11020
11021         change_done = TRUE;
11022       }
11023       else if (change->has_action)
11024       {
11025         ExecuteCustomElementAction(x, y, element, p);
11026         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11027       }
11028     }
11029   }
11030
11031   RECURSION_LOOP_DETECTION_END();
11032
11033   return change_done;
11034 }
11035
11036 static void PlayPlayerSound(struct PlayerInfo *player)
11037 {
11038   int jx = player->jx, jy = player->jy;
11039   int sound_element = player->artwork_element;
11040   int last_action = player->last_action_waiting;
11041   int action = player->action_waiting;
11042
11043   if (player->is_waiting)
11044   {
11045     if (action != last_action)
11046       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11047     else
11048       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11049   }
11050   else
11051   {
11052     if (action != last_action)
11053       StopSound(element_info[sound_element].sound[last_action]);
11054
11055     if (last_action == ACTION_SLEEPING)
11056       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11057   }
11058 }
11059
11060 static void PlayAllPlayersSound(void)
11061 {
11062   int i;
11063
11064   for (i = 0; i < MAX_PLAYERS; i++)
11065     if (stored_player[i].active)
11066       PlayPlayerSound(&stored_player[i]);
11067 }
11068
11069 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11070 {
11071   boolean last_waiting = player->is_waiting;
11072   int move_dir = player->MovDir;
11073
11074   player->dir_waiting = move_dir;
11075   player->last_action_waiting = player->action_waiting;
11076
11077   if (is_waiting)
11078   {
11079     if (!last_waiting)          // not waiting -> waiting
11080     {
11081       player->is_waiting = TRUE;
11082
11083       player->frame_counter_bored =
11084         FrameCounter +
11085         game.player_boring_delay_fixed +
11086         GetSimpleRandom(game.player_boring_delay_random);
11087       player->frame_counter_sleeping =
11088         FrameCounter +
11089         game.player_sleeping_delay_fixed +
11090         GetSimpleRandom(game.player_sleeping_delay_random);
11091
11092       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11093     }
11094
11095     if (game.player_sleeping_delay_fixed +
11096         game.player_sleeping_delay_random > 0 &&
11097         player->anim_delay_counter == 0 &&
11098         player->post_delay_counter == 0 &&
11099         FrameCounter >= player->frame_counter_sleeping)
11100       player->is_sleeping = TRUE;
11101     else if (game.player_boring_delay_fixed +
11102              game.player_boring_delay_random > 0 &&
11103              FrameCounter >= player->frame_counter_bored)
11104       player->is_bored = TRUE;
11105
11106     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11107                               player->is_bored ? ACTION_BORING :
11108                               ACTION_WAITING);
11109
11110     if (player->is_sleeping && player->use_murphy)
11111     {
11112       // special case for sleeping Murphy when leaning against non-free tile
11113
11114       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11115           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11116            !IS_MOVING(player->jx - 1, player->jy)))
11117         move_dir = MV_LEFT;
11118       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11119                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11120                 !IS_MOVING(player->jx + 1, player->jy)))
11121         move_dir = MV_RIGHT;
11122       else
11123         player->is_sleeping = FALSE;
11124
11125       player->dir_waiting = move_dir;
11126     }
11127
11128     if (player->is_sleeping)
11129     {
11130       if (player->num_special_action_sleeping > 0)
11131       {
11132         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11133         {
11134           int last_special_action = player->special_action_sleeping;
11135           int num_special_action = player->num_special_action_sleeping;
11136           int special_action =
11137             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11138              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11139              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11140              last_special_action + 1 : ACTION_SLEEPING);
11141           int special_graphic =
11142             el_act_dir2img(player->artwork_element, special_action, move_dir);
11143
11144           player->anim_delay_counter =
11145             graphic_info[special_graphic].anim_delay_fixed +
11146             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11147           player->post_delay_counter =
11148             graphic_info[special_graphic].post_delay_fixed +
11149             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11150
11151           player->special_action_sleeping = special_action;
11152         }
11153
11154         if (player->anim_delay_counter > 0)
11155         {
11156           player->action_waiting = player->special_action_sleeping;
11157           player->anim_delay_counter--;
11158         }
11159         else if (player->post_delay_counter > 0)
11160         {
11161           player->post_delay_counter--;
11162         }
11163       }
11164     }
11165     else if (player->is_bored)
11166     {
11167       if (player->num_special_action_bored > 0)
11168       {
11169         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11170         {
11171           int special_action =
11172             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11173           int special_graphic =
11174             el_act_dir2img(player->artwork_element, special_action, move_dir);
11175
11176           player->anim_delay_counter =
11177             graphic_info[special_graphic].anim_delay_fixed +
11178             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11179           player->post_delay_counter =
11180             graphic_info[special_graphic].post_delay_fixed +
11181             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11182
11183           player->special_action_bored = special_action;
11184         }
11185
11186         if (player->anim_delay_counter > 0)
11187         {
11188           player->action_waiting = player->special_action_bored;
11189           player->anim_delay_counter--;
11190         }
11191         else if (player->post_delay_counter > 0)
11192         {
11193           player->post_delay_counter--;
11194         }
11195       }
11196     }
11197   }
11198   else if (last_waiting)        // waiting -> not waiting
11199   {
11200     player->is_waiting = FALSE;
11201     player->is_bored = FALSE;
11202     player->is_sleeping = FALSE;
11203
11204     player->frame_counter_bored = -1;
11205     player->frame_counter_sleeping = -1;
11206
11207     player->anim_delay_counter = 0;
11208     player->post_delay_counter = 0;
11209
11210     player->dir_waiting = player->MovDir;
11211     player->action_waiting = ACTION_DEFAULT;
11212
11213     player->special_action_bored = ACTION_DEFAULT;
11214     player->special_action_sleeping = ACTION_DEFAULT;
11215   }
11216 }
11217
11218 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11219 {
11220   if ((!player->is_moving  && player->was_moving) ||
11221       (player->MovPos == 0 && player->was_moving) ||
11222       (player->is_snapping && !player->was_snapping) ||
11223       (player->is_dropping && !player->was_dropping))
11224   {
11225     if (!CheckSaveEngineSnapshotToList())
11226       return;
11227
11228     player->was_moving = FALSE;
11229     player->was_snapping = TRUE;
11230     player->was_dropping = TRUE;
11231   }
11232   else
11233   {
11234     if (player->is_moving)
11235       player->was_moving = TRUE;
11236
11237     if (!player->is_snapping)
11238       player->was_snapping = FALSE;
11239
11240     if (!player->is_dropping)
11241       player->was_dropping = FALSE;
11242   }
11243
11244   static struct MouseActionInfo mouse_action_last = { 0 };
11245   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11246   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11247
11248   if (new_released)
11249     CheckSaveEngineSnapshotToList();
11250
11251   mouse_action_last = mouse_action;
11252 }
11253
11254 static void CheckSingleStepMode(struct PlayerInfo *player)
11255 {
11256   if (tape.single_step && tape.recording && !tape.pausing)
11257   {
11258     /* as it is called "single step mode", just return to pause mode when the
11259        player stopped moving after one tile (or never starts moving at all) */
11260     if (!player->is_moving &&
11261         !player->is_pushing &&
11262         !player->is_dropping_pressed &&
11263         !player->effective_mouse_action.button)
11264       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11265   }
11266
11267   CheckSaveEngineSnapshot(player);
11268 }
11269
11270 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11271 {
11272   int left      = player_action & JOY_LEFT;
11273   int right     = player_action & JOY_RIGHT;
11274   int up        = player_action & JOY_UP;
11275   int down      = player_action & JOY_DOWN;
11276   int button1   = player_action & JOY_BUTTON_1;
11277   int button2   = player_action & JOY_BUTTON_2;
11278   int dx        = (left ? -1 : right ? 1 : 0);
11279   int dy        = (up   ? -1 : down  ? 1 : 0);
11280
11281   if (!player->active || tape.pausing)
11282     return 0;
11283
11284   if (player_action)
11285   {
11286     if (button1)
11287       SnapField(player, dx, dy);
11288     else
11289     {
11290       if (button2)
11291         DropElement(player);
11292
11293       MovePlayer(player, dx, dy);
11294     }
11295
11296     CheckSingleStepMode(player);
11297
11298     SetPlayerWaiting(player, FALSE);
11299
11300     return player_action;
11301   }
11302   else
11303   {
11304     // no actions for this player (no input at player's configured device)
11305
11306     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11307     SnapField(player, 0, 0);
11308     CheckGravityMovementWhenNotMoving(player);
11309
11310     if (player->MovPos == 0)
11311       SetPlayerWaiting(player, TRUE);
11312
11313     if (player->MovPos == 0)    // needed for tape.playing
11314       player->is_moving = FALSE;
11315
11316     player->is_dropping = FALSE;
11317     player->is_dropping_pressed = FALSE;
11318     player->drop_pressed_delay = 0;
11319
11320     CheckSingleStepMode(player);
11321
11322     return 0;
11323   }
11324 }
11325
11326 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11327                                          byte *tape_action)
11328 {
11329   if (!tape.use_mouse_actions)
11330     return;
11331
11332   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11333   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11334   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11335 }
11336
11337 static void SetTapeActionFromMouseAction(byte *tape_action,
11338                                          struct MouseActionInfo *mouse_action)
11339 {
11340   if (!tape.use_mouse_actions)
11341     return;
11342
11343   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11344   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11345   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11346 }
11347
11348 static void CheckLevelSolved(void)
11349 {
11350   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11351   {
11352     if (game_em.level_solved &&
11353         !game_em.game_over)                             // game won
11354     {
11355       LevelSolved();
11356
11357       game_em.game_over = TRUE;
11358
11359       game.all_players_gone = TRUE;
11360     }
11361
11362     if (game_em.game_over)                              // game lost
11363       game.all_players_gone = TRUE;
11364   }
11365   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11366   {
11367     if (game_sp.level_solved &&
11368         !game_sp.game_over)                             // game won
11369     {
11370       LevelSolved();
11371
11372       game_sp.game_over = TRUE;
11373
11374       game.all_players_gone = TRUE;
11375     }
11376
11377     if (game_sp.game_over)                              // game lost
11378       game.all_players_gone = TRUE;
11379   }
11380   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11381   {
11382     if (game_mm.level_solved &&
11383         !game_mm.game_over)                             // game won
11384     {
11385       LevelSolved();
11386
11387       game_mm.game_over = TRUE;
11388
11389       game.all_players_gone = TRUE;
11390     }
11391
11392     if (game_mm.game_over)                              // game lost
11393       game.all_players_gone = TRUE;
11394   }
11395 }
11396
11397 static void CheckLevelTime(void)
11398 {
11399   int i;
11400
11401   if (TimeFrames >= FRAMES_PER_SECOND)
11402   {
11403     TimeFrames = 0;
11404     TapeTime++;
11405
11406     for (i = 0; i < MAX_PLAYERS; i++)
11407     {
11408       struct PlayerInfo *player = &stored_player[i];
11409
11410       if (SHIELD_ON(player))
11411       {
11412         player->shield_normal_time_left--;
11413
11414         if (player->shield_deadly_time_left > 0)
11415           player->shield_deadly_time_left--;
11416       }
11417     }
11418
11419     if (!game.LevelSolved && !level.use_step_counter)
11420     {
11421       TimePlayed++;
11422
11423       if (TimeLeft > 0)
11424       {
11425         TimeLeft--;
11426
11427         if (TimeLeft <= 10 && setup.time_limit)
11428           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11429
11430         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11431            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11432
11433         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11434
11435         if (!TimeLeft && setup.time_limit)
11436         {
11437           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11438             game_em.lev->killed_out_of_time = TRUE;
11439           else
11440             for (i = 0; i < MAX_PLAYERS; i++)
11441               KillPlayer(&stored_player[i]);
11442         }
11443       }
11444       else if (game.no_time_limit && !game.all_players_gone)
11445       {
11446         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11447       }
11448
11449       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11450     }
11451
11452     if (tape.recording || tape.playing)
11453       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11454   }
11455
11456   if (tape.recording || tape.playing)
11457     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11458
11459   UpdateAndDisplayGameControlValues();
11460 }
11461
11462 void AdvanceFrameAndPlayerCounters(int player_nr)
11463 {
11464   int i;
11465
11466   // advance frame counters (global frame counter and time frame counter)
11467   FrameCounter++;
11468   TimeFrames++;
11469
11470   // advance player counters (counters for move delay, move animation etc.)
11471   for (i = 0; i < MAX_PLAYERS; i++)
11472   {
11473     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11474     int move_delay_value = stored_player[i].move_delay_value;
11475     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11476
11477     if (!advance_player_counters)       // not all players may be affected
11478       continue;
11479
11480     if (move_frames == 0)       // less than one move per game frame
11481     {
11482       int stepsize = TILEX / move_delay_value;
11483       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11484       int count = (stored_player[i].is_moving ?
11485                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11486
11487       if (count % delay == 0)
11488         move_frames = 1;
11489     }
11490
11491     stored_player[i].Frame += move_frames;
11492
11493     if (stored_player[i].MovPos != 0)
11494       stored_player[i].StepFrame += move_frames;
11495
11496     if (stored_player[i].move_delay > 0)
11497       stored_player[i].move_delay--;
11498
11499     // due to bugs in previous versions, counter must count up, not down
11500     if (stored_player[i].push_delay != -1)
11501       stored_player[i].push_delay++;
11502
11503     if (stored_player[i].drop_delay > 0)
11504       stored_player[i].drop_delay--;
11505
11506     if (stored_player[i].is_dropping_pressed)
11507       stored_player[i].drop_pressed_delay++;
11508   }
11509 }
11510
11511 void StartGameActions(boolean init_network_game, boolean record_tape,
11512                       int random_seed)
11513 {
11514   unsigned int new_random_seed = InitRND(random_seed);
11515
11516   if (record_tape)
11517     TapeStartRecording(new_random_seed);
11518
11519   if (init_network_game)
11520   {
11521     SendToServer_LevelFile();
11522     SendToServer_StartPlaying();
11523
11524     return;
11525   }
11526
11527   InitGame();
11528 }
11529
11530 static void GameActionsExt(void)
11531 {
11532 #if 0
11533   static unsigned int game_frame_delay = 0;
11534 #endif
11535   unsigned int game_frame_delay_value;
11536   byte *recorded_player_action;
11537   byte summarized_player_action = 0;
11538   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11539   int i;
11540
11541   // detect endless loops, caused by custom element programming
11542   if (recursion_loop_detected && recursion_loop_depth == 0)
11543   {
11544     char *message = getStringCat3("Internal Error! Element ",
11545                                   EL_NAME(recursion_loop_element),
11546                                   " caused endless loop! Quit the game?");
11547
11548     Warn("element '%s' caused endless loop in game engine",
11549          EL_NAME(recursion_loop_element));
11550
11551     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11552
11553     recursion_loop_detected = FALSE;    // if game should be continued
11554
11555     free(message);
11556
11557     return;
11558   }
11559
11560   if (game.restart_level)
11561     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11562
11563   CheckLevelSolved();
11564
11565   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11566     GameWon();
11567
11568   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11569     TapeStop();
11570
11571   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11572     return;
11573
11574   game_frame_delay_value =
11575     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11576
11577   if (tape.playing && tape.warp_forward && !tape.pausing)
11578     game_frame_delay_value = 0;
11579
11580   SetVideoFrameDelay(game_frame_delay_value);
11581
11582   // (de)activate virtual buttons depending on current game status
11583   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11584   {
11585     if (game.all_players_gone)  // if no players there to be controlled anymore
11586       SetOverlayActive(FALSE);
11587     else if (!tape.playing)     // if game continues after tape stopped playing
11588       SetOverlayActive(TRUE);
11589   }
11590
11591 #if 0
11592 #if 0
11593   // ---------- main game synchronization point ----------
11594
11595   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11596
11597   Debug("game:playing:skip", "skip == %d", skip);
11598
11599 #else
11600   // ---------- main game synchronization point ----------
11601
11602   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11603 #endif
11604 #endif
11605
11606   if (network_playing && !network_player_action_received)
11607   {
11608     // try to get network player actions in time
11609
11610     // last chance to get network player actions without main loop delay
11611     HandleNetworking();
11612
11613     // game was quit by network peer
11614     if (game_status != GAME_MODE_PLAYING)
11615       return;
11616
11617     // check if network player actions still missing and game still running
11618     if (!network_player_action_received && !checkGameEnded())
11619       return;           // failed to get network player actions in time
11620
11621     // do not yet reset "network_player_action_received" (for tape.pausing)
11622   }
11623
11624   if (tape.pausing)
11625     return;
11626
11627   // at this point we know that we really continue executing the game
11628
11629   network_player_action_received = FALSE;
11630
11631   // when playing tape, read previously recorded player input from tape data
11632   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11633
11634   local_player->effective_mouse_action = local_player->mouse_action;
11635
11636   if (recorded_player_action != NULL)
11637     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11638                                  recorded_player_action);
11639
11640   // TapePlayAction() may return NULL when toggling to "pause before death"
11641   if (tape.pausing)
11642     return;
11643
11644   if (tape.set_centered_player)
11645   {
11646     game.centered_player_nr_next = tape.centered_player_nr_next;
11647     game.set_centered_player = TRUE;
11648   }
11649
11650   for (i = 0; i < MAX_PLAYERS; i++)
11651   {
11652     summarized_player_action |= stored_player[i].action;
11653
11654     if (!network_playing && (game.team_mode || tape.playing))
11655       stored_player[i].effective_action = stored_player[i].action;
11656   }
11657
11658   if (network_playing && !checkGameEnded())
11659     SendToServer_MovePlayer(summarized_player_action);
11660
11661   // summarize all actions at local players mapped input device position
11662   // (this allows using different input devices in single player mode)
11663   if (!network.enabled && !game.team_mode)
11664     stored_player[map_player_action[local_player->index_nr]].effective_action =
11665       summarized_player_action;
11666
11667   // summarize all actions at centered player in local team mode
11668   if (tape.recording &&
11669       setup.team_mode && !network.enabled &&
11670       setup.input_on_focus &&
11671       game.centered_player_nr != -1)
11672   {
11673     for (i = 0; i < MAX_PLAYERS; i++)
11674       stored_player[map_player_action[i]].effective_action =
11675         (i == game.centered_player_nr ? summarized_player_action : 0);
11676   }
11677
11678   if (recorded_player_action != NULL)
11679     for (i = 0; i < MAX_PLAYERS; i++)
11680       stored_player[i].effective_action = recorded_player_action[i];
11681
11682   for (i = 0; i < MAX_PLAYERS; i++)
11683   {
11684     tape_action[i] = stored_player[i].effective_action;
11685
11686     /* (this may happen in the RND game engine if a player was not present on
11687        the playfield on level start, but appeared later from a custom element */
11688     if (setup.team_mode &&
11689         tape.recording &&
11690         tape_action[i] &&
11691         !tape.player_participates[i])
11692       tape.player_participates[i] = TRUE;
11693   }
11694
11695   SetTapeActionFromMouseAction(tape_action,
11696                                &local_player->effective_mouse_action);
11697
11698   // only record actions from input devices, but not programmed actions
11699   if (tape.recording)
11700     TapeRecordAction(tape_action);
11701
11702   // remember if game was played (especially after tape stopped playing)
11703   if (!tape.playing && summarized_player_action)
11704     game.GamePlayed = TRUE;
11705
11706 #if USE_NEW_PLAYER_ASSIGNMENTS
11707   // !!! also map player actions in single player mode !!!
11708   // if (game.team_mode)
11709   if (1)
11710   {
11711     byte mapped_action[MAX_PLAYERS];
11712
11713 #if DEBUG_PLAYER_ACTIONS
11714     for (i = 0; i < MAX_PLAYERS; i++)
11715       DebugContinued("", "%d, ", stored_player[i].effective_action);
11716 #endif
11717
11718     for (i = 0; i < MAX_PLAYERS; i++)
11719       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11720
11721     for (i = 0; i < MAX_PLAYERS; i++)
11722       stored_player[i].effective_action = mapped_action[i];
11723
11724 #if DEBUG_PLAYER_ACTIONS
11725     DebugContinued("", "=> ");
11726     for (i = 0; i < MAX_PLAYERS; i++)
11727       DebugContinued("", "%d, ", stored_player[i].effective_action);
11728     DebugContinued("game:playing:player", "\n");
11729 #endif
11730   }
11731 #if DEBUG_PLAYER_ACTIONS
11732   else
11733   {
11734     for (i = 0; i < MAX_PLAYERS; i++)
11735       DebugContinued("", "%d, ", stored_player[i].effective_action);
11736     DebugContinued("game:playing:player", "\n");
11737   }
11738 #endif
11739 #endif
11740
11741   for (i = 0; i < MAX_PLAYERS; i++)
11742   {
11743     // allow engine snapshot in case of changed movement attempt
11744     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11745         (stored_player[i].effective_action & KEY_MOTION))
11746       game.snapshot.changed_action = TRUE;
11747
11748     // allow engine snapshot in case of snapping/dropping attempt
11749     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11750         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11751       game.snapshot.changed_action = TRUE;
11752
11753     game.snapshot.last_action[i] = stored_player[i].effective_action;
11754   }
11755
11756   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11757   {
11758     GameActions_EM_Main();
11759   }
11760   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11761   {
11762     GameActions_SP_Main();
11763   }
11764   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11765   {
11766     GameActions_MM_Main();
11767   }
11768   else
11769   {
11770     GameActions_RND_Main();
11771   }
11772
11773   BlitScreenToBitmap(backbuffer);
11774
11775   CheckLevelSolved();
11776   CheckLevelTime();
11777
11778   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11779
11780   if (global.show_frames_per_second)
11781   {
11782     static unsigned int fps_counter = 0;
11783     static int fps_frames = 0;
11784     unsigned int fps_delay_ms = Counter() - fps_counter;
11785
11786     fps_frames++;
11787
11788     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11789     {
11790       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11791
11792       fps_frames = 0;
11793       fps_counter = Counter();
11794
11795       // always draw FPS to screen after FPS value was updated
11796       redraw_mask |= REDRAW_FPS;
11797     }
11798
11799     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11800     if (GetDrawDeactivationMask() == REDRAW_NONE)
11801       redraw_mask |= REDRAW_FPS;
11802   }
11803 }
11804
11805 static void GameActions_CheckSaveEngineSnapshot(void)
11806 {
11807   if (!game.snapshot.save_snapshot)
11808     return;
11809
11810   // clear flag for saving snapshot _before_ saving snapshot
11811   game.snapshot.save_snapshot = FALSE;
11812
11813   SaveEngineSnapshotToList();
11814 }
11815
11816 void GameActions(void)
11817 {
11818   GameActionsExt();
11819
11820   GameActions_CheckSaveEngineSnapshot();
11821 }
11822
11823 void GameActions_EM_Main(void)
11824 {
11825   byte effective_action[MAX_PLAYERS];
11826   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11827   int i;
11828
11829   for (i = 0; i < MAX_PLAYERS; i++)
11830     effective_action[i] = stored_player[i].effective_action;
11831
11832   GameActions_EM(effective_action, warp_mode);
11833 }
11834
11835 void GameActions_SP_Main(void)
11836 {
11837   byte effective_action[MAX_PLAYERS];
11838   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11839   int i;
11840
11841   for (i = 0; i < MAX_PLAYERS; i++)
11842     effective_action[i] = stored_player[i].effective_action;
11843
11844   GameActions_SP(effective_action, warp_mode);
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847   {
11848     if (stored_player[i].force_dropping)
11849       stored_player[i].action |= KEY_BUTTON_DROP;
11850
11851     stored_player[i].force_dropping = FALSE;
11852   }
11853 }
11854
11855 void GameActions_MM_Main(void)
11856 {
11857   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11858
11859   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11860 }
11861
11862 void GameActions_RND_Main(void)
11863 {
11864   GameActions_RND();
11865 }
11866
11867 void GameActions_RND(void)
11868 {
11869   static struct MouseActionInfo mouse_action_last = { 0 };
11870   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11871   int magic_wall_x = 0, magic_wall_y = 0;
11872   int i, x, y, element, graphic, last_gfx_frame;
11873
11874   InitPlayfieldScanModeVars();
11875
11876   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11877   {
11878     SCAN_PLAYFIELD(x, y)
11879     {
11880       ChangeCount[x][y] = 0;
11881       ChangeEvent[x][y] = -1;
11882     }
11883   }
11884
11885   if (game.set_centered_player)
11886   {
11887     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11888
11889     // switching to "all players" only possible if all players fit to screen
11890     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11891     {
11892       game.centered_player_nr_next = game.centered_player_nr;
11893       game.set_centered_player = FALSE;
11894     }
11895
11896     // do not switch focus to non-existing (or non-active) player
11897     if (game.centered_player_nr_next >= 0 &&
11898         !stored_player[game.centered_player_nr_next].active)
11899     {
11900       game.centered_player_nr_next = game.centered_player_nr;
11901       game.set_centered_player = FALSE;
11902     }
11903   }
11904
11905   if (game.set_centered_player &&
11906       ScreenMovPos == 0)        // screen currently aligned at tile position
11907   {
11908     int sx, sy;
11909
11910     if (game.centered_player_nr_next == -1)
11911     {
11912       setScreenCenteredToAllPlayers(&sx, &sy);
11913     }
11914     else
11915     {
11916       sx = stored_player[game.centered_player_nr_next].jx;
11917       sy = stored_player[game.centered_player_nr_next].jy;
11918     }
11919
11920     game.centered_player_nr = game.centered_player_nr_next;
11921     game.set_centered_player = FALSE;
11922
11923     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11924     DrawGameDoorValues();
11925   }
11926
11927   for (i = 0; i < MAX_PLAYERS; i++)
11928   {
11929     int actual_player_action = stored_player[i].effective_action;
11930
11931 #if 1
11932     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11933        - rnd_equinox_tetrachloride 048
11934        - rnd_equinox_tetrachloride_ii 096
11935        - rnd_emanuel_schmieg 002
11936        - doctor_sloan_ww 001, 020
11937     */
11938     if (stored_player[i].MovPos == 0)
11939       CheckGravityMovement(&stored_player[i]);
11940 #endif
11941
11942     // overwrite programmed action with tape action
11943     if (stored_player[i].programmed_action)
11944       actual_player_action = stored_player[i].programmed_action;
11945
11946     PlayerActions(&stored_player[i], actual_player_action);
11947
11948     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11949   }
11950
11951   ScrollScreen(NULL, SCROLL_GO_ON);
11952
11953   /* for backwards compatibility, the following code emulates a fixed bug that
11954      occured when pushing elements (causing elements that just made their last
11955      pushing step to already (if possible) make their first falling step in the
11956      same game frame, which is bad); this code is also needed to use the famous
11957      "spring push bug" which is used in older levels and might be wanted to be
11958      used also in newer levels, but in this case the buggy pushing code is only
11959      affecting the "spring" element and no other elements */
11960
11961   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11962   {
11963     for (i = 0; i < MAX_PLAYERS; i++)
11964     {
11965       struct PlayerInfo *player = &stored_player[i];
11966       int x = player->jx;
11967       int y = player->jy;
11968
11969       if (player->active && player->is_pushing && player->is_moving &&
11970           IS_MOVING(x, y) &&
11971           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11972            Tile[x][y] == EL_SPRING))
11973       {
11974         ContinueMoving(x, y);
11975
11976         // continue moving after pushing (this is actually a bug)
11977         if (!IS_MOVING(x, y))
11978           Stop[x][y] = FALSE;
11979       }
11980     }
11981   }
11982
11983   SCAN_PLAYFIELD(x, y)
11984   {
11985     Last[x][y] = Tile[x][y];
11986
11987     ChangeCount[x][y] = 0;
11988     ChangeEvent[x][y] = -1;
11989
11990     // this must be handled before main playfield loop
11991     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11992     {
11993       MovDelay[x][y]--;
11994       if (MovDelay[x][y] <= 0)
11995         RemoveField(x, y);
11996     }
11997
11998     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11999     {
12000       MovDelay[x][y]--;
12001       if (MovDelay[x][y] <= 0)
12002       {
12003         RemoveField(x, y);
12004         TEST_DrawLevelField(x, y);
12005
12006         TestIfElementTouchesCustomElement(x, y);        // for empty space
12007       }
12008     }
12009
12010 #if DEBUG
12011     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12012     {
12013       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12014             x, y);
12015       Debug("game:playing:GameActions_RND", "This should never happen!");
12016
12017       ChangePage[x][y] = -1;
12018     }
12019 #endif
12020
12021     Stop[x][y] = FALSE;
12022     if (WasJustMoving[x][y] > 0)
12023       WasJustMoving[x][y]--;
12024     if (WasJustFalling[x][y] > 0)
12025       WasJustFalling[x][y]--;
12026     if (CheckCollision[x][y] > 0)
12027       CheckCollision[x][y]--;
12028     if (CheckImpact[x][y] > 0)
12029       CheckImpact[x][y]--;
12030
12031     GfxFrame[x][y]++;
12032
12033     /* reset finished pushing action (not done in ContinueMoving() to allow
12034        continuous pushing animation for elements with zero push delay) */
12035     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12036     {
12037       ResetGfxAnimation(x, y);
12038       TEST_DrawLevelField(x, y);
12039     }
12040
12041 #if DEBUG
12042     if (IS_BLOCKED(x, y))
12043     {
12044       int oldx, oldy;
12045
12046       Blocked2Moving(x, y, &oldx, &oldy);
12047       if (!IS_MOVING(oldx, oldy))
12048       {
12049         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12050         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12051         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12052         Debug("game:playing:GameActions_RND", "This should never happen!");
12053       }
12054     }
12055 #endif
12056   }
12057
12058   if (mouse_action.button)
12059   {
12060     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12061
12062     x = mouse_action.lx;
12063     y = mouse_action.ly;
12064     element = Tile[x][y];
12065
12066     if (new_button)
12067     {
12068       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12069       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12070     }
12071
12072     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12073     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12074   }
12075
12076   SCAN_PLAYFIELD(x, y)
12077   {
12078     element = Tile[x][y];
12079     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12080     last_gfx_frame = GfxFrame[x][y];
12081
12082     ResetGfxFrame(x, y);
12083
12084     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12085       DrawLevelGraphicAnimation(x, y, graphic);
12086
12087     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12088         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12089       ResetRandomAnimationValue(x, y);
12090
12091     SetRandomAnimationValue(x, y);
12092
12093     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12094
12095     if (IS_INACTIVE(element))
12096     {
12097       if (IS_ANIMATED(graphic))
12098         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12099
12100       continue;
12101     }
12102
12103     // this may take place after moving, so 'element' may have changed
12104     if (IS_CHANGING(x, y) &&
12105         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12106     {
12107       int page = element_info[element].event_page_nr[CE_DELAY];
12108
12109       HandleElementChange(x, y, page);
12110
12111       element = Tile[x][y];
12112       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12113     }
12114
12115     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12116     {
12117       StartMoving(x, y);
12118
12119       element = Tile[x][y];
12120       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12121
12122       if (IS_ANIMATED(graphic) &&
12123           !IS_MOVING(x, y) &&
12124           !Stop[x][y])
12125         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12126
12127       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12128         TEST_DrawTwinkleOnField(x, y);
12129     }
12130     else if (element == EL_ACID)
12131     {
12132       if (!Stop[x][y])
12133         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12134     }
12135     else if ((element == EL_EXIT_OPEN ||
12136               element == EL_EM_EXIT_OPEN ||
12137               element == EL_SP_EXIT_OPEN ||
12138               element == EL_STEEL_EXIT_OPEN ||
12139               element == EL_EM_STEEL_EXIT_OPEN ||
12140               element == EL_SP_TERMINAL ||
12141               element == EL_SP_TERMINAL_ACTIVE ||
12142               element == EL_EXTRA_TIME ||
12143               element == EL_SHIELD_NORMAL ||
12144               element == EL_SHIELD_DEADLY) &&
12145              IS_ANIMATED(graphic))
12146       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12147     else if (IS_MOVING(x, y))
12148       ContinueMoving(x, y);
12149     else if (IS_ACTIVE_BOMB(element))
12150       CheckDynamite(x, y);
12151     else if (element == EL_AMOEBA_GROWING)
12152       AmoebaGrowing(x, y);
12153     else if (element == EL_AMOEBA_SHRINKING)
12154       AmoebaShrinking(x, y);
12155
12156 #if !USE_NEW_AMOEBA_CODE
12157     else if (IS_AMOEBALIVE(element))
12158       AmoebaReproduce(x, y);
12159 #endif
12160
12161     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12162       Life(x, y);
12163     else if (element == EL_EXIT_CLOSED)
12164       CheckExit(x, y);
12165     else if (element == EL_EM_EXIT_CLOSED)
12166       CheckExitEM(x, y);
12167     else if (element == EL_STEEL_EXIT_CLOSED)
12168       CheckExitSteel(x, y);
12169     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12170       CheckExitSteelEM(x, y);
12171     else if (element == EL_SP_EXIT_CLOSED)
12172       CheckExitSP(x, y);
12173     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12174              element == EL_EXPANDABLE_STEELWALL_GROWING)
12175       MauerWaechst(x, y);
12176     else if (element == EL_EXPANDABLE_WALL ||
12177              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12178              element == EL_EXPANDABLE_WALL_VERTICAL ||
12179              element == EL_EXPANDABLE_WALL_ANY ||
12180              element == EL_BD_EXPANDABLE_WALL)
12181       MauerAbleger(x, y);
12182     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12183              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12184              element == EL_EXPANDABLE_STEELWALL_ANY)
12185       MauerAblegerStahl(x, y);
12186     else if (element == EL_FLAMES)
12187       CheckForDragon(x, y);
12188     else if (element == EL_EXPLOSION)
12189       ; // drawing of correct explosion animation is handled separately
12190     else if (element == EL_ELEMENT_SNAPPING ||
12191              element == EL_DIAGONAL_SHRINKING ||
12192              element == EL_DIAGONAL_GROWING)
12193     {
12194       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12195
12196       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12197     }
12198     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12199       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12200
12201     if (IS_BELT_ACTIVE(element))
12202       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12203
12204     if (game.magic_wall_active)
12205     {
12206       int jx = local_player->jx, jy = local_player->jy;
12207
12208       // play the element sound at the position nearest to the player
12209       if ((element == EL_MAGIC_WALL_FULL ||
12210            element == EL_MAGIC_WALL_ACTIVE ||
12211            element == EL_MAGIC_WALL_EMPTYING ||
12212            element == EL_BD_MAGIC_WALL_FULL ||
12213            element == EL_BD_MAGIC_WALL_ACTIVE ||
12214            element == EL_BD_MAGIC_WALL_EMPTYING ||
12215            element == EL_DC_MAGIC_WALL_FULL ||
12216            element == EL_DC_MAGIC_WALL_ACTIVE ||
12217            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12218           ABS(x - jx) + ABS(y - jy) <
12219           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12220       {
12221         magic_wall_x = x;
12222         magic_wall_y = y;
12223       }
12224     }
12225   }
12226
12227 #if USE_NEW_AMOEBA_CODE
12228   // new experimental amoeba growth stuff
12229   if (!(FrameCounter % 8))
12230   {
12231     static unsigned int random = 1684108901;
12232
12233     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12234     {
12235       x = RND(lev_fieldx);
12236       y = RND(lev_fieldy);
12237       element = Tile[x][y];
12238
12239       if (!IS_PLAYER(x,y) &&
12240           (element == EL_EMPTY ||
12241            CAN_GROW_INTO(element) ||
12242            element == EL_QUICKSAND_EMPTY ||
12243            element == EL_QUICKSAND_FAST_EMPTY ||
12244            element == EL_ACID_SPLASH_LEFT ||
12245            element == EL_ACID_SPLASH_RIGHT))
12246       {
12247         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12248             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12249             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12250             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12251           Tile[x][y] = EL_AMOEBA_DROP;
12252       }
12253
12254       random = random * 129 + 1;
12255     }
12256   }
12257 #endif
12258
12259   game.explosions_delayed = FALSE;
12260
12261   SCAN_PLAYFIELD(x, y)
12262   {
12263     element = Tile[x][y];
12264
12265     if (ExplodeField[x][y])
12266       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12267     else if (element == EL_EXPLOSION)
12268       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12269
12270     ExplodeField[x][y] = EX_TYPE_NONE;
12271   }
12272
12273   game.explosions_delayed = TRUE;
12274
12275   if (game.magic_wall_active)
12276   {
12277     if (!(game.magic_wall_time_left % 4))
12278     {
12279       int element = Tile[magic_wall_x][magic_wall_y];
12280
12281       if (element == EL_BD_MAGIC_WALL_FULL ||
12282           element == EL_BD_MAGIC_WALL_ACTIVE ||
12283           element == EL_BD_MAGIC_WALL_EMPTYING)
12284         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12285       else if (element == EL_DC_MAGIC_WALL_FULL ||
12286                element == EL_DC_MAGIC_WALL_ACTIVE ||
12287                element == EL_DC_MAGIC_WALL_EMPTYING)
12288         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12289       else
12290         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12291     }
12292
12293     if (game.magic_wall_time_left > 0)
12294     {
12295       game.magic_wall_time_left--;
12296
12297       if (!game.magic_wall_time_left)
12298       {
12299         SCAN_PLAYFIELD(x, y)
12300         {
12301           element = Tile[x][y];
12302
12303           if (element == EL_MAGIC_WALL_ACTIVE ||
12304               element == EL_MAGIC_WALL_FULL)
12305           {
12306             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12307             TEST_DrawLevelField(x, y);
12308           }
12309           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12310                    element == EL_BD_MAGIC_WALL_FULL)
12311           {
12312             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12313             TEST_DrawLevelField(x, y);
12314           }
12315           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12316                    element == EL_DC_MAGIC_WALL_FULL)
12317           {
12318             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12319             TEST_DrawLevelField(x, y);
12320           }
12321         }
12322
12323         game.magic_wall_active = FALSE;
12324       }
12325     }
12326   }
12327
12328   if (game.light_time_left > 0)
12329   {
12330     game.light_time_left--;
12331
12332     if (game.light_time_left == 0)
12333       RedrawAllLightSwitchesAndInvisibleElements();
12334   }
12335
12336   if (game.timegate_time_left > 0)
12337   {
12338     game.timegate_time_left--;
12339
12340     if (game.timegate_time_left == 0)
12341       CloseAllOpenTimegates();
12342   }
12343
12344   if (game.lenses_time_left > 0)
12345   {
12346     game.lenses_time_left--;
12347
12348     if (game.lenses_time_left == 0)
12349       RedrawAllInvisibleElementsForLenses();
12350   }
12351
12352   if (game.magnify_time_left > 0)
12353   {
12354     game.magnify_time_left--;
12355
12356     if (game.magnify_time_left == 0)
12357       RedrawAllInvisibleElementsForMagnifier();
12358   }
12359
12360   for (i = 0; i < MAX_PLAYERS; i++)
12361   {
12362     struct PlayerInfo *player = &stored_player[i];
12363
12364     if (SHIELD_ON(player))
12365     {
12366       if (player->shield_deadly_time_left)
12367         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12368       else if (player->shield_normal_time_left)
12369         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12370     }
12371   }
12372
12373 #if USE_DELAYED_GFX_REDRAW
12374   SCAN_PLAYFIELD(x, y)
12375   {
12376     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12377     {
12378       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12379          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12380
12381       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12382         DrawLevelField(x, y);
12383
12384       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12385         DrawLevelFieldCrumbled(x, y);
12386
12387       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12388         DrawLevelFieldCrumbledNeighbours(x, y);
12389
12390       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12391         DrawTwinkleOnField(x, y);
12392     }
12393
12394     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12395   }
12396 #endif
12397
12398   DrawAllPlayers();
12399   PlayAllPlayersSound();
12400
12401   for (i = 0; i < MAX_PLAYERS; i++)
12402   {
12403     struct PlayerInfo *player = &stored_player[i];
12404
12405     if (player->show_envelope != 0 && (!player->active ||
12406                                        player->MovPos == 0))
12407     {
12408       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12409
12410       player->show_envelope = 0;
12411     }
12412   }
12413
12414   // use random number generator in every frame to make it less predictable
12415   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12416     RND(1);
12417
12418   mouse_action_last = mouse_action;
12419 }
12420
12421 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12422 {
12423   int min_x = x, min_y = y, max_x = x, max_y = y;
12424   int i;
12425
12426   for (i = 0; i < MAX_PLAYERS; i++)
12427   {
12428     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12429
12430     if (!stored_player[i].active || &stored_player[i] == player)
12431       continue;
12432
12433     min_x = MIN(min_x, jx);
12434     min_y = MIN(min_y, jy);
12435     max_x = MAX(max_x, jx);
12436     max_y = MAX(max_y, jy);
12437   }
12438
12439   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12440 }
12441
12442 static boolean AllPlayersInVisibleScreen(void)
12443 {
12444   int i;
12445
12446   for (i = 0; i < MAX_PLAYERS; i++)
12447   {
12448     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12449
12450     if (!stored_player[i].active)
12451       continue;
12452
12453     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12454       return FALSE;
12455   }
12456
12457   return TRUE;
12458 }
12459
12460 void ScrollLevel(int dx, int dy)
12461 {
12462   int scroll_offset = 2 * TILEX_VAR;
12463   int x, y;
12464
12465   BlitBitmap(drawto_field, drawto_field,
12466              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12467              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12468              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12469              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12470              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12471              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12472
12473   if (dx != 0)
12474   {
12475     x = (dx == 1 ? BX1 : BX2);
12476     for (y = BY1; y <= BY2; y++)
12477       DrawScreenField(x, y);
12478   }
12479
12480   if (dy != 0)
12481   {
12482     y = (dy == 1 ? BY1 : BY2);
12483     for (x = BX1; x <= BX2; x++)
12484       DrawScreenField(x, y);
12485   }
12486
12487   redraw_mask |= REDRAW_FIELD;
12488 }
12489
12490 static boolean canFallDown(struct PlayerInfo *player)
12491 {
12492   int jx = player->jx, jy = player->jy;
12493
12494   return (IN_LEV_FIELD(jx, jy + 1) &&
12495           (IS_FREE(jx, jy + 1) ||
12496            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12497           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12498           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12499 }
12500
12501 static boolean canPassField(int x, int y, int move_dir)
12502 {
12503   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12504   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12505   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12506   int nextx = x + dx;
12507   int nexty = y + dy;
12508   int element = Tile[x][y];
12509
12510   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12511           !CAN_MOVE(element) &&
12512           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12513           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12514           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12515 }
12516
12517 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12518 {
12519   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12520   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12521   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12522   int newx = x + dx;
12523   int newy = y + dy;
12524
12525   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12526           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12527           (IS_DIGGABLE(Tile[newx][newy]) ||
12528            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12529            canPassField(newx, newy, move_dir)));
12530 }
12531
12532 static void CheckGravityMovement(struct PlayerInfo *player)
12533 {
12534   if (player->gravity && !player->programmed_action)
12535   {
12536     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12537     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12538     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12539     int jx = player->jx, jy = player->jy;
12540     boolean player_is_moving_to_valid_field =
12541       (!player_is_snapping &&
12542        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12543         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12544     boolean player_can_fall_down = canFallDown(player);
12545
12546     if (player_can_fall_down &&
12547         !player_is_moving_to_valid_field)
12548       player->programmed_action = MV_DOWN;
12549   }
12550 }
12551
12552 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12553 {
12554   return CheckGravityMovement(player);
12555
12556   if (player->gravity && !player->programmed_action)
12557   {
12558     int jx = player->jx, jy = player->jy;
12559     boolean field_under_player_is_free =
12560       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12561     boolean player_is_standing_on_valid_field =
12562       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12563        (IS_WALKABLE(Tile[jx][jy]) &&
12564         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12565
12566     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12567       player->programmed_action = MV_DOWN;
12568   }
12569 }
12570
12571 /*
12572   MovePlayerOneStep()
12573   -----------------------------------------------------------------------------
12574   dx, dy:               direction (non-diagonal) to try to move the player to
12575   real_dx, real_dy:     direction as read from input device (can be diagonal)
12576 */
12577
12578 boolean MovePlayerOneStep(struct PlayerInfo *player,
12579                           int dx, int dy, int real_dx, int real_dy)
12580 {
12581   int jx = player->jx, jy = player->jy;
12582   int new_jx = jx + dx, new_jy = jy + dy;
12583   int can_move;
12584   boolean player_can_move = !player->cannot_move;
12585
12586   if (!player->active || (!dx && !dy))
12587     return MP_NO_ACTION;
12588
12589   player->MovDir = (dx < 0 ? MV_LEFT :
12590                     dx > 0 ? MV_RIGHT :
12591                     dy < 0 ? MV_UP :
12592                     dy > 0 ? MV_DOWN :  MV_NONE);
12593
12594   if (!IN_LEV_FIELD(new_jx, new_jy))
12595     return MP_NO_ACTION;
12596
12597   if (!player_can_move)
12598   {
12599     if (player->MovPos == 0)
12600     {
12601       player->is_moving = FALSE;
12602       player->is_digging = FALSE;
12603       player->is_collecting = FALSE;
12604       player->is_snapping = FALSE;
12605       player->is_pushing = FALSE;
12606     }
12607   }
12608
12609   if (!network.enabled && game.centered_player_nr == -1 &&
12610       !AllPlayersInSight(player, new_jx, new_jy))
12611     return MP_NO_ACTION;
12612
12613   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12614   if (can_move != MP_MOVING)
12615     return can_move;
12616
12617   // check if DigField() has caused relocation of the player
12618   if (player->jx != jx || player->jy != jy)
12619     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12620
12621   StorePlayer[jx][jy] = 0;
12622   player->last_jx = jx;
12623   player->last_jy = jy;
12624   player->jx = new_jx;
12625   player->jy = new_jy;
12626   StorePlayer[new_jx][new_jy] = player->element_nr;
12627
12628   if (player->move_delay_value_next != -1)
12629   {
12630     player->move_delay_value = player->move_delay_value_next;
12631     player->move_delay_value_next = -1;
12632   }
12633
12634   player->MovPos =
12635     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12636
12637   player->step_counter++;
12638
12639   PlayerVisit[jx][jy] = FrameCounter;
12640
12641   player->is_moving = TRUE;
12642
12643 #if 1
12644   // should better be called in MovePlayer(), but this breaks some tapes
12645   ScrollPlayer(player, SCROLL_INIT);
12646 #endif
12647
12648   return MP_MOVING;
12649 }
12650
12651 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12652 {
12653   int jx = player->jx, jy = player->jy;
12654   int old_jx = jx, old_jy = jy;
12655   int moved = MP_NO_ACTION;
12656
12657   if (!player->active)
12658     return FALSE;
12659
12660   if (!dx && !dy)
12661   {
12662     if (player->MovPos == 0)
12663     {
12664       player->is_moving = FALSE;
12665       player->is_digging = FALSE;
12666       player->is_collecting = FALSE;
12667       player->is_snapping = FALSE;
12668       player->is_pushing = FALSE;
12669     }
12670
12671     return FALSE;
12672   }
12673
12674   if (player->move_delay > 0)
12675     return FALSE;
12676
12677   player->move_delay = -1;              // set to "uninitialized" value
12678
12679   // store if player is automatically moved to next field
12680   player->is_auto_moving = (player->programmed_action != MV_NONE);
12681
12682   // remove the last programmed player action
12683   player->programmed_action = 0;
12684
12685   if (player->MovPos)
12686   {
12687     // should only happen if pre-1.2 tape recordings are played
12688     // this is only for backward compatibility
12689
12690     int original_move_delay_value = player->move_delay_value;
12691
12692 #if DEBUG
12693     Debug("game:playing:MovePlayer",
12694           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12695           tape.counter);
12696 #endif
12697
12698     // scroll remaining steps with finest movement resolution
12699     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12700
12701     while (player->MovPos)
12702     {
12703       ScrollPlayer(player, SCROLL_GO_ON);
12704       ScrollScreen(NULL, SCROLL_GO_ON);
12705
12706       AdvanceFrameAndPlayerCounters(player->index_nr);
12707
12708       DrawAllPlayers();
12709       BackToFront_WithFrameDelay(0);
12710     }
12711
12712     player->move_delay_value = original_move_delay_value;
12713   }
12714
12715   player->is_active = FALSE;
12716
12717   if (player->last_move_dir & MV_HORIZONTAL)
12718   {
12719     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12720       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12721   }
12722   else
12723   {
12724     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12725       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12726   }
12727
12728   if (!moved && !player->is_active)
12729   {
12730     player->is_moving = FALSE;
12731     player->is_digging = FALSE;
12732     player->is_collecting = FALSE;
12733     player->is_snapping = FALSE;
12734     player->is_pushing = FALSE;
12735   }
12736
12737   jx = player->jx;
12738   jy = player->jy;
12739
12740   if (moved & MP_MOVING && !ScreenMovPos &&
12741       (player->index_nr == game.centered_player_nr ||
12742        game.centered_player_nr == -1))
12743   {
12744     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12745
12746     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12747     {
12748       // actual player has left the screen -- scroll in that direction
12749       if (jx != old_jx)         // player has moved horizontally
12750         scroll_x += (jx - old_jx);
12751       else                      // player has moved vertically
12752         scroll_y += (jy - old_jy);
12753     }
12754     else
12755     {
12756       int offset_raw = game.scroll_delay_value;
12757
12758       if (jx != old_jx)         // player has moved horizontally
12759       {
12760         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12761         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12762         int new_scroll_x = jx - MIDPOSX + offset_x;
12763
12764         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12765             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12766           scroll_x = new_scroll_x;
12767
12768         // don't scroll over playfield boundaries
12769         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12770
12771         // don't scroll more than one field at a time
12772         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12773
12774         // don't scroll against the player's moving direction
12775         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12776             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12777           scroll_x = old_scroll_x;
12778       }
12779       else                      // player has moved vertically
12780       {
12781         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12782         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12783         int new_scroll_y = jy - MIDPOSY + offset_y;
12784
12785         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12786             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12787           scroll_y = new_scroll_y;
12788
12789         // don't scroll over playfield boundaries
12790         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12791
12792         // don't scroll more than one field at a time
12793         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12794
12795         // don't scroll against the player's moving direction
12796         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12797             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12798           scroll_y = old_scroll_y;
12799       }
12800     }
12801
12802     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12803     {
12804       if (!network.enabled && game.centered_player_nr == -1 &&
12805           !AllPlayersInVisibleScreen())
12806       {
12807         scroll_x = old_scroll_x;
12808         scroll_y = old_scroll_y;
12809       }
12810       else
12811       {
12812         ScrollScreen(player, SCROLL_INIT);
12813         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12814       }
12815     }
12816   }
12817
12818   player->StepFrame = 0;
12819
12820   if (moved & MP_MOVING)
12821   {
12822     if (old_jx != jx && old_jy == jy)
12823       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12824     else if (old_jx == jx && old_jy != jy)
12825       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12826
12827     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12828
12829     player->last_move_dir = player->MovDir;
12830     player->is_moving = TRUE;
12831     player->is_snapping = FALSE;
12832     player->is_switching = FALSE;
12833     player->is_dropping = FALSE;
12834     player->is_dropping_pressed = FALSE;
12835     player->drop_pressed_delay = 0;
12836
12837 #if 0
12838     // should better be called here than above, but this breaks some tapes
12839     ScrollPlayer(player, SCROLL_INIT);
12840 #endif
12841   }
12842   else
12843   {
12844     CheckGravityMovementWhenNotMoving(player);
12845
12846     player->is_moving = FALSE;
12847
12848     /* at this point, the player is allowed to move, but cannot move right now
12849        (e.g. because of something blocking the way) -- ensure that the player
12850        is also allowed to move in the next frame (in old versions before 3.1.1,
12851        the player was forced to wait again for eight frames before next try) */
12852
12853     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12854       player->move_delay = 0;   // allow direct movement in the next frame
12855   }
12856
12857   if (player->move_delay == -1)         // not yet initialized by DigField()
12858     player->move_delay = player->move_delay_value;
12859
12860   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12861   {
12862     TestIfPlayerTouchesBadThing(jx, jy);
12863     TestIfPlayerTouchesCustomElement(jx, jy);
12864   }
12865
12866   if (!player->active)
12867     RemovePlayer(player);
12868
12869   return moved;
12870 }
12871
12872 void ScrollPlayer(struct PlayerInfo *player, int mode)
12873 {
12874   int jx = player->jx, jy = player->jy;
12875   int last_jx = player->last_jx, last_jy = player->last_jy;
12876   int move_stepsize = TILEX / player->move_delay_value;
12877
12878   if (!player->active)
12879     return;
12880
12881   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12882     return;
12883
12884   if (mode == SCROLL_INIT)
12885   {
12886     player->actual_frame_counter = FrameCounter;
12887     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12888
12889     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12890         Tile[last_jx][last_jy] == EL_EMPTY)
12891     {
12892       int last_field_block_delay = 0;   // start with no blocking at all
12893       int block_delay_adjustment = player->block_delay_adjustment;
12894
12895       // if player blocks last field, add delay for exactly one move
12896       if (player->block_last_field)
12897       {
12898         last_field_block_delay += player->move_delay_value;
12899
12900         // when blocking enabled, prevent moving up despite gravity
12901         if (player->gravity && player->MovDir == MV_UP)
12902           block_delay_adjustment = -1;
12903       }
12904
12905       // add block delay adjustment (also possible when not blocking)
12906       last_field_block_delay += block_delay_adjustment;
12907
12908       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12909       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12910     }
12911
12912     if (player->MovPos != 0)    // player has not yet reached destination
12913       return;
12914   }
12915   else if (!FrameReached(&player->actual_frame_counter, 1))
12916     return;
12917
12918   if (player->MovPos != 0)
12919   {
12920     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12921     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12922
12923     // before DrawPlayer() to draw correct player graphic for this case
12924     if (player->MovPos == 0)
12925       CheckGravityMovement(player);
12926   }
12927
12928   if (player->MovPos == 0)      // player reached destination field
12929   {
12930     if (player->move_delay_reset_counter > 0)
12931     {
12932       player->move_delay_reset_counter--;
12933
12934       if (player->move_delay_reset_counter == 0)
12935       {
12936         // continue with normal speed after quickly moving through gate
12937         HALVE_PLAYER_SPEED(player);
12938
12939         // be able to make the next move without delay
12940         player->move_delay = 0;
12941       }
12942     }
12943
12944     player->last_jx = jx;
12945     player->last_jy = jy;
12946
12947     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12948         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12949         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12950         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12951         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12952         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12953         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12954         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12955     {
12956       ExitPlayer(player);
12957
12958       if (game.players_still_needed == 0 &&
12959           (game.friends_still_needed == 0 ||
12960            IS_SP_ELEMENT(Tile[jx][jy])))
12961         LevelSolved();
12962     }
12963
12964     // this breaks one level: "machine", level 000
12965     {
12966       int move_direction = player->MovDir;
12967       int enter_side = MV_DIR_OPPOSITE(move_direction);
12968       int leave_side = move_direction;
12969       int old_jx = last_jx;
12970       int old_jy = last_jy;
12971       int old_element = Tile[old_jx][old_jy];
12972       int new_element = Tile[jx][jy];
12973
12974       if (IS_CUSTOM_ELEMENT(old_element))
12975         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12976                                    CE_LEFT_BY_PLAYER,
12977                                    player->index_bit, leave_side);
12978
12979       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12980                                           CE_PLAYER_LEAVES_X,
12981                                           player->index_bit, leave_side);
12982
12983       if (IS_CUSTOM_ELEMENT(new_element))
12984         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12985                                    player->index_bit, enter_side);
12986
12987       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12988                                           CE_PLAYER_ENTERS_X,
12989                                           player->index_bit, enter_side);
12990
12991       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12992                                         CE_MOVE_OF_X, move_direction);
12993     }
12994
12995     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12996     {
12997       TestIfPlayerTouchesBadThing(jx, jy);
12998       TestIfPlayerTouchesCustomElement(jx, jy);
12999
13000       /* needed because pushed element has not yet reached its destination,
13001          so it would trigger a change event at its previous field location */
13002       if (!player->is_pushing)
13003         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13004
13005       if (!player->active)
13006         RemovePlayer(player);
13007     }
13008
13009     if (!game.LevelSolved && level.use_step_counter)
13010     {
13011       int i;
13012
13013       TimePlayed++;
13014
13015       if (TimeLeft > 0)
13016       {
13017         TimeLeft--;
13018
13019         if (TimeLeft <= 10 && setup.time_limit)
13020           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13021
13022         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13023
13024         DisplayGameControlValues();
13025
13026         if (!TimeLeft && setup.time_limit)
13027           for (i = 0; i < MAX_PLAYERS; i++)
13028             KillPlayer(&stored_player[i]);
13029       }
13030       else if (game.no_time_limit && !game.all_players_gone)
13031       {
13032         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13033
13034         DisplayGameControlValues();
13035       }
13036     }
13037
13038     if (tape.single_step && tape.recording && !tape.pausing &&
13039         !player->programmed_action)
13040       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13041
13042     if (!player->programmed_action)
13043       CheckSaveEngineSnapshot(player);
13044   }
13045 }
13046
13047 void ScrollScreen(struct PlayerInfo *player, int mode)
13048 {
13049   static unsigned int screen_frame_counter = 0;
13050
13051   if (mode == SCROLL_INIT)
13052   {
13053     // set scrolling step size according to actual player's moving speed
13054     ScrollStepSize = TILEX / player->move_delay_value;
13055
13056     screen_frame_counter = FrameCounter;
13057     ScreenMovDir = player->MovDir;
13058     ScreenMovPos = player->MovPos;
13059     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13060     return;
13061   }
13062   else if (!FrameReached(&screen_frame_counter, 1))
13063     return;
13064
13065   if (ScreenMovPos)
13066   {
13067     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13068     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13069     redraw_mask |= REDRAW_FIELD;
13070   }
13071   else
13072     ScreenMovDir = MV_NONE;
13073 }
13074
13075 void TestIfPlayerTouchesCustomElement(int x, int y)
13076 {
13077   static int xy[4][2] =
13078   {
13079     { 0, -1 },
13080     { -1, 0 },
13081     { +1, 0 },
13082     { 0, +1 }
13083   };
13084   static int trigger_sides[4][2] =
13085   {
13086     // center side       border side
13087     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13088     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13089     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13090     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13091   };
13092   static int touch_dir[4] =
13093   {
13094     MV_LEFT | MV_RIGHT,
13095     MV_UP   | MV_DOWN,
13096     MV_UP   | MV_DOWN,
13097     MV_LEFT | MV_RIGHT
13098   };
13099   int center_element = Tile[x][y];      // should always be non-moving!
13100   int i;
13101
13102   for (i = 0; i < NUM_DIRECTIONS; i++)
13103   {
13104     int xx = x + xy[i][0];
13105     int yy = y + xy[i][1];
13106     int center_side = trigger_sides[i][0];
13107     int border_side = trigger_sides[i][1];
13108     int border_element;
13109
13110     if (!IN_LEV_FIELD(xx, yy))
13111       continue;
13112
13113     if (IS_PLAYER(x, y))                // player found at center element
13114     {
13115       struct PlayerInfo *player = PLAYERINFO(x, y);
13116
13117       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13118         border_element = Tile[xx][yy];          // may be moving!
13119       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13120         border_element = Tile[xx][yy];
13121       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13122         border_element = MovingOrBlocked2Element(xx, yy);
13123       else
13124         continue;               // center and border element do not touch
13125
13126       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13127                                  player->index_bit, border_side);
13128       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13129                                           CE_PLAYER_TOUCHES_X,
13130                                           player->index_bit, border_side);
13131
13132       {
13133         /* use player element that is initially defined in the level playfield,
13134            not the player element that corresponds to the runtime player number
13135            (example: a level that contains EL_PLAYER_3 as the only player would
13136            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13137         int player_element = PLAYERINFO(x, y)->initial_element;
13138
13139         CheckElementChangeBySide(xx, yy, border_element, player_element,
13140                                  CE_TOUCHING_X, border_side);
13141       }
13142     }
13143     else if (IS_PLAYER(xx, yy))         // player found at border element
13144     {
13145       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13146
13147       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13148       {
13149         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13150           continue;             // center and border element do not touch
13151       }
13152
13153       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13154                                  player->index_bit, center_side);
13155       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13156                                           CE_PLAYER_TOUCHES_X,
13157                                           player->index_bit, center_side);
13158
13159       {
13160         /* use player element that is initially defined in the level playfield,
13161            not the player element that corresponds to the runtime player number
13162            (example: a level that contains EL_PLAYER_3 as the only player would
13163            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13164         int player_element = PLAYERINFO(xx, yy)->initial_element;
13165
13166         CheckElementChangeBySide(x, y, center_element, player_element,
13167                                  CE_TOUCHING_X, center_side);
13168       }
13169
13170       break;
13171     }
13172   }
13173 }
13174
13175 void TestIfElementTouchesCustomElement(int x, int y)
13176 {
13177   static int xy[4][2] =
13178   {
13179     { 0, -1 },
13180     { -1, 0 },
13181     { +1, 0 },
13182     { 0, +1 }
13183   };
13184   static int trigger_sides[4][2] =
13185   {
13186     // center side      border side
13187     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13188     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13189     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13190     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13191   };
13192   static int touch_dir[4] =
13193   {
13194     MV_LEFT | MV_RIGHT,
13195     MV_UP   | MV_DOWN,
13196     MV_UP   | MV_DOWN,
13197     MV_LEFT | MV_RIGHT
13198   };
13199   boolean change_center_element = FALSE;
13200   int center_element = Tile[x][y];      // should always be non-moving!
13201   int border_element_old[NUM_DIRECTIONS];
13202   int i;
13203
13204   for (i = 0; i < NUM_DIRECTIONS; i++)
13205   {
13206     int xx = x + xy[i][0];
13207     int yy = y + xy[i][1];
13208     int border_element;
13209
13210     border_element_old[i] = -1;
13211
13212     if (!IN_LEV_FIELD(xx, yy))
13213       continue;
13214
13215     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13216       border_element = Tile[xx][yy];    // may be moving!
13217     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13218       border_element = Tile[xx][yy];
13219     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13220       border_element = MovingOrBlocked2Element(xx, yy);
13221     else
13222       continue;                 // center and border element do not touch
13223
13224     border_element_old[i] = border_element;
13225   }
13226
13227   for (i = 0; i < NUM_DIRECTIONS; i++)
13228   {
13229     int xx = x + xy[i][0];
13230     int yy = y + xy[i][1];
13231     int center_side = trigger_sides[i][0];
13232     int border_element = border_element_old[i];
13233
13234     if (border_element == -1)
13235       continue;
13236
13237     // check for change of border element
13238     CheckElementChangeBySide(xx, yy, border_element, center_element,
13239                              CE_TOUCHING_X, center_side);
13240
13241     // (center element cannot be player, so we dont have to check this here)
13242   }
13243
13244   for (i = 0; i < NUM_DIRECTIONS; i++)
13245   {
13246     int xx = x + xy[i][0];
13247     int yy = y + xy[i][1];
13248     int border_side = trigger_sides[i][1];
13249     int border_element = border_element_old[i];
13250
13251     if (border_element == -1)
13252       continue;
13253
13254     // check for change of center element (but change it only once)
13255     if (!change_center_element)
13256       change_center_element =
13257         CheckElementChangeBySide(x, y, center_element, border_element,
13258                                  CE_TOUCHING_X, border_side);
13259
13260     if (IS_PLAYER(xx, yy))
13261     {
13262       /* use player element that is initially defined in the level playfield,
13263          not the player element that corresponds to the runtime player number
13264          (example: a level that contains EL_PLAYER_3 as the only player would
13265          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13266       int player_element = PLAYERINFO(xx, yy)->initial_element;
13267
13268       CheckElementChangeBySide(x, y, center_element, player_element,
13269                                CE_TOUCHING_X, border_side);
13270     }
13271   }
13272 }
13273
13274 void TestIfElementHitsCustomElement(int x, int y, int direction)
13275 {
13276   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13277   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13278   int hitx = x + dx, hity = y + dy;
13279   int hitting_element = Tile[x][y];
13280   int touched_element;
13281
13282   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13283     return;
13284
13285   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13286                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13287
13288   if (IN_LEV_FIELD(hitx, hity))
13289   {
13290     int opposite_direction = MV_DIR_OPPOSITE(direction);
13291     int hitting_side = direction;
13292     int touched_side = opposite_direction;
13293     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13294                           MovDir[hitx][hity] != direction ||
13295                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13296
13297     object_hit = TRUE;
13298
13299     if (object_hit)
13300     {
13301       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13302                                CE_HITTING_X, touched_side);
13303
13304       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13305                                CE_HIT_BY_X, hitting_side);
13306
13307       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13308                                CE_HIT_BY_SOMETHING, opposite_direction);
13309
13310       if (IS_PLAYER(hitx, hity))
13311       {
13312         /* use player element that is initially defined in the level playfield,
13313            not the player element that corresponds to the runtime player number
13314            (example: a level that contains EL_PLAYER_3 as the only player would
13315            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13316         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13317
13318         CheckElementChangeBySide(x, y, hitting_element, player_element,
13319                                  CE_HITTING_X, touched_side);
13320       }
13321     }
13322   }
13323
13324   // "hitting something" is also true when hitting the playfield border
13325   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13326                            CE_HITTING_SOMETHING, direction);
13327 }
13328
13329 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13330 {
13331   int i, kill_x = -1, kill_y = -1;
13332
13333   int bad_element = -1;
13334   static int test_xy[4][2] =
13335   {
13336     { 0, -1 },
13337     { -1, 0 },
13338     { +1, 0 },
13339     { 0, +1 }
13340   };
13341   static int test_dir[4] =
13342   {
13343     MV_UP,
13344     MV_LEFT,
13345     MV_RIGHT,
13346     MV_DOWN
13347   };
13348
13349   for (i = 0; i < NUM_DIRECTIONS; i++)
13350   {
13351     int test_x, test_y, test_move_dir, test_element;
13352
13353     test_x = good_x + test_xy[i][0];
13354     test_y = good_y + test_xy[i][1];
13355
13356     if (!IN_LEV_FIELD(test_x, test_y))
13357       continue;
13358
13359     test_move_dir =
13360       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13361
13362     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13363
13364     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13365        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13366     */
13367     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13368         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13369     {
13370       kill_x = test_x;
13371       kill_y = test_y;
13372       bad_element = test_element;
13373
13374       break;
13375     }
13376   }
13377
13378   if (kill_x != -1 || kill_y != -1)
13379   {
13380     if (IS_PLAYER(good_x, good_y))
13381     {
13382       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13383
13384       if (player->shield_deadly_time_left > 0 &&
13385           !IS_INDESTRUCTIBLE(bad_element))
13386         Bang(kill_x, kill_y);
13387       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13388         KillPlayer(player);
13389     }
13390     else
13391       Bang(good_x, good_y);
13392   }
13393 }
13394
13395 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13396 {
13397   int i, kill_x = -1, kill_y = -1;
13398   int bad_element = Tile[bad_x][bad_y];
13399   static int test_xy[4][2] =
13400   {
13401     { 0, -1 },
13402     { -1, 0 },
13403     { +1, 0 },
13404     { 0, +1 }
13405   };
13406   static int touch_dir[4] =
13407   {
13408     MV_LEFT | MV_RIGHT,
13409     MV_UP   | MV_DOWN,
13410     MV_UP   | MV_DOWN,
13411     MV_LEFT | MV_RIGHT
13412   };
13413   static int test_dir[4] =
13414   {
13415     MV_UP,
13416     MV_LEFT,
13417     MV_RIGHT,
13418     MV_DOWN
13419   };
13420
13421   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13422     return;
13423
13424   for (i = 0; i < NUM_DIRECTIONS; i++)
13425   {
13426     int test_x, test_y, test_move_dir, test_element;
13427
13428     test_x = bad_x + test_xy[i][0];
13429     test_y = bad_y + test_xy[i][1];
13430
13431     if (!IN_LEV_FIELD(test_x, test_y))
13432       continue;
13433
13434     test_move_dir =
13435       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13436
13437     test_element = Tile[test_x][test_y];
13438
13439     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13440        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13441     */
13442     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13443         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13444     {
13445       // good thing is player or penguin that does not move away
13446       if (IS_PLAYER(test_x, test_y))
13447       {
13448         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13449
13450         if (bad_element == EL_ROBOT && player->is_moving)
13451           continue;     // robot does not kill player if he is moving
13452
13453         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13454         {
13455           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13456             continue;           // center and border element do not touch
13457         }
13458
13459         kill_x = test_x;
13460         kill_y = test_y;
13461
13462         break;
13463       }
13464       else if (test_element == EL_PENGUIN)
13465       {
13466         kill_x = test_x;
13467         kill_y = test_y;
13468
13469         break;
13470       }
13471     }
13472   }
13473
13474   if (kill_x != -1 || kill_y != -1)
13475   {
13476     if (IS_PLAYER(kill_x, kill_y))
13477     {
13478       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13479
13480       if (player->shield_deadly_time_left > 0 &&
13481           !IS_INDESTRUCTIBLE(bad_element))
13482         Bang(bad_x, bad_y);
13483       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13484         KillPlayer(player);
13485     }
13486     else
13487       Bang(kill_x, kill_y);
13488   }
13489 }
13490
13491 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13492 {
13493   int bad_element = Tile[bad_x][bad_y];
13494   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13495   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13496   int test_x = bad_x + dx, test_y = bad_y + dy;
13497   int test_move_dir, test_element;
13498   int kill_x = -1, kill_y = -1;
13499
13500   if (!IN_LEV_FIELD(test_x, test_y))
13501     return;
13502
13503   test_move_dir =
13504     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13505
13506   test_element = Tile[test_x][test_y];
13507
13508   if (test_move_dir != bad_move_dir)
13509   {
13510     // good thing can be player or penguin that does not move away
13511     if (IS_PLAYER(test_x, test_y))
13512     {
13513       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13514
13515       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13516          player as being hit when he is moving towards the bad thing, because
13517          the "get hit by" condition would be lost after the player stops) */
13518       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13519         return;         // player moves away from bad thing
13520
13521       kill_x = test_x;
13522       kill_y = test_y;
13523     }
13524     else if (test_element == EL_PENGUIN)
13525     {
13526       kill_x = test_x;
13527       kill_y = test_y;
13528     }
13529   }
13530
13531   if (kill_x != -1 || kill_y != -1)
13532   {
13533     if (IS_PLAYER(kill_x, kill_y))
13534     {
13535       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13536
13537       if (player->shield_deadly_time_left > 0 &&
13538           !IS_INDESTRUCTIBLE(bad_element))
13539         Bang(bad_x, bad_y);
13540       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13541         KillPlayer(player);
13542     }
13543     else
13544       Bang(kill_x, kill_y);
13545   }
13546 }
13547
13548 void TestIfPlayerTouchesBadThing(int x, int y)
13549 {
13550   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13551 }
13552
13553 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13554 {
13555   TestIfGoodThingHitsBadThing(x, y, move_dir);
13556 }
13557
13558 void TestIfBadThingTouchesPlayer(int x, int y)
13559 {
13560   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13561 }
13562
13563 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13564 {
13565   TestIfBadThingHitsGoodThing(x, y, move_dir);
13566 }
13567
13568 void TestIfFriendTouchesBadThing(int x, int y)
13569 {
13570   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13571 }
13572
13573 void TestIfBadThingTouchesFriend(int x, int y)
13574 {
13575   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13576 }
13577
13578 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13579 {
13580   int i, kill_x = bad_x, kill_y = bad_y;
13581   static int xy[4][2] =
13582   {
13583     { 0, -1 },
13584     { -1, 0 },
13585     { +1, 0 },
13586     { 0, +1 }
13587   };
13588
13589   for (i = 0; i < NUM_DIRECTIONS; i++)
13590   {
13591     int x, y, element;
13592
13593     x = bad_x + xy[i][0];
13594     y = bad_y + xy[i][1];
13595     if (!IN_LEV_FIELD(x, y))
13596       continue;
13597
13598     element = Tile[x][y];
13599     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13600         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13601     {
13602       kill_x = x;
13603       kill_y = y;
13604       break;
13605     }
13606   }
13607
13608   if (kill_x != bad_x || kill_y != bad_y)
13609     Bang(bad_x, bad_y);
13610 }
13611
13612 void KillPlayer(struct PlayerInfo *player)
13613 {
13614   int jx = player->jx, jy = player->jy;
13615
13616   if (!player->active)
13617     return;
13618
13619 #if 0
13620   Debug("game:playing:KillPlayer",
13621         "0: killed == %d, active == %d, reanimated == %d",
13622         player->killed, player->active, player->reanimated);
13623 #endif
13624
13625   /* the following code was introduced to prevent an infinite loop when calling
13626      -> Bang()
13627      -> CheckTriggeredElementChangeExt()
13628      -> ExecuteCustomElementAction()
13629      -> KillPlayer()
13630      -> (infinitely repeating the above sequence of function calls)
13631      which occurs when killing the player while having a CE with the setting
13632      "kill player X when explosion of <player X>"; the solution using a new
13633      field "player->killed" was chosen for backwards compatibility, although
13634      clever use of the fields "player->active" etc. would probably also work */
13635 #if 1
13636   if (player->killed)
13637     return;
13638 #endif
13639
13640   player->killed = TRUE;
13641
13642   // remove accessible field at the player's position
13643   Tile[jx][jy] = EL_EMPTY;
13644
13645   // deactivate shield (else Bang()/Explode() would not work right)
13646   player->shield_normal_time_left = 0;
13647   player->shield_deadly_time_left = 0;
13648
13649 #if 0
13650   Debug("game:playing:KillPlayer",
13651         "1: killed == %d, active == %d, reanimated == %d",
13652         player->killed, player->active, player->reanimated);
13653 #endif
13654
13655   Bang(jx, jy);
13656
13657 #if 0
13658   Debug("game:playing:KillPlayer",
13659         "2: killed == %d, active == %d, reanimated == %d",
13660         player->killed, player->active, player->reanimated);
13661 #endif
13662
13663   if (player->reanimated)       // killed player may have been reanimated
13664     player->killed = player->reanimated = FALSE;
13665   else
13666     BuryPlayer(player);
13667 }
13668
13669 static void KillPlayerUnlessEnemyProtected(int x, int y)
13670 {
13671   if (!PLAYER_ENEMY_PROTECTED(x, y))
13672     KillPlayer(PLAYERINFO(x, y));
13673 }
13674
13675 static void KillPlayerUnlessExplosionProtected(int x, int y)
13676 {
13677   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13678     KillPlayer(PLAYERINFO(x, y));
13679 }
13680
13681 void BuryPlayer(struct PlayerInfo *player)
13682 {
13683   int jx = player->jx, jy = player->jy;
13684
13685   if (!player->active)
13686     return;
13687
13688   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13689   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13690
13691   RemovePlayer(player);
13692
13693   player->buried = TRUE;
13694
13695   if (game.all_players_gone)
13696     game.GameOver = TRUE;
13697 }
13698
13699 void RemovePlayer(struct PlayerInfo *player)
13700 {
13701   int jx = player->jx, jy = player->jy;
13702   int i, found = FALSE;
13703
13704   player->present = FALSE;
13705   player->active = FALSE;
13706
13707   // required for some CE actions (even if the player is not active anymore)
13708   player->MovPos = 0;
13709
13710   if (!ExplodeField[jx][jy])
13711     StorePlayer[jx][jy] = 0;
13712
13713   if (player->is_moving)
13714     TEST_DrawLevelField(player->last_jx, player->last_jy);
13715
13716   for (i = 0; i < MAX_PLAYERS; i++)
13717     if (stored_player[i].active)
13718       found = TRUE;
13719
13720   if (!found)
13721   {
13722     game.all_players_gone = TRUE;
13723     game.GameOver = TRUE;
13724   }
13725
13726   game.exit_x = game.robot_wheel_x = jx;
13727   game.exit_y = game.robot_wheel_y = jy;
13728 }
13729
13730 void ExitPlayer(struct PlayerInfo *player)
13731 {
13732   DrawPlayer(player);   // needed here only to cleanup last field
13733   RemovePlayer(player);
13734
13735   if (game.players_still_needed > 0)
13736     game.players_still_needed--;
13737 }
13738
13739 static void setFieldForSnapping(int x, int y, int element, int direction)
13740 {
13741   struct ElementInfo *ei = &element_info[element];
13742   int direction_bit = MV_DIR_TO_BIT(direction);
13743   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13744   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13745                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13746
13747   Tile[x][y] = EL_ELEMENT_SNAPPING;
13748   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13749
13750   ResetGfxAnimation(x, y);
13751
13752   GfxElement[x][y] = element;
13753   GfxAction[x][y] = action;
13754   GfxDir[x][y] = direction;
13755   GfxFrame[x][y] = -1;
13756 }
13757
13758 /*
13759   =============================================================================
13760   checkDiagonalPushing()
13761   -----------------------------------------------------------------------------
13762   check if diagonal input device direction results in pushing of object
13763   (by checking if the alternative direction is walkable, diggable, ...)
13764   =============================================================================
13765 */
13766
13767 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13768                                     int x, int y, int real_dx, int real_dy)
13769 {
13770   int jx, jy, dx, dy, xx, yy;
13771
13772   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13773     return TRUE;
13774
13775   // diagonal direction: check alternative direction
13776   jx = player->jx;
13777   jy = player->jy;
13778   dx = x - jx;
13779   dy = y - jy;
13780   xx = jx + (dx == 0 ? real_dx : 0);
13781   yy = jy + (dy == 0 ? real_dy : 0);
13782
13783   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13784 }
13785
13786 /*
13787   =============================================================================
13788   DigField()
13789   -----------------------------------------------------------------------------
13790   x, y:                 field next to player (non-diagonal) to try to dig to
13791   real_dx, real_dy:     direction as read from input device (can be diagonal)
13792   =============================================================================
13793 */
13794
13795 static int DigField(struct PlayerInfo *player,
13796                     int oldx, int oldy, int x, int y,
13797                     int real_dx, int real_dy, int mode)
13798 {
13799   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13800   boolean player_was_pushing = player->is_pushing;
13801   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13802   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13803   int jx = oldx, jy = oldy;
13804   int dx = x - jx, dy = y - jy;
13805   int nextx = x + dx, nexty = y + dy;
13806   int move_direction = (dx == -1 ? MV_LEFT  :
13807                         dx == +1 ? MV_RIGHT :
13808                         dy == -1 ? MV_UP    :
13809                         dy == +1 ? MV_DOWN  : MV_NONE);
13810   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13811   int dig_side = MV_DIR_OPPOSITE(move_direction);
13812   int old_element = Tile[jx][jy];
13813   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13814   int collect_count;
13815
13816   if (is_player)                // function can also be called by EL_PENGUIN
13817   {
13818     if (player->MovPos == 0)
13819     {
13820       player->is_digging = FALSE;
13821       player->is_collecting = FALSE;
13822     }
13823
13824     if (player->MovPos == 0)    // last pushing move finished
13825       player->is_pushing = FALSE;
13826
13827     if (mode == DF_NO_PUSH)     // player just stopped pushing
13828     {
13829       player->is_switching = FALSE;
13830       player->push_delay = -1;
13831
13832       return MP_NO_ACTION;
13833     }
13834   }
13835
13836   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13837     old_element = Back[jx][jy];
13838
13839   // in case of element dropped at player position, check background
13840   else if (Back[jx][jy] != EL_EMPTY &&
13841            game.engine_version >= VERSION_IDENT(2,2,0,0))
13842     old_element = Back[jx][jy];
13843
13844   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13845     return MP_NO_ACTION;        // field has no opening in this direction
13846
13847   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13848     return MP_NO_ACTION;        // field has no opening in this direction
13849
13850   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13851   {
13852     SplashAcid(x, y);
13853
13854     Tile[jx][jy] = player->artwork_element;
13855     InitMovingField(jx, jy, MV_DOWN);
13856     Store[jx][jy] = EL_ACID;
13857     ContinueMoving(jx, jy);
13858     BuryPlayer(player);
13859
13860     return MP_DONT_RUN_INTO;
13861   }
13862
13863   if (player_can_move && DONT_RUN_INTO(element))
13864   {
13865     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13866
13867     return MP_DONT_RUN_INTO;
13868   }
13869
13870   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13871     return MP_NO_ACTION;
13872
13873   collect_count = element_info[element].collect_count_initial;
13874
13875   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13876     return MP_NO_ACTION;
13877
13878   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13879     player_can_move = player_can_move_or_snap;
13880
13881   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13882       game.engine_version >= VERSION_IDENT(2,2,0,0))
13883   {
13884     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13885                                player->index_bit, dig_side);
13886     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13887                                         player->index_bit, dig_side);
13888
13889     if (element == EL_DC_LANDMINE)
13890       Bang(x, y);
13891
13892     if (Tile[x][y] != element)          // field changed by snapping
13893       return MP_ACTION;
13894
13895     return MP_NO_ACTION;
13896   }
13897
13898   if (player->gravity && is_player && !player->is_auto_moving &&
13899       canFallDown(player) && move_direction != MV_DOWN &&
13900       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13901     return MP_NO_ACTION;        // player cannot walk here due to gravity
13902
13903   if (player_can_move &&
13904       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13905   {
13906     int sound_element = SND_ELEMENT(element);
13907     int sound_action = ACTION_WALKING;
13908
13909     if (IS_RND_GATE(element))
13910     {
13911       if (!player->key[RND_GATE_NR(element)])
13912         return MP_NO_ACTION;
13913     }
13914     else if (IS_RND_GATE_GRAY(element))
13915     {
13916       if (!player->key[RND_GATE_GRAY_NR(element)])
13917         return MP_NO_ACTION;
13918     }
13919     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13920     {
13921       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13922         return MP_NO_ACTION;
13923     }
13924     else if (element == EL_EXIT_OPEN ||
13925              element == EL_EM_EXIT_OPEN ||
13926              element == EL_EM_EXIT_OPENING ||
13927              element == EL_STEEL_EXIT_OPEN ||
13928              element == EL_EM_STEEL_EXIT_OPEN ||
13929              element == EL_EM_STEEL_EXIT_OPENING ||
13930              element == EL_SP_EXIT_OPEN ||
13931              element == EL_SP_EXIT_OPENING)
13932     {
13933       sound_action = ACTION_PASSING;    // player is passing exit
13934     }
13935     else if (element == EL_EMPTY)
13936     {
13937       sound_action = ACTION_MOVING;             // nothing to walk on
13938     }
13939
13940     // play sound from background or player, whatever is available
13941     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13942       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13943     else
13944       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13945   }
13946   else if (player_can_move &&
13947            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13948   {
13949     if (!ACCESS_FROM(element, opposite_direction))
13950       return MP_NO_ACTION;      // field not accessible from this direction
13951
13952     if (CAN_MOVE(element))      // only fixed elements can be passed!
13953       return MP_NO_ACTION;
13954
13955     if (IS_EM_GATE(element))
13956     {
13957       if (!player->key[EM_GATE_NR(element)])
13958         return MP_NO_ACTION;
13959     }
13960     else if (IS_EM_GATE_GRAY(element))
13961     {
13962       if (!player->key[EM_GATE_GRAY_NR(element)])
13963         return MP_NO_ACTION;
13964     }
13965     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13966     {
13967       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13968         return MP_NO_ACTION;
13969     }
13970     else if (IS_EMC_GATE(element))
13971     {
13972       if (!player->key[EMC_GATE_NR(element)])
13973         return MP_NO_ACTION;
13974     }
13975     else if (IS_EMC_GATE_GRAY(element))
13976     {
13977       if (!player->key[EMC_GATE_GRAY_NR(element)])
13978         return MP_NO_ACTION;
13979     }
13980     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13981     {
13982       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13983         return MP_NO_ACTION;
13984     }
13985     else if (element == EL_DC_GATE_WHITE ||
13986              element == EL_DC_GATE_WHITE_GRAY ||
13987              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13988     {
13989       if (player->num_white_keys == 0)
13990         return MP_NO_ACTION;
13991
13992       player->num_white_keys--;
13993     }
13994     else if (IS_SP_PORT(element))
13995     {
13996       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13997           element == EL_SP_GRAVITY_PORT_RIGHT ||
13998           element == EL_SP_GRAVITY_PORT_UP ||
13999           element == EL_SP_GRAVITY_PORT_DOWN)
14000         player->gravity = !player->gravity;
14001       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14002                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14003                element == EL_SP_GRAVITY_ON_PORT_UP ||
14004                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14005         player->gravity = TRUE;
14006       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14007                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14008                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14009                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14010         player->gravity = FALSE;
14011     }
14012
14013     // automatically move to the next field with double speed
14014     player->programmed_action = move_direction;
14015
14016     if (player->move_delay_reset_counter == 0)
14017     {
14018       player->move_delay_reset_counter = 2;     // two double speed steps
14019
14020       DOUBLE_PLAYER_SPEED(player);
14021     }
14022
14023     PlayLevelSoundAction(x, y, ACTION_PASSING);
14024   }
14025   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14026   {
14027     RemoveField(x, y);
14028
14029     if (mode != DF_SNAP)
14030     {
14031       GfxElement[x][y] = GFX_ELEMENT(element);
14032       player->is_digging = TRUE;
14033     }
14034
14035     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14036
14037     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14038                                         player->index_bit, dig_side);
14039
14040     // if digging triggered player relocation, finish digging tile
14041     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14042       setFieldForSnapping(x, y, element, move_direction);
14043
14044     if (mode == DF_SNAP)
14045     {
14046       if (level.block_snap_field)
14047         setFieldForSnapping(x, y, element, move_direction);
14048       else
14049         TestIfElementTouchesCustomElement(x, y);        // for empty space
14050
14051       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14052                                           player->index_bit, dig_side);
14053     }
14054   }
14055   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14056   {
14057     RemoveField(x, y);
14058
14059     if (is_player && mode != DF_SNAP)
14060     {
14061       GfxElement[x][y] = element;
14062       player->is_collecting = TRUE;
14063     }
14064
14065     if (element == EL_SPEED_PILL)
14066     {
14067       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14068     }
14069     else if (element == EL_EXTRA_TIME && level.time > 0)
14070     {
14071       TimeLeft += level.extra_time;
14072
14073       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14074
14075       DisplayGameControlValues();
14076     }
14077     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14078     {
14079       player->shield_normal_time_left += level.shield_normal_time;
14080       if (element == EL_SHIELD_DEADLY)
14081         player->shield_deadly_time_left += level.shield_deadly_time;
14082     }
14083     else if (element == EL_DYNAMITE ||
14084              element == EL_EM_DYNAMITE ||
14085              element == EL_SP_DISK_RED)
14086     {
14087       if (player->inventory_size < MAX_INVENTORY_SIZE)
14088         player->inventory_element[player->inventory_size++] = element;
14089
14090       DrawGameDoorValues();
14091     }
14092     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14093     {
14094       player->dynabomb_count++;
14095       player->dynabombs_left++;
14096     }
14097     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14098     {
14099       player->dynabomb_size++;
14100     }
14101     else if (element == EL_DYNABOMB_INCREASE_POWER)
14102     {
14103       player->dynabomb_xl = TRUE;
14104     }
14105     else if (IS_KEY(element))
14106     {
14107       player->key[KEY_NR(element)] = TRUE;
14108
14109       DrawGameDoorValues();
14110     }
14111     else if (element == EL_DC_KEY_WHITE)
14112     {
14113       player->num_white_keys++;
14114
14115       // display white keys?
14116       // DrawGameDoorValues();
14117     }
14118     else if (IS_ENVELOPE(element))
14119     {
14120       player->show_envelope = element;
14121     }
14122     else if (element == EL_EMC_LENSES)
14123     {
14124       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14125
14126       RedrawAllInvisibleElementsForLenses();
14127     }
14128     else if (element == EL_EMC_MAGNIFIER)
14129     {
14130       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14131
14132       RedrawAllInvisibleElementsForMagnifier();
14133     }
14134     else if (IS_DROPPABLE(element) ||
14135              IS_THROWABLE(element))     // can be collected and dropped
14136     {
14137       int i;
14138
14139       if (collect_count == 0)
14140         player->inventory_infinite_element = element;
14141       else
14142         for (i = 0; i < collect_count; i++)
14143           if (player->inventory_size < MAX_INVENTORY_SIZE)
14144             player->inventory_element[player->inventory_size++] = element;
14145
14146       DrawGameDoorValues();
14147     }
14148     else if (collect_count > 0)
14149     {
14150       game.gems_still_needed -= collect_count;
14151       if (game.gems_still_needed < 0)
14152         game.gems_still_needed = 0;
14153
14154       game.snapshot.collected_item = TRUE;
14155
14156       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14157
14158       DisplayGameControlValues();
14159     }
14160
14161     RaiseScoreElement(element);
14162     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14163
14164     if (is_player)
14165     {
14166       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14167                                           player->index_bit, dig_side);
14168
14169       // if collecting triggered player relocation, finish collecting tile
14170       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14171         setFieldForSnapping(x, y, element, move_direction);
14172     }
14173
14174     if (mode == DF_SNAP)
14175     {
14176       if (level.block_snap_field)
14177         setFieldForSnapping(x, y, element, move_direction);
14178       else
14179         TestIfElementTouchesCustomElement(x, y);        // for empty space
14180
14181       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14182                                           player->index_bit, dig_side);
14183     }
14184   }
14185   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14186   {
14187     if (mode == DF_SNAP && element != EL_BD_ROCK)
14188       return MP_NO_ACTION;
14189
14190     if (CAN_FALL(element) && dy)
14191       return MP_NO_ACTION;
14192
14193     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14194         !(element == EL_SPRING && level.use_spring_bug))
14195       return MP_NO_ACTION;
14196
14197     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14198         ((move_direction & MV_VERTICAL &&
14199           ((element_info[element].move_pattern & MV_LEFT &&
14200             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14201            (element_info[element].move_pattern & MV_RIGHT &&
14202             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14203          (move_direction & MV_HORIZONTAL &&
14204           ((element_info[element].move_pattern & MV_UP &&
14205             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14206            (element_info[element].move_pattern & MV_DOWN &&
14207             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14208       return MP_NO_ACTION;
14209
14210     // do not push elements already moving away faster than player
14211     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14212         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14213       return MP_NO_ACTION;
14214
14215     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14216     {
14217       if (player->push_delay_value == -1 || !player_was_pushing)
14218         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14219     }
14220     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14221     {
14222       if (player->push_delay_value == -1)
14223         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14224     }
14225     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14226     {
14227       if (!player->is_pushing)
14228         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14229     }
14230
14231     player->is_pushing = TRUE;
14232     player->is_active = TRUE;
14233
14234     if (!(IN_LEV_FIELD(nextx, nexty) &&
14235           (IS_FREE(nextx, nexty) ||
14236            (IS_SB_ELEMENT(element) &&
14237             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14238            (IS_CUSTOM_ELEMENT(element) &&
14239             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14240       return MP_NO_ACTION;
14241
14242     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14243       return MP_NO_ACTION;
14244
14245     if (player->push_delay == -1)       // new pushing; restart delay
14246       player->push_delay = 0;
14247
14248     if (player->push_delay < player->push_delay_value &&
14249         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14250         element != EL_SPRING && element != EL_BALLOON)
14251     {
14252       // make sure that there is no move delay before next try to push
14253       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14254         player->move_delay = 0;
14255
14256       return MP_NO_ACTION;
14257     }
14258
14259     if (IS_CUSTOM_ELEMENT(element) &&
14260         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14261     {
14262       if (!DigFieldByCE(nextx, nexty, element))
14263         return MP_NO_ACTION;
14264     }
14265
14266     if (IS_SB_ELEMENT(element))
14267     {
14268       boolean sokoban_task_solved = FALSE;
14269
14270       if (element == EL_SOKOBAN_FIELD_FULL)
14271       {
14272         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14273
14274         IncrementSokobanFieldsNeeded();
14275         IncrementSokobanObjectsNeeded();
14276       }
14277
14278       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14279       {
14280         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14281
14282         DecrementSokobanFieldsNeeded();
14283         DecrementSokobanObjectsNeeded();
14284
14285         // sokoban object was pushed from empty field to sokoban field
14286         if (Back[x][y] == EL_EMPTY)
14287           sokoban_task_solved = TRUE;
14288       }
14289
14290       Tile[x][y] = EL_SOKOBAN_OBJECT;
14291
14292       if (Back[x][y] == Back[nextx][nexty])
14293         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14294       else if (Back[x][y] != 0)
14295         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14296                                     ACTION_EMPTYING);
14297       else
14298         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14299                                     ACTION_FILLING);
14300
14301       if (sokoban_task_solved &&
14302           game.sokoban_fields_still_needed == 0 &&
14303           game.sokoban_objects_still_needed == 0 &&
14304           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14305       {
14306         game.players_still_needed = 0;
14307
14308         LevelSolved();
14309
14310         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14311       }
14312     }
14313     else
14314       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14315
14316     InitMovingField(x, y, move_direction);
14317     GfxAction[x][y] = ACTION_PUSHING;
14318
14319     if (mode == DF_SNAP)
14320       ContinueMoving(x, y);
14321     else
14322       MovPos[x][y] = (dx != 0 ? dx : dy);
14323
14324     Pushed[x][y] = TRUE;
14325     Pushed[nextx][nexty] = TRUE;
14326
14327     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14328       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14329     else
14330       player->push_delay_value = -1;    // get new value later
14331
14332     // check for element change _after_ element has been pushed
14333     if (game.use_change_when_pushing_bug)
14334     {
14335       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14336                                  player->index_bit, dig_side);
14337       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14338                                           player->index_bit, dig_side);
14339     }
14340   }
14341   else if (IS_SWITCHABLE(element))
14342   {
14343     if (PLAYER_SWITCHING(player, x, y))
14344     {
14345       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14346                                           player->index_bit, dig_side);
14347
14348       return MP_ACTION;
14349     }
14350
14351     player->is_switching = TRUE;
14352     player->switch_x = x;
14353     player->switch_y = y;
14354
14355     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14356
14357     if (element == EL_ROBOT_WHEEL)
14358     {
14359       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14360
14361       game.robot_wheel_x = x;
14362       game.robot_wheel_y = y;
14363       game.robot_wheel_active = TRUE;
14364
14365       TEST_DrawLevelField(x, y);
14366     }
14367     else if (element == EL_SP_TERMINAL)
14368     {
14369       int xx, yy;
14370
14371       SCAN_PLAYFIELD(xx, yy)
14372       {
14373         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14374         {
14375           Bang(xx, yy);
14376         }
14377         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14378         {
14379           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14380
14381           ResetGfxAnimation(xx, yy);
14382           TEST_DrawLevelField(xx, yy);
14383         }
14384       }
14385     }
14386     else if (IS_BELT_SWITCH(element))
14387     {
14388       ToggleBeltSwitch(x, y);
14389     }
14390     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14391              element == EL_SWITCHGATE_SWITCH_DOWN ||
14392              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14393              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14394     {
14395       ToggleSwitchgateSwitch(x, y);
14396     }
14397     else if (element == EL_LIGHT_SWITCH ||
14398              element == EL_LIGHT_SWITCH_ACTIVE)
14399     {
14400       ToggleLightSwitch(x, y);
14401     }
14402     else if (element == EL_TIMEGATE_SWITCH ||
14403              element == EL_DC_TIMEGATE_SWITCH)
14404     {
14405       ActivateTimegateSwitch(x, y);
14406     }
14407     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14408              element == EL_BALLOON_SWITCH_RIGHT ||
14409              element == EL_BALLOON_SWITCH_UP    ||
14410              element == EL_BALLOON_SWITCH_DOWN  ||
14411              element == EL_BALLOON_SWITCH_NONE  ||
14412              element == EL_BALLOON_SWITCH_ANY)
14413     {
14414       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14415                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14416                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14417                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14418                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14419                              move_direction);
14420     }
14421     else if (element == EL_LAMP)
14422     {
14423       Tile[x][y] = EL_LAMP_ACTIVE;
14424       game.lights_still_needed--;
14425
14426       ResetGfxAnimation(x, y);
14427       TEST_DrawLevelField(x, y);
14428     }
14429     else if (element == EL_TIME_ORB_FULL)
14430     {
14431       Tile[x][y] = EL_TIME_ORB_EMPTY;
14432
14433       if (level.time > 0 || level.use_time_orb_bug)
14434       {
14435         TimeLeft += level.time_orb_time;
14436         game.no_time_limit = FALSE;
14437
14438         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14439
14440         DisplayGameControlValues();
14441       }
14442
14443       ResetGfxAnimation(x, y);
14444       TEST_DrawLevelField(x, y);
14445     }
14446     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14447              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14448     {
14449       int xx, yy;
14450
14451       game.ball_active = !game.ball_active;
14452
14453       SCAN_PLAYFIELD(xx, yy)
14454       {
14455         int e = Tile[xx][yy];
14456
14457         if (game.ball_active)
14458         {
14459           if (e == EL_EMC_MAGIC_BALL)
14460             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14461           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14462             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14463         }
14464         else
14465         {
14466           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14467             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14468           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14469             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14470         }
14471       }
14472     }
14473
14474     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14475                                         player->index_bit, dig_side);
14476
14477     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14478                                         player->index_bit, dig_side);
14479
14480     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14481                                         player->index_bit, dig_side);
14482
14483     return MP_ACTION;
14484   }
14485   else
14486   {
14487     if (!PLAYER_SWITCHING(player, x, y))
14488     {
14489       player->is_switching = TRUE;
14490       player->switch_x = x;
14491       player->switch_y = y;
14492
14493       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14494                                  player->index_bit, dig_side);
14495       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14496                                           player->index_bit, dig_side);
14497
14498       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14499                                  player->index_bit, dig_side);
14500       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14501                                           player->index_bit, dig_side);
14502     }
14503
14504     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14505                                player->index_bit, dig_side);
14506     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14507                                         player->index_bit, dig_side);
14508
14509     return MP_NO_ACTION;
14510   }
14511
14512   player->push_delay = -1;
14513
14514   if (is_player)                // function can also be called by EL_PENGUIN
14515   {
14516     if (Tile[x][y] != element)          // really digged/collected something
14517     {
14518       player->is_collecting = !player->is_digging;
14519       player->is_active = TRUE;
14520     }
14521   }
14522
14523   return MP_MOVING;
14524 }
14525
14526 static boolean DigFieldByCE(int x, int y, int digging_element)
14527 {
14528   int element = Tile[x][y];
14529
14530   if (!IS_FREE(x, y))
14531   {
14532     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14533                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14534                   ACTION_BREAKING);
14535
14536     // no element can dig solid indestructible elements
14537     if (IS_INDESTRUCTIBLE(element) &&
14538         !IS_DIGGABLE(element) &&
14539         !IS_COLLECTIBLE(element))
14540       return FALSE;
14541
14542     if (AmoebaNr[x][y] &&
14543         (element == EL_AMOEBA_FULL ||
14544          element == EL_BD_AMOEBA ||
14545          element == EL_AMOEBA_GROWING))
14546     {
14547       AmoebaCnt[AmoebaNr[x][y]]--;
14548       AmoebaCnt2[AmoebaNr[x][y]]--;
14549     }
14550
14551     if (IS_MOVING(x, y))
14552       RemoveMovingField(x, y);
14553     else
14554     {
14555       RemoveField(x, y);
14556       TEST_DrawLevelField(x, y);
14557     }
14558
14559     // if digged element was about to explode, prevent the explosion
14560     ExplodeField[x][y] = EX_TYPE_NONE;
14561
14562     PlayLevelSoundAction(x, y, action);
14563   }
14564
14565   Store[x][y] = EL_EMPTY;
14566
14567   // this makes it possible to leave the removed element again
14568   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14569     Store[x][y] = element;
14570
14571   return TRUE;
14572 }
14573
14574 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14575 {
14576   int jx = player->jx, jy = player->jy;
14577   int x = jx + dx, y = jy + dy;
14578   int snap_direction = (dx == -1 ? MV_LEFT  :
14579                         dx == +1 ? MV_RIGHT :
14580                         dy == -1 ? MV_UP    :
14581                         dy == +1 ? MV_DOWN  : MV_NONE);
14582   boolean can_continue_snapping = (level.continuous_snapping &&
14583                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14584
14585   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14586     return FALSE;
14587
14588   if (!player->active || !IN_LEV_FIELD(x, y))
14589     return FALSE;
14590
14591   if (dx && dy)
14592     return FALSE;
14593
14594   if (!dx && !dy)
14595   {
14596     if (player->MovPos == 0)
14597       player->is_pushing = FALSE;
14598
14599     player->is_snapping = FALSE;
14600
14601     if (player->MovPos == 0)
14602     {
14603       player->is_moving = FALSE;
14604       player->is_digging = FALSE;
14605       player->is_collecting = FALSE;
14606     }
14607
14608     return FALSE;
14609   }
14610
14611   // prevent snapping with already pressed snap key when not allowed
14612   if (player->is_snapping && !can_continue_snapping)
14613     return FALSE;
14614
14615   player->MovDir = snap_direction;
14616
14617   if (player->MovPos == 0)
14618   {
14619     player->is_moving = FALSE;
14620     player->is_digging = FALSE;
14621     player->is_collecting = FALSE;
14622   }
14623
14624   player->is_dropping = FALSE;
14625   player->is_dropping_pressed = FALSE;
14626   player->drop_pressed_delay = 0;
14627
14628   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14629     return FALSE;
14630
14631   player->is_snapping = TRUE;
14632   player->is_active = TRUE;
14633
14634   if (player->MovPos == 0)
14635   {
14636     player->is_moving = FALSE;
14637     player->is_digging = FALSE;
14638     player->is_collecting = FALSE;
14639   }
14640
14641   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14642     TEST_DrawLevelField(player->last_jx, player->last_jy);
14643
14644   TEST_DrawLevelField(x, y);
14645
14646   return TRUE;
14647 }
14648
14649 static boolean DropElement(struct PlayerInfo *player)
14650 {
14651   int old_element, new_element;
14652   int dropx = player->jx, dropy = player->jy;
14653   int drop_direction = player->MovDir;
14654   int drop_side = drop_direction;
14655   int drop_element = get_next_dropped_element(player);
14656
14657   /* do not drop an element on top of another element; when holding drop key
14658      pressed without moving, dropped element must move away before the next
14659      element can be dropped (this is especially important if the next element
14660      is dynamite, which can be placed on background for historical reasons) */
14661   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14662     return MP_ACTION;
14663
14664   if (IS_THROWABLE(drop_element))
14665   {
14666     dropx += GET_DX_FROM_DIR(drop_direction);
14667     dropy += GET_DY_FROM_DIR(drop_direction);
14668
14669     if (!IN_LEV_FIELD(dropx, dropy))
14670       return FALSE;
14671   }
14672
14673   old_element = Tile[dropx][dropy];     // old element at dropping position
14674   new_element = drop_element;           // default: no change when dropping
14675
14676   // check if player is active, not moving and ready to drop
14677   if (!player->active || player->MovPos || player->drop_delay > 0)
14678     return FALSE;
14679
14680   // check if player has anything that can be dropped
14681   if (new_element == EL_UNDEFINED)
14682     return FALSE;
14683
14684   // only set if player has anything that can be dropped
14685   player->is_dropping_pressed = TRUE;
14686
14687   // check if drop key was pressed long enough for EM style dynamite
14688   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14689     return FALSE;
14690
14691   // check if anything can be dropped at the current position
14692   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14693     return FALSE;
14694
14695   // collected custom elements can only be dropped on empty fields
14696   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14697     return FALSE;
14698
14699   if (old_element != EL_EMPTY)
14700     Back[dropx][dropy] = old_element;   // store old element on this field
14701
14702   ResetGfxAnimation(dropx, dropy);
14703   ResetRandomAnimationValue(dropx, dropy);
14704
14705   if (player->inventory_size > 0 ||
14706       player->inventory_infinite_element != EL_UNDEFINED)
14707   {
14708     if (player->inventory_size > 0)
14709     {
14710       player->inventory_size--;
14711
14712       DrawGameDoorValues();
14713
14714       if (new_element == EL_DYNAMITE)
14715         new_element = EL_DYNAMITE_ACTIVE;
14716       else if (new_element == EL_EM_DYNAMITE)
14717         new_element = EL_EM_DYNAMITE_ACTIVE;
14718       else if (new_element == EL_SP_DISK_RED)
14719         new_element = EL_SP_DISK_RED_ACTIVE;
14720     }
14721
14722     Tile[dropx][dropy] = new_element;
14723
14724     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14725       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14726                           el2img(Tile[dropx][dropy]), 0);
14727
14728     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14729
14730     // needed if previous element just changed to "empty" in the last frame
14731     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14732
14733     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14734                                player->index_bit, drop_side);
14735     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14736                                         CE_PLAYER_DROPS_X,
14737                                         player->index_bit, drop_side);
14738
14739     TestIfElementTouchesCustomElement(dropx, dropy);
14740   }
14741   else          // player is dropping a dyna bomb
14742   {
14743     player->dynabombs_left--;
14744
14745     Tile[dropx][dropy] = new_element;
14746
14747     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14748       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14749                           el2img(Tile[dropx][dropy]), 0);
14750
14751     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14752   }
14753
14754   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14755     InitField_WithBug1(dropx, dropy, FALSE);
14756
14757   new_element = Tile[dropx][dropy];     // element might have changed
14758
14759   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14760       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14761   {
14762     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14763       MovDir[dropx][dropy] = drop_direction;
14764
14765     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14766
14767     // do not cause impact style collision by dropping elements that can fall
14768     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14769   }
14770
14771   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14772   player->is_dropping = TRUE;
14773
14774   player->drop_pressed_delay = 0;
14775   player->is_dropping_pressed = FALSE;
14776
14777   player->drop_x = dropx;
14778   player->drop_y = dropy;
14779
14780   return TRUE;
14781 }
14782
14783 // ----------------------------------------------------------------------------
14784 // game sound playing functions
14785 // ----------------------------------------------------------------------------
14786
14787 static int *loop_sound_frame = NULL;
14788 static int *loop_sound_volume = NULL;
14789
14790 void InitPlayLevelSound(void)
14791 {
14792   int num_sounds = getSoundListSize();
14793
14794   checked_free(loop_sound_frame);
14795   checked_free(loop_sound_volume);
14796
14797   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14798   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14799 }
14800
14801 static void PlayLevelSound(int x, int y, int nr)
14802 {
14803   int sx = SCREENX(x), sy = SCREENY(y);
14804   int volume, stereo_position;
14805   int max_distance = 8;
14806   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14807
14808   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14809       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14810     return;
14811
14812   if (!IN_LEV_FIELD(x, y) ||
14813       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14814       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14815     return;
14816
14817   volume = SOUND_MAX_VOLUME;
14818
14819   if (!IN_SCR_FIELD(sx, sy))
14820   {
14821     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14822     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14823
14824     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14825   }
14826
14827   stereo_position = (SOUND_MAX_LEFT +
14828                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14829                      (SCR_FIELDX + 2 * max_distance));
14830
14831   if (IS_LOOP_SOUND(nr))
14832   {
14833     /* This assures that quieter loop sounds do not overwrite louder ones,
14834        while restarting sound volume comparison with each new game frame. */
14835
14836     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14837       return;
14838
14839     loop_sound_volume[nr] = volume;
14840     loop_sound_frame[nr] = FrameCounter;
14841   }
14842
14843   PlaySoundExt(nr, volume, stereo_position, type);
14844 }
14845
14846 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14847 {
14848   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14849                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14850                  y < LEVELY(BY1) ? LEVELY(BY1) :
14851                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14852                  sound_action);
14853 }
14854
14855 static void PlayLevelSoundAction(int x, int y, int action)
14856 {
14857   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14858 }
14859
14860 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14861 {
14862   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14863
14864   if (sound_effect != SND_UNDEFINED)
14865     PlayLevelSound(x, y, sound_effect);
14866 }
14867
14868 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14869                                               int action)
14870 {
14871   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14872
14873   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14874     PlayLevelSound(x, y, sound_effect);
14875 }
14876
14877 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14878 {
14879   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14880
14881   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14882     PlayLevelSound(x, y, sound_effect);
14883 }
14884
14885 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14886 {
14887   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14888
14889   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14890     StopSound(sound_effect);
14891 }
14892
14893 static int getLevelMusicNr(void)
14894 {
14895   if (levelset.music[level_nr] != MUS_UNDEFINED)
14896     return levelset.music[level_nr];            // from config file
14897   else
14898     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14899 }
14900
14901 static void FadeLevelSounds(void)
14902 {
14903   FadeSounds();
14904 }
14905
14906 static void FadeLevelMusic(void)
14907 {
14908   int music_nr = getLevelMusicNr();
14909   char *curr_music = getCurrentlyPlayingMusicFilename();
14910   char *next_music = getMusicInfoEntryFilename(music_nr);
14911
14912   if (!strEqual(curr_music, next_music))
14913     FadeMusic();
14914 }
14915
14916 void FadeLevelSoundsAndMusic(void)
14917 {
14918   FadeLevelSounds();
14919   FadeLevelMusic();
14920 }
14921
14922 static void PlayLevelMusic(void)
14923 {
14924   int music_nr = getLevelMusicNr();
14925   char *curr_music = getCurrentlyPlayingMusicFilename();
14926   char *next_music = getMusicInfoEntryFilename(music_nr);
14927
14928   if (!strEqual(curr_music, next_music))
14929     PlayMusicLoop(music_nr);
14930 }
14931
14932 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14933 {
14934   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14935   int offset = 0;
14936   int x = xx - offset;
14937   int y = yy - offset;
14938
14939   switch (sample)
14940   {
14941     case SOUND_blank:
14942       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14943       break;
14944
14945     case SOUND_roll:
14946       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14947       break;
14948
14949     case SOUND_stone:
14950       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14951       break;
14952
14953     case SOUND_nut:
14954       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14955       break;
14956
14957     case SOUND_crack:
14958       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14959       break;
14960
14961     case SOUND_bug:
14962       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14963       break;
14964
14965     case SOUND_tank:
14966       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14967       break;
14968
14969     case SOUND_android_clone:
14970       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14971       break;
14972
14973     case SOUND_android_move:
14974       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14975       break;
14976
14977     case SOUND_spring:
14978       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14979       break;
14980
14981     case SOUND_slurp:
14982       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14983       break;
14984
14985     case SOUND_eater:
14986       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14987       break;
14988
14989     case SOUND_eater_eat:
14990       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14991       break;
14992
14993     case SOUND_alien:
14994       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14995       break;
14996
14997     case SOUND_collect:
14998       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14999       break;
15000
15001     case SOUND_diamond:
15002       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15003       break;
15004
15005     case SOUND_squash:
15006       // !!! CHECK THIS !!!
15007 #if 1
15008       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15009 #else
15010       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15011 #endif
15012       break;
15013
15014     case SOUND_wonderfall:
15015       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15016       break;
15017
15018     case SOUND_drip:
15019       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15020       break;
15021
15022     case SOUND_push:
15023       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15024       break;
15025
15026     case SOUND_dirt:
15027       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15028       break;
15029
15030     case SOUND_acid:
15031       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15032       break;
15033
15034     case SOUND_ball:
15035       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15036       break;
15037
15038     case SOUND_slide:
15039       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15040       break;
15041
15042     case SOUND_wonder:
15043       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15044       break;
15045
15046     case SOUND_door:
15047       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15048       break;
15049
15050     case SOUND_exit_open:
15051       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15052       break;
15053
15054     case SOUND_exit_leave:
15055       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15056       break;
15057
15058     case SOUND_dynamite:
15059       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15060       break;
15061
15062     case SOUND_tick:
15063       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15064       break;
15065
15066     case SOUND_press:
15067       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15068       break;
15069
15070     case SOUND_wheel:
15071       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15072       break;
15073
15074     case SOUND_boom:
15075       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15076       break;
15077
15078     case SOUND_die:
15079       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15080       break;
15081
15082     case SOUND_time:
15083       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15084       break;
15085
15086     default:
15087       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15088       break;
15089   }
15090 }
15091
15092 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15093 {
15094   int element = map_element_SP_to_RND(element_sp);
15095   int action = map_action_SP_to_RND(action_sp);
15096   int offset = (setup.sp_show_border_elements ? 0 : 1);
15097   int x = xx - offset;
15098   int y = yy - offset;
15099
15100   PlayLevelSoundElementAction(x, y, element, action);
15101 }
15102
15103 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15104 {
15105   int element = map_element_MM_to_RND(element_mm);
15106   int action = map_action_MM_to_RND(action_mm);
15107   int offset = 0;
15108   int x = xx - offset;
15109   int y = yy - offset;
15110
15111   if (!IS_MM_ELEMENT(element))
15112     element = EL_MM_DEFAULT;
15113
15114   PlayLevelSoundElementAction(x, y, element, action);
15115 }
15116
15117 void PlaySound_MM(int sound_mm)
15118 {
15119   int sound = map_sound_MM_to_RND(sound_mm);
15120
15121   if (sound == SND_UNDEFINED)
15122     return;
15123
15124   PlaySound(sound);
15125 }
15126
15127 void PlaySoundLoop_MM(int sound_mm)
15128 {
15129   int sound = map_sound_MM_to_RND(sound_mm);
15130
15131   if (sound == SND_UNDEFINED)
15132     return;
15133
15134   PlaySoundLoop(sound);
15135 }
15136
15137 void StopSound_MM(int sound_mm)
15138 {
15139   int sound = map_sound_MM_to_RND(sound_mm);
15140
15141   if (sound == SND_UNDEFINED)
15142     return;
15143
15144   StopSound(sound);
15145 }
15146
15147 void RaiseScore(int value)
15148 {
15149   game.score += value;
15150
15151   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15152
15153   DisplayGameControlValues();
15154 }
15155
15156 void RaiseScoreElement(int element)
15157 {
15158   switch (element)
15159   {
15160     case EL_EMERALD:
15161     case EL_BD_DIAMOND:
15162     case EL_EMERALD_YELLOW:
15163     case EL_EMERALD_RED:
15164     case EL_EMERALD_PURPLE:
15165     case EL_SP_INFOTRON:
15166       RaiseScore(level.score[SC_EMERALD]);
15167       break;
15168     case EL_DIAMOND:
15169       RaiseScore(level.score[SC_DIAMOND]);
15170       break;
15171     case EL_CRYSTAL:
15172       RaiseScore(level.score[SC_CRYSTAL]);
15173       break;
15174     case EL_PEARL:
15175       RaiseScore(level.score[SC_PEARL]);
15176       break;
15177     case EL_BUG:
15178     case EL_BD_BUTTERFLY:
15179     case EL_SP_ELECTRON:
15180       RaiseScore(level.score[SC_BUG]);
15181       break;
15182     case EL_SPACESHIP:
15183     case EL_BD_FIREFLY:
15184     case EL_SP_SNIKSNAK:
15185       RaiseScore(level.score[SC_SPACESHIP]);
15186       break;
15187     case EL_YAMYAM:
15188     case EL_DARK_YAMYAM:
15189       RaiseScore(level.score[SC_YAMYAM]);
15190       break;
15191     case EL_ROBOT:
15192       RaiseScore(level.score[SC_ROBOT]);
15193       break;
15194     case EL_PACMAN:
15195       RaiseScore(level.score[SC_PACMAN]);
15196       break;
15197     case EL_NUT:
15198       RaiseScore(level.score[SC_NUT]);
15199       break;
15200     case EL_DYNAMITE:
15201     case EL_EM_DYNAMITE:
15202     case EL_SP_DISK_RED:
15203     case EL_DYNABOMB_INCREASE_NUMBER:
15204     case EL_DYNABOMB_INCREASE_SIZE:
15205     case EL_DYNABOMB_INCREASE_POWER:
15206       RaiseScore(level.score[SC_DYNAMITE]);
15207       break;
15208     case EL_SHIELD_NORMAL:
15209     case EL_SHIELD_DEADLY:
15210       RaiseScore(level.score[SC_SHIELD]);
15211       break;
15212     case EL_EXTRA_TIME:
15213       RaiseScore(level.extra_time_score);
15214       break;
15215     case EL_KEY_1:
15216     case EL_KEY_2:
15217     case EL_KEY_3:
15218     case EL_KEY_4:
15219     case EL_EM_KEY_1:
15220     case EL_EM_KEY_2:
15221     case EL_EM_KEY_3:
15222     case EL_EM_KEY_4:
15223     case EL_EMC_KEY_5:
15224     case EL_EMC_KEY_6:
15225     case EL_EMC_KEY_7:
15226     case EL_EMC_KEY_8:
15227     case EL_DC_KEY_WHITE:
15228       RaiseScore(level.score[SC_KEY]);
15229       break;
15230     default:
15231       RaiseScore(element_info[element].collect_score);
15232       break;
15233   }
15234 }
15235
15236 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15237 {
15238   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15239   {
15240     // closing door required in case of envelope style request dialogs
15241     if (!skip_request)
15242     {
15243       // prevent short reactivation of overlay buttons while closing door
15244       SetOverlayActive(FALSE);
15245
15246       CloseDoor(DOOR_CLOSE_1);
15247     }
15248
15249     if (network.enabled)
15250       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15251     else
15252     {
15253       if (quick_quit)
15254         FadeSkipNextFadeIn();
15255
15256       SetGameStatus(GAME_MODE_MAIN);
15257
15258       DrawMainMenu();
15259     }
15260   }
15261   else          // continue playing the game
15262   {
15263     if (tape.playing && tape.deactivate_display)
15264       TapeDeactivateDisplayOff(TRUE);
15265
15266     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15267
15268     if (tape.playing && tape.deactivate_display)
15269       TapeDeactivateDisplayOn();
15270   }
15271 }
15272
15273 void RequestQuitGame(boolean ask_if_really_quit)
15274 {
15275   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15276   boolean skip_request = game.all_players_gone || quick_quit;
15277
15278   RequestQuitGameExt(skip_request, quick_quit,
15279                      "Do you really want to quit the game?");
15280 }
15281
15282 void RequestRestartGame(char *message)
15283 {
15284   game.restart_game_message = NULL;
15285
15286   boolean has_started_game = hasStartedNetworkGame();
15287   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15288
15289   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15290   {
15291     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15292   }
15293   else
15294   {
15295     // needed in case of envelope request to close game panel
15296     CloseDoor(DOOR_CLOSE_1);
15297
15298     SetGameStatus(GAME_MODE_MAIN);
15299
15300     DrawMainMenu();
15301   }
15302 }
15303
15304 void CheckGameOver(void)
15305 {
15306   static boolean last_game_over = FALSE;
15307   static int game_over_delay = 0;
15308   int game_over_delay_value = 50;
15309   boolean game_over = checkGameFailed();
15310
15311   // do not handle game over if request dialog is already active
15312   if (game.request_active)
15313     return;
15314
15315   // do not ask to play again if game was never actually played
15316   if (!game.GamePlayed)
15317     return;
15318
15319   if (!game_over)
15320   {
15321     last_game_over = FALSE;
15322     game_over_delay = game_over_delay_value;
15323
15324     return;
15325   }
15326
15327   if (game_over_delay > 0)
15328   {
15329     game_over_delay--;
15330
15331     return;
15332   }
15333
15334   if (last_game_over != game_over)
15335     game.restart_game_message = (hasStartedNetworkGame() ?
15336                                  "Game over! Play it again?" :
15337                                  "Game over!");
15338
15339   last_game_over = game_over;
15340 }
15341
15342 boolean checkGameSolved(void)
15343 {
15344   // set for all game engines if level was solved
15345   return game.LevelSolved_GameEnd;
15346 }
15347
15348 boolean checkGameFailed(void)
15349 {
15350   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15351     return (game_em.game_over && !game_em.level_solved);
15352   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15353     return (game_sp.game_over && !game_sp.level_solved);
15354   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15355     return (game_mm.game_over && !game_mm.level_solved);
15356   else                          // GAME_ENGINE_TYPE_RND
15357     return (game.GameOver && !game.LevelSolved);
15358 }
15359
15360 boolean checkGameEnded(void)
15361 {
15362   return (checkGameSolved() || checkGameFailed());
15363 }
15364
15365
15366 // ----------------------------------------------------------------------------
15367 // random generator functions
15368 // ----------------------------------------------------------------------------
15369
15370 unsigned int InitEngineRandom_RND(int seed)
15371 {
15372   game.num_random_calls = 0;
15373
15374   return InitEngineRandom(seed);
15375 }
15376
15377 unsigned int RND(int max)
15378 {
15379   if (max > 0)
15380   {
15381     game.num_random_calls++;
15382
15383     return GetEngineRandom(max);
15384   }
15385
15386   return 0;
15387 }
15388
15389
15390 // ----------------------------------------------------------------------------
15391 // game engine snapshot handling functions
15392 // ----------------------------------------------------------------------------
15393
15394 struct EngineSnapshotInfo
15395 {
15396   // runtime values for custom element collect score
15397   int collect_score[NUM_CUSTOM_ELEMENTS];
15398
15399   // runtime values for group element choice position
15400   int choice_pos[NUM_GROUP_ELEMENTS];
15401
15402   // runtime values for belt position animations
15403   int belt_graphic[4][NUM_BELT_PARTS];
15404   int belt_anim_mode[4][NUM_BELT_PARTS];
15405 };
15406
15407 static struct EngineSnapshotInfo engine_snapshot_rnd;
15408 static char *snapshot_level_identifier = NULL;
15409 static int snapshot_level_nr = -1;
15410
15411 static void SaveEngineSnapshotValues_RND(void)
15412 {
15413   static int belt_base_active_element[4] =
15414   {
15415     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15416     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15417     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15418     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15419   };
15420   int i, j;
15421
15422   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15423   {
15424     int element = EL_CUSTOM_START + i;
15425
15426     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15427   }
15428
15429   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15430   {
15431     int element = EL_GROUP_START + i;
15432
15433     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15434   }
15435
15436   for (i = 0; i < 4; i++)
15437   {
15438     for (j = 0; j < NUM_BELT_PARTS; j++)
15439     {
15440       int element = belt_base_active_element[i] + j;
15441       int graphic = el2img(element);
15442       int anim_mode = graphic_info[graphic].anim_mode;
15443
15444       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15445       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15446     }
15447   }
15448 }
15449
15450 static void LoadEngineSnapshotValues_RND(void)
15451 {
15452   unsigned int num_random_calls = game.num_random_calls;
15453   int i, j;
15454
15455   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15456   {
15457     int element = EL_CUSTOM_START + i;
15458
15459     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15460   }
15461
15462   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15463   {
15464     int element = EL_GROUP_START + i;
15465
15466     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15467   }
15468
15469   for (i = 0; i < 4; i++)
15470   {
15471     for (j = 0; j < NUM_BELT_PARTS; j++)
15472     {
15473       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15474       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15475
15476       graphic_info[graphic].anim_mode = anim_mode;
15477     }
15478   }
15479
15480   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15481   {
15482     InitRND(tape.random_seed);
15483     for (i = 0; i < num_random_calls; i++)
15484       RND(1);
15485   }
15486
15487   if (game.num_random_calls != num_random_calls)
15488   {
15489     Error("number of random calls out of sync");
15490     Error("number of random calls should be %d", num_random_calls);
15491     Error("number of random calls is %d", game.num_random_calls);
15492
15493     Fail("this should not happen -- please debug");
15494   }
15495 }
15496
15497 void FreeEngineSnapshotSingle(void)
15498 {
15499   FreeSnapshotSingle();
15500
15501   setString(&snapshot_level_identifier, NULL);
15502   snapshot_level_nr = -1;
15503 }
15504
15505 void FreeEngineSnapshotList(void)
15506 {
15507   FreeSnapshotList();
15508 }
15509
15510 static ListNode *SaveEngineSnapshotBuffers(void)
15511 {
15512   ListNode *buffers = NULL;
15513
15514   // copy some special values to a structure better suited for the snapshot
15515
15516   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15517     SaveEngineSnapshotValues_RND();
15518   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15519     SaveEngineSnapshotValues_EM();
15520   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15521     SaveEngineSnapshotValues_SP(&buffers);
15522   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15523     SaveEngineSnapshotValues_MM(&buffers);
15524
15525   // save values stored in special snapshot structure
15526
15527   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15528     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15529   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15530     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15531   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15532     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15533   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15534     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15535
15536   // save further RND engine values
15537
15538   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15539   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15540   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15541
15542   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15543   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15544   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15545   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15546   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15547
15548   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15549   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15550   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15551
15552   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15553
15554   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15555   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15556
15557   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15558   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15559   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15560   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15561   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15575
15576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15578
15579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15582
15583   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15585
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15591
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15594
15595 #if 0
15596   ListNode *node = engine_snapshot_list_rnd;
15597   int num_bytes = 0;
15598
15599   while (node != NULL)
15600   {
15601     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15602
15603     node = node->next;
15604   }
15605
15606   Debug("game:playing:SaveEngineSnapshotBuffers",
15607         "size of engine snapshot: %d bytes", num_bytes);
15608 #endif
15609
15610   return buffers;
15611 }
15612
15613 void SaveEngineSnapshotSingle(void)
15614 {
15615   ListNode *buffers = SaveEngineSnapshotBuffers();
15616
15617   // finally save all snapshot buffers to single snapshot
15618   SaveSnapshotSingle(buffers);
15619
15620   // save level identification information
15621   setString(&snapshot_level_identifier, leveldir_current->identifier);
15622   snapshot_level_nr = level_nr;
15623 }
15624
15625 boolean CheckSaveEngineSnapshotToList(void)
15626 {
15627   boolean save_snapshot =
15628     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15629      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15630       game.snapshot.changed_action) ||
15631      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15632       game.snapshot.collected_item));
15633
15634   game.snapshot.changed_action = FALSE;
15635   game.snapshot.collected_item = FALSE;
15636   game.snapshot.save_snapshot = save_snapshot;
15637
15638   return save_snapshot;
15639 }
15640
15641 void SaveEngineSnapshotToList(void)
15642 {
15643   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15644       tape.quick_resume)
15645     return;
15646
15647   ListNode *buffers = SaveEngineSnapshotBuffers();
15648
15649   // finally save all snapshot buffers to snapshot list
15650   SaveSnapshotToList(buffers);
15651 }
15652
15653 void SaveEngineSnapshotToListInitial(void)
15654 {
15655   FreeEngineSnapshotList();
15656
15657   SaveEngineSnapshotToList();
15658 }
15659
15660 static void LoadEngineSnapshotValues(void)
15661 {
15662   // restore special values from snapshot structure
15663
15664   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15665     LoadEngineSnapshotValues_RND();
15666   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15667     LoadEngineSnapshotValues_EM();
15668   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15669     LoadEngineSnapshotValues_SP();
15670   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15671     LoadEngineSnapshotValues_MM();
15672 }
15673
15674 void LoadEngineSnapshotSingle(void)
15675 {
15676   LoadSnapshotSingle();
15677
15678   LoadEngineSnapshotValues();
15679 }
15680
15681 static void LoadEngineSnapshot_Undo(int steps)
15682 {
15683   LoadSnapshotFromList_Older(steps);
15684
15685   LoadEngineSnapshotValues();
15686 }
15687
15688 static void LoadEngineSnapshot_Redo(int steps)
15689 {
15690   LoadSnapshotFromList_Newer(steps);
15691
15692   LoadEngineSnapshotValues();
15693 }
15694
15695 boolean CheckEngineSnapshotSingle(void)
15696 {
15697   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15698           snapshot_level_nr == level_nr);
15699 }
15700
15701 boolean CheckEngineSnapshotList(void)
15702 {
15703   return CheckSnapshotList();
15704 }
15705
15706
15707 // ---------- new game button stuff -------------------------------------------
15708
15709 static struct
15710 {
15711   int graphic;
15712   struct XY *pos;
15713   int gadget_id;
15714   boolean *setup_value;
15715   boolean allowed_on_tape;
15716   boolean is_touch_button;
15717   char *infotext;
15718 } gamebutton_info[NUM_GAME_BUTTONS] =
15719 {
15720   {
15721     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15722     GAME_CTRL_ID_STOP,                          NULL,
15723     TRUE, FALSE,                                "stop game"
15724   },
15725   {
15726     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15727     GAME_CTRL_ID_PAUSE,                         NULL,
15728     TRUE, FALSE,                                "pause game"
15729   },
15730   {
15731     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15732     GAME_CTRL_ID_PLAY,                          NULL,
15733     TRUE, FALSE,                                "play game"
15734   },
15735   {
15736     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15737     GAME_CTRL_ID_UNDO,                          NULL,
15738     TRUE, FALSE,                                "undo step"
15739   },
15740   {
15741     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15742     GAME_CTRL_ID_REDO,                          NULL,
15743     TRUE, FALSE,                                "redo step"
15744   },
15745   {
15746     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15747     GAME_CTRL_ID_SAVE,                          NULL,
15748     TRUE, FALSE,                                "save game"
15749   },
15750   {
15751     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15752     GAME_CTRL_ID_PAUSE2,                        NULL,
15753     TRUE, FALSE,                                "pause game"
15754   },
15755   {
15756     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15757     GAME_CTRL_ID_LOAD,                          NULL,
15758     TRUE, FALSE,                                "load game"
15759   },
15760   {
15761     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15762     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15763     FALSE, FALSE,                               "stop game"
15764   },
15765   {
15766     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15767     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15768     FALSE, FALSE,                               "pause game"
15769   },
15770   {
15771     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15772     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15773     FALSE, FALSE,                               "play game"
15774   },
15775   {
15776     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15777     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15778     FALSE, TRUE,                                "stop game"
15779   },
15780   {
15781     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15782     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15783     FALSE, TRUE,                                "pause game"
15784   },
15785   {
15786     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15787     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15788     TRUE, FALSE,                                "background music on/off"
15789   },
15790   {
15791     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15792     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15793     TRUE, FALSE,                                "sound loops on/off"
15794   },
15795   {
15796     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15797     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15798     TRUE, FALSE,                                "normal sounds on/off"
15799   },
15800   {
15801     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15802     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15803     FALSE, FALSE,                               "background music on/off"
15804   },
15805   {
15806     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15807     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15808     FALSE, FALSE,                               "sound loops on/off"
15809   },
15810   {
15811     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15812     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15813     FALSE, FALSE,                               "normal sounds on/off"
15814   }
15815 };
15816
15817 void CreateGameButtons(void)
15818 {
15819   int i;
15820
15821   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15822   {
15823     int graphic = gamebutton_info[i].graphic;
15824     struct GraphicInfo *gfx = &graphic_info[graphic];
15825     struct XY *pos = gamebutton_info[i].pos;
15826     struct GadgetInfo *gi;
15827     int button_type;
15828     boolean checked;
15829     unsigned int event_mask;
15830     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15831     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15832     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15833     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15834     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15835     int gd_x   = gfx->src_x;
15836     int gd_y   = gfx->src_y;
15837     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15838     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15839     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15840     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15841     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15842     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15843     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15844     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15845     int id = i;
15846
15847     if (gfx->bitmap == NULL)
15848     {
15849       game_gadget[id] = NULL;
15850
15851       continue;
15852     }
15853
15854     if (id == GAME_CTRL_ID_STOP ||
15855         id == GAME_CTRL_ID_PANEL_STOP ||
15856         id == GAME_CTRL_ID_TOUCH_STOP ||
15857         id == GAME_CTRL_ID_PLAY ||
15858         id == GAME_CTRL_ID_PANEL_PLAY ||
15859         id == GAME_CTRL_ID_SAVE ||
15860         id == GAME_CTRL_ID_LOAD)
15861     {
15862       button_type = GD_TYPE_NORMAL_BUTTON;
15863       checked = FALSE;
15864       event_mask = GD_EVENT_RELEASED;
15865     }
15866     else if (id == GAME_CTRL_ID_UNDO ||
15867              id == GAME_CTRL_ID_REDO)
15868     {
15869       button_type = GD_TYPE_NORMAL_BUTTON;
15870       checked = FALSE;
15871       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15872     }
15873     else
15874     {
15875       button_type = GD_TYPE_CHECK_BUTTON;
15876       checked = (gamebutton_info[i].setup_value != NULL ?
15877                  *gamebutton_info[i].setup_value : FALSE);
15878       event_mask = GD_EVENT_PRESSED;
15879     }
15880
15881     gi = CreateGadget(GDI_CUSTOM_ID, id,
15882                       GDI_IMAGE_ID, graphic,
15883                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15884                       GDI_X, base_x + x,
15885                       GDI_Y, base_y + y,
15886                       GDI_WIDTH, gfx->width,
15887                       GDI_HEIGHT, gfx->height,
15888                       GDI_TYPE, button_type,
15889                       GDI_STATE, GD_BUTTON_UNPRESSED,
15890                       GDI_CHECKED, checked,
15891                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15892                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15893                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15894                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15895                       GDI_DIRECT_DRAW, FALSE,
15896                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15897                       GDI_EVENT_MASK, event_mask,
15898                       GDI_CALLBACK_ACTION, HandleGameButtons,
15899                       GDI_END);
15900
15901     if (gi == NULL)
15902       Fail("cannot create gadget");
15903
15904     game_gadget[id] = gi;
15905   }
15906 }
15907
15908 void FreeGameButtons(void)
15909 {
15910   int i;
15911
15912   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15913     FreeGadget(game_gadget[i]);
15914 }
15915
15916 static void UnmapGameButtonsAtSamePosition(int id)
15917 {
15918   int i;
15919
15920   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15921     if (i != id &&
15922         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15923         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15924       UnmapGadget(game_gadget[i]);
15925 }
15926
15927 static void UnmapGameButtonsAtSamePosition_All(void)
15928 {
15929   if (setup.show_snapshot_buttons)
15930   {
15931     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15932     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15933     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15934   }
15935   else
15936   {
15937     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15938     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15939     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15940
15941     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15942     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15943     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15944   }
15945 }
15946
15947 static void MapGameButtonsAtSamePosition(int id)
15948 {
15949   int i;
15950
15951   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15952     if (i != id &&
15953         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15954         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15955       MapGadget(game_gadget[i]);
15956
15957   UnmapGameButtonsAtSamePosition_All();
15958 }
15959
15960 void MapUndoRedoButtons(void)
15961 {
15962   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15963   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15964
15965   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15966   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15967 }
15968
15969 void UnmapUndoRedoButtons(void)
15970 {
15971   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15972   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15973
15974   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15975   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15976 }
15977
15978 void ModifyPauseButtons(void)
15979 {
15980   static int ids[] =
15981   {
15982     GAME_CTRL_ID_PAUSE,
15983     GAME_CTRL_ID_PAUSE2,
15984     GAME_CTRL_ID_PANEL_PAUSE,
15985     GAME_CTRL_ID_TOUCH_PAUSE,
15986     -1
15987   };
15988   int i;
15989
15990   for (i = 0; ids[i] > -1; i++)
15991     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15992 }
15993
15994 static void MapGameButtonsExt(boolean on_tape)
15995 {
15996   int i;
15997
15998   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15999     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16000         i != GAME_CTRL_ID_UNDO &&
16001         i != GAME_CTRL_ID_REDO)
16002       MapGadget(game_gadget[i]);
16003
16004   UnmapGameButtonsAtSamePosition_All();
16005
16006   RedrawGameButtons();
16007 }
16008
16009 static void UnmapGameButtonsExt(boolean on_tape)
16010 {
16011   int i;
16012
16013   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16014     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16015       UnmapGadget(game_gadget[i]);
16016 }
16017
16018 static void RedrawGameButtonsExt(boolean on_tape)
16019 {
16020   int i;
16021
16022   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16023     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16024       RedrawGadget(game_gadget[i]);
16025 }
16026
16027 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16028 {
16029   if (gi == NULL)
16030     return;
16031
16032   gi->checked = state;
16033 }
16034
16035 static void RedrawSoundButtonGadget(int id)
16036 {
16037   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16038              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16039              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16040              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16041              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16042              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16043              id);
16044
16045   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16046   RedrawGadget(game_gadget[id2]);
16047 }
16048
16049 void MapGameButtons(void)
16050 {
16051   MapGameButtonsExt(FALSE);
16052 }
16053
16054 void UnmapGameButtons(void)
16055 {
16056   UnmapGameButtonsExt(FALSE);
16057 }
16058
16059 void RedrawGameButtons(void)
16060 {
16061   RedrawGameButtonsExt(FALSE);
16062 }
16063
16064 void MapGameButtonsOnTape(void)
16065 {
16066   MapGameButtonsExt(TRUE);
16067 }
16068
16069 void UnmapGameButtonsOnTape(void)
16070 {
16071   UnmapGameButtonsExt(TRUE);
16072 }
16073
16074 void RedrawGameButtonsOnTape(void)
16075 {
16076   RedrawGameButtonsExt(TRUE);
16077 }
16078
16079 static void GameUndoRedoExt(void)
16080 {
16081   ClearPlayerAction();
16082
16083   tape.pausing = TRUE;
16084
16085   RedrawPlayfield();
16086   UpdateAndDisplayGameControlValues();
16087
16088   DrawCompleteVideoDisplay();
16089   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16090   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16091   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16092
16093   BackToFront();
16094 }
16095
16096 static void GameUndo(int steps)
16097 {
16098   if (!CheckEngineSnapshotList())
16099     return;
16100
16101   LoadEngineSnapshot_Undo(steps);
16102
16103   GameUndoRedoExt();
16104 }
16105
16106 static void GameRedo(int steps)
16107 {
16108   if (!CheckEngineSnapshotList())
16109     return;
16110
16111   LoadEngineSnapshot_Redo(steps);
16112
16113   GameUndoRedoExt();
16114 }
16115
16116 static void HandleGameButtonsExt(int id, int button)
16117 {
16118   static boolean game_undo_executed = FALSE;
16119   int steps = BUTTON_STEPSIZE(button);
16120   boolean handle_game_buttons =
16121     (game_status == GAME_MODE_PLAYING ||
16122      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16123
16124   if (!handle_game_buttons)
16125     return;
16126
16127   switch (id)
16128   {
16129     case GAME_CTRL_ID_STOP:
16130     case GAME_CTRL_ID_PANEL_STOP:
16131     case GAME_CTRL_ID_TOUCH_STOP:
16132       if (game_status == GAME_MODE_MAIN)
16133         break;
16134
16135       if (tape.playing)
16136         TapeStop();
16137       else
16138         RequestQuitGame(TRUE);
16139
16140       break;
16141
16142     case GAME_CTRL_ID_PAUSE:
16143     case GAME_CTRL_ID_PAUSE2:
16144     case GAME_CTRL_ID_PANEL_PAUSE:
16145     case GAME_CTRL_ID_TOUCH_PAUSE:
16146       if (network.enabled && game_status == GAME_MODE_PLAYING)
16147       {
16148         if (tape.pausing)
16149           SendToServer_ContinuePlaying();
16150         else
16151           SendToServer_PausePlaying();
16152       }
16153       else
16154         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16155
16156       game_undo_executed = FALSE;
16157
16158       break;
16159
16160     case GAME_CTRL_ID_PLAY:
16161     case GAME_CTRL_ID_PANEL_PLAY:
16162       if (game_status == GAME_MODE_MAIN)
16163       {
16164         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16165       }
16166       else if (tape.pausing)
16167       {
16168         if (network.enabled)
16169           SendToServer_ContinuePlaying();
16170         else
16171           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16172       }
16173       break;
16174
16175     case GAME_CTRL_ID_UNDO:
16176       // Important: When using "save snapshot when collecting an item" mode,
16177       // load last (current) snapshot for first "undo" after pressing "pause"
16178       // (else the last-but-one snapshot would be loaded, because the snapshot
16179       // pointer already points to the last snapshot when pressing "pause",
16180       // which is fine for "every step/move" mode, but not for "every collect")
16181       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16182           !game_undo_executed)
16183         steps--;
16184
16185       game_undo_executed = TRUE;
16186
16187       GameUndo(steps);
16188       break;
16189
16190     case GAME_CTRL_ID_REDO:
16191       GameRedo(steps);
16192       break;
16193
16194     case GAME_CTRL_ID_SAVE:
16195       TapeQuickSave();
16196       break;
16197
16198     case GAME_CTRL_ID_LOAD:
16199       TapeQuickLoad();
16200       break;
16201
16202     case SOUND_CTRL_ID_MUSIC:
16203     case SOUND_CTRL_ID_PANEL_MUSIC:
16204       if (setup.sound_music)
16205       { 
16206         setup.sound_music = FALSE;
16207
16208         FadeMusic();
16209       }
16210       else if (audio.music_available)
16211       { 
16212         setup.sound = setup.sound_music = TRUE;
16213
16214         SetAudioMode(setup.sound);
16215
16216         if (game_status == GAME_MODE_PLAYING)
16217           PlayLevelMusic();
16218       }
16219
16220       RedrawSoundButtonGadget(id);
16221
16222       break;
16223
16224     case SOUND_CTRL_ID_LOOPS:
16225     case SOUND_CTRL_ID_PANEL_LOOPS:
16226       if (setup.sound_loops)
16227         setup.sound_loops = FALSE;
16228       else if (audio.loops_available)
16229       {
16230         setup.sound = setup.sound_loops = TRUE;
16231
16232         SetAudioMode(setup.sound);
16233       }
16234
16235       RedrawSoundButtonGadget(id);
16236
16237       break;
16238
16239     case SOUND_CTRL_ID_SIMPLE:
16240     case SOUND_CTRL_ID_PANEL_SIMPLE:
16241       if (setup.sound_simple)
16242         setup.sound_simple = FALSE;
16243       else if (audio.sound_available)
16244       {
16245         setup.sound = setup.sound_simple = TRUE;
16246
16247         SetAudioMode(setup.sound);
16248       }
16249
16250       RedrawSoundButtonGadget(id);
16251
16252       break;
16253
16254     default:
16255       break;
16256   }
16257 }
16258
16259 static void HandleGameButtons(struct GadgetInfo *gi)
16260 {
16261   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16262 }
16263
16264 void HandleSoundButtonKeys(Key key)
16265 {
16266   if (key == setup.shortcut.sound_simple)
16267     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16268   else if (key == setup.shortcut.sound_loops)
16269     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16270   else if (key == setup.shortcut.sound_music)
16271     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16272 }