1b7eaa3bdb5bae22d0627adc2bb070eb2c1901c1
[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 //                  http://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()             (local_player->LevelSolved_PanelOff)
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_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for other actions */
831 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN       (1)
833 #define MOVE_STEPSIZE_MAX       (TILEX)
834
835 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
837
838 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
839
840 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
841                                  RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
843                                  RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                  RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
847                                     (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
849                                  RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
852                                  RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
854                                  RND((c)->delay_random))
855
856
857 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
858          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
859
860 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
861         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
862          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
863          (be) + (e) - EL_SELF)
864
865 #define GET_PLAYER_FROM_BITS(p)                                         \
866         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
867
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
869         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
870          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
871          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
872          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
873          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
874          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
875          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
876          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
877          (e))
878
879 #define CAN_GROW_INTO(e)                                                \
880         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
881
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
883                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
884                                         (condition)))
885
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
887                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
888                                         (CAN_MOVE_INTO_ACID(e) &&       \
889                                          Feld[x][y] == EL_ACID) ||      \
890                                         (condition)))
891
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
893                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
894                                         (CAN_MOVE_INTO_ACID(e) &&       \
895                                          Feld[x][y] == EL_ACID) ||      \
896                                         (condition)))
897
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
899                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
900                                         (condition) ||                  \
901                                         (CAN_MOVE_INTO_ACID(e) &&       \
902                                          Feld[x][y] == EL_ACID) ||      \
903                                         (DONT_COLLIDE_WITH(e) &&        \
904                                          IS_PLAYER(x, y) &&             \
905                                          !PLAYER_ENEMY_PROTECTED(x, y))))
906
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
908         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
909
910 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
911         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
912
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
915
916 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
917         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
919
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
921         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
922
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
924         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
925
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
928
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
931
932 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
933         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
934
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940                                                  IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
943
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
946
947 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
949
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
951         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
952                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
953
954 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
955
956 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
957                 (!IS_PLAYER(x, y) &&                                    \
958                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
959
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
961         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
962
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
965
966 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
970
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP               0
973 #define GAME_CTRL_ID_PAUSE              1
974 #define GAME_CTRL_ID_PLAY               2
975 #define GAME_CTRL_ID_UNDO               3
976 #define GAME_CTRL_ID_REDO               4
977 #define GAME_CTRL_ID_SAVE               5
978 #define GAME_CTRL_ID_PAUSE2             6
979 #define GAME_CTRL_ID_LOAD               7
980 #define SOUND_CTRL_ID_MUSIC             8
981 #define SOUND_CTRL_ID_LOOPS             9
982 #define SOUND_CTRL_ID_SIMPLE            10
983
984 #define NUM_GAME_BUTTONS                11
985
986
987 /* forward declaration for internal use */
988
989 static void CreateField(int, int, int);
990
991 static void ResetGfxAnimation(int, int);
992
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
995
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1000
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1005
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1012
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1016
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1020
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1023         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1027         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1029         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1030
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev)                             \
1033         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1035         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1037         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1038
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1047
1048 static void HandleGameButtons(struct GadgetInfo *);
1049
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1057
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1068
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1072
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1075
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1077
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1081
1082 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1083 {                                                                       \
1084   if (recursion_loop_detected)                                          \
1085     return (rc);                                                        \
1086                                                                         \
1087   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1088   {                                                                     \
1089     recursion_loop_detected = TRUE;                                     \
1090     recursion_loop_element = (e);                                       \
1091   }                                                                     \
1092                                                                         \
1093   recursion_loop_depth++;                                               \
1094 }
1095
1096 #define RECURSION_LOOP_DETECTION_END()                                  \
1097 {                                                                       \
1098   recursion_loop_depth--;                                               \
1099 }
1100
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1104
1105 static int map_player_action[MAX_PLAYERS];
1106
1107
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after  */
1110 /* a specified time, eventually calling a function when changing             */
1111 /* ------------------------------------------------------------------------- */
1112
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1116
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1120
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1124
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1127
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1130
1131 struct ChangingElementInfo
1132 {
1133   int element;
1134   int target_element;
1135   int change_delay;
1136   void (*pre_change_function)(int x, int y);
1137   void (*change_function)(int x, int y);
1138   void (*post_change_function)(int x, int y);
1139 };
1140
1141 static struct ChangingElementInfo change_delay_list[] =
1142 {
1143   {
1144     EL_NUT_BREAKING,
1145     EL_EMERALD,
1146     6,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_PEARL_BREAKING,
1153     EL_EMPTY,
1154     8,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_EXIT_OPENING,
1161     EL_EXIT_OPEN,
1162     29,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_CLOSING,
1169     EL_EXIT_CLOSED,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_STEEL_EXIT_OPENING,
1177     EL_STEEL_EXIT_OPEN,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_CLOSING,
1185     EL_STEEL_EXIT_CLOSED,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_EM_EXIT_OPENING,
1193     EL_EM_EXIT_OPEN,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_CLOSING,
1201     EL_EMPTY,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_STEEL_EXIT_OPENING,
1209     EL_EM_STEEL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_CLOSING,
1217     EL_STEELWALL,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_SP_EXIT_OPENING,
1225     EL_SP_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_CLOSING,
1233     EL_SP_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SWITCHGATE_OPENING,
1241     EL_SWITCHGATE_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_CLOSING,
1249     EL_SWITCHGATE_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_TIMEGATE_OPENING,
1257     EL_TIMEGATE_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_CLOSING,
1265     EL_TIMEGATE_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271
1272   {
1273     EL_ACID_SPLASH_LEFT,
1274     EL_EMPTY,
1275     8,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_ACID_SPLASH_RIGHT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SP_BUGGY_BASE,
1290     EL_SP_BUGGY_BASE_ACTIVATING,
1291     0,
1292     InitBuggyBase,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE_ACTIVATING,
1298     EL_SP_BUGGY_BASE_ACTIVE,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVE,
1306     EL_SP_BUGGY_BASE,
1307     0,
1308     InitBuggyBase,
1309     WarnBuggyBase,
1310     NULL
1311   },
1312   {
1313     EL_TRAP,
1314     EL_TRAP_ACTIVE,
1315     0,
1316     InitTrap,
1317     NULL,
1318     ActivateTrap
1319   },
1320   {
1321     EL_TRAP_ACTIVE,
1322     EL_TRAP,
1323     31,
1324     NULL,
1325     ChangeActiveTrap,
1326     NULL
1327   },
1328   {
1329     EL_ROBOT_WHEEL_ACTIVE,
1330     EL_ROBOT_WHEEL,
1331     0,
1332     InitRobotWheel,
1333     RunRobotWheel,
1334     StopRobotWheel
1335   },
1336   {
1337     EL_TIMEGATE_SWITCH_ACTIVE,
1338     EL_TIMEGATE_SWITCH,
1339     0,
1340     InitTimegateWheel,
1341     RunTimegateWheel,
1342     NULL
1343   },
1344   {
1345     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346     EL_DC_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     EL_EMC_MAGIC_BALL_ACTIVE,
1355     0,
1356     InitMagicBallDelay,
1357     NULL,
1358     ActivateMagicBall
1359   },
1360   {
1361     EL_EMC_SPRING_BUMPER_ACTIVE,
1362     EL_EMC_SPRING_BUMPER,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_DIAGONAL_SHRINKING,
1370     EL_UNDEFINED,
1371     0,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_GROWING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL,
1383   },
1384
1385   {
1386     EL_UNDEFINED,
1387     EL_UNDEFINED,
1388     -1,
1389     NULL,
1390     NULL,
1391     NULL
1392   }
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int push_delay_fixed, push_delay_random;
1399 }
1400 push_delay_list[] =
1401 {
1402   { EL_SPRING,                  0, 0 },
1403   { EL_BALLOON,                 0, 0 },
1404
1405   { EL_SOKOBAN_OBJECT,          2, 0 },
1406   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1407   { EL_SATELLITE,               2, 0 },
1408   { EL_SP_DISK_YELLOW,          2, 0 },
1409
1410   { EL_UNDEFINED,               0, 0 },
1411 };
1412
1413 struct
1414 {
1415   int element;
1416   int move_stepsize;
1417 }
1418 move_stepsize_list[] =
1419 {
1420   { EL_AMOEBA_DROP,             2 },
1421   { EL_AMOEBA_DROPPING,         2 },
1422   { EL_QUICKSAND_FILLING,       1 },
1423   { EL_QUICKSAND_EMPTYING,      1 },
1424   { EL_QUICKSAND_FAST_FILLING,  2 },
1425   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426   { EL_MAGIC_WALL_FILLING,      2 },
1427   { EL_MAGIC_WALL_EMPTYING,     2 },
1428   { EL_BD_MAGIC_WALL_FILLING,   2 },
1429   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1430   { EL_DC_MAGIC_WALL_FILLING,   2 },
1431   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1432
1433   { EL_UNDEFINED,               0 },
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int count;
1440 }
1441 collect_count_list[] =
1442 {
1443   { EL_EMERALD,                 1 },
1444   { EL_BD_DIAMOND,              1 },
1445   { EL_EMERALD_YELLOW,          1 },
1446   { EL_EMERALD_RED,             1 },
1447   { EL_EMERALD_PURPLE,          1 },
1448   { EL_DIAMOND,                 3 },
1449   { EL_SP_INFOTRON,             1 },
1450   { EL_PEARL,                   5 },
1451   { EL_CRYSTAL,                 8 },
1452
1453   { EL_UNDEFINED,               0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int direction;
1460 }
1461 access_direction_list[] =
1462 {
1463   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1465   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1466   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1469   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1470   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1471   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1472   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1473   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1474
1475   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1476   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1477   { EL_SP_PORT_UP,                                                   MV_DOWN },
1478   { EL_SP_PORT_DOWN,                                         MV_UP           },
1479   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1480   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1481   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1483   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1484   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1486   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1487   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1488   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1489   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1490   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1491   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1492   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1493   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1494
1495   { EL_UNDEFINED,                       MV_NONE                              }
1496 };
1497
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1499
1500 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1503                                  IS_JUST_CHANGING(x, y))
1504
1505 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1506
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1512
1513 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1514                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1515                                      (y) += playfield_scan_delta_y)     \
1516                                 for ((x) = playfield_scan_start_x;      \
1517                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1518                                      (x) += playfield_scan_delta_x)
1519
1520 #ifdef DEBUG
1521 void DEBUG_SetMaximumDynamite()
1522 {
1523   int i;
1524
1525   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527       local_player->inventory_element[local_player->inventory_size++] =
1528         EL_DYNAMITE;
1529 }
1530 #endif
1531
1532 static void InitPlayfieldScanModeVars()
1533 {
1534   if (game.use_reverse_scan_direction)
1535   {
1536     playfield_scan_start_x = lev_fieldx - 1;
1537     playfield_scan_start_y = lev_fieldy - 1;
1538
1539     playfield_scan_delta_x = -1;
1540     playfield_scan_delta_y = -1;
1541   }
1542   else
1543   {
1544     playfield_scan_start_x = 0;
1545     playfield_scan_start_y = 0;
1546
1547     playfield_scan_delta_x = 1;
1548     playfield_scan_delta_y = 1;
1549   }
1550 }
1551
1552 static void InitPlayfieldScanMode(int mode)
1553 {
1554   game.use_reverse_scan_direction =
1555     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1556
1557   InitPlayfieldScanModeVars();
1558 }
1559
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1561 {
1562   move_stepsize =
1563     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1564
1565   /* make sure that stepsize value is always a power of 2 */
1566   move_stepsize = (1 << log_2(move_stepsize));
1567
1568   return TILEX / move_stepsize;
1569 }
1570
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1572                                boolean init_game)
1573 {
1574   int player_nr = player->index_nr;
1575   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1577
1578   /* do no immediately change move delay -- the player might just be moving */
1579   player->move_delay_value_next = move_delay;
1580
1581   /* information if player can move must be set separately */
1582   player->cannot_move = cannot_move;
1583
1584   if (init_game)
1585   {
1586     player->move_delay       = game.initial_move_delay[player_nr];
1587     player->move_delay_value = game.initial_move_delay_value[player_nr];
1588
1589     player->move_delay_value_next = -1;
1590
1591     player->move_delay_reset_counter = 0;
1592   }
1593 }
1594
1595 void GetPlayerConfig()
1596 {
1597   GameFrameDelay = setup.game_frame_delay;
1598
1599   if (!audio.sound_available)
1600     setup.sound_simple = FALSE;
1601
1602   if (!audio.loops_available)
1603     setup.sound_loops = FALSE;
1604
1605   if (!audio.music_available)
1606     setup.sound_music = FALSE;
1607
1608   if (!video.fullscreen_available)
1609     setup.fullscreen = FALSE;
1610
1611   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1612
1613   SetAudioMode(setup.sound);
1614   InitJoysticks();
1615 }
1616
1617 int GetElementFromGroupElement(int element)
1618 {
1619   if (IS_GROUP_ELEMENT(element))
1620   {
1621     struct ElementGroupInfo *group = element_info[element].group;
1622     int last_anim_random_frame = gfx.anim_random_frame;
1623     int element_pos;
1624
1625     if (group->choice_mode == ANIM_RANDOM)
1626       gfx.anim_random_frame = RND(group->num_elements_resolved);
1627
1628     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629                                     group->choice_mode, 0,
1630                                     group->choice_pos);
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = last_anim_random_frame;
1634
1635     group->choice_pos++;
1636
1637     element = group->element_resolved[element_pos];
1638   }
1639
1640   return element;
1641 }
1642
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1644 {
1645   if (element == EL_SP_MURPHY)
1646   {
1647     if (init_game)
1648     {
1649       if (stored_player[0].present)
1650       {
1651         Feld[x][y] = EL_SP_MURPHY_CLONE;
1652
1653         return;
1654       }
1655       else
1656       {
1657         stored_player[0].initial_element = element;
1658         stored_player[0].use_murphy = TRUE;
1659
1660         if (!level.use_artwork_element[0])
1661           stored_player[0].artwork_element = EL_SP_MURPHY;
1662       }
1663
1664       Feld[x][y] = EL_PLAYER_1;
1665     }
1666   }
1667
1668   if (init_game)
1669   {
1670     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671     int jx = player->jx, jy = player->jy;
1672
1673     player->present = TRUE;
1674
1675     player->block_last_field = (element == EL_SP_MURPHY ?
1676                                 level.sp_block_last_field :
1677                                 level.block_last_field);
1678
1679     /* ---------- initialize player's last field block delay --------------- */
1680
1681     /* always start with reliable default value (no adjustment needed) */
1682     player->block_delay_adjustment = 0;
1683
1684     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685     if (player->block_last_field && element == EL_SP_MURPHY)
1686       player->block_delay_adjustment = 1;
1687
1688     /* special case 2: in game engines before 3.1.1, blocking was different */
1689     if (game.use_block_last_field_bug)
1690       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1691
1692     if (!options.network || player->connected)
1693     {
1694       player->active = TRUE;
1695
1696       /* remove potentially duplicate players */
1697       if (StorePlayer[jx][jy] == Feld[x][y])
1698         StorePlayer[jx][jy] = 0;
1699
1700       StorePlayer[x][y] = Feld[x][y];
1701
1702 #if DEBUG_INIT_PLAYER
1703       if (options.debug)
1704       {
1705         printf("- player element %d activated", player->element_nr);
1706         printf(" (local player is %d and currently %s)\n",
1707                local_player->element_nr,
1708                local_player->active ? "active" : "not active");
1709       }
1710     }
1711 #endif
1712
1713     Feld[x][y] = EL_EMPTY;
1714
1715     player->jx = player->last_jx = x;
1716     player->jy = player->last_jy = y;
1717   }
1718
1719   if (!init_game)
1720   {
1721     int player_nr = GET_PLAYER_NR(element);
1722     struct PlayerInfo *player = &stored_player[player_nr];
1723
1724     if (player->active && player->killed)
1725       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1726   }
1727 }
1728
1729 static void InitField(int x, int y, boolean init_game)
1730 {
1731   int element = Feld[x][y];
1732
1733   switch (element)
1734   {
1735     case EL_SP_MURPHY:
1736     case EL_PLAYER_1:
1737     case EL_PLAYER_2:
1738     case EL_PLAYER_3:
1739     case EL_PLAYER_4:
1740       InitPlayerField(x, y, element, init_game);
1741       break;
1742
1743     case EL_SOKOBAN_FIELD_PLAYER:
1744       element = Feld[x][y] = EL_PLAYER_1;
1745       InitField(x, y, init_game);
1746
1747       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748       InitField(x, y, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_EMPTY:
1752       local_player->sokobanfields_still_needed++;
1753       break;
1754
1755     case EL_STONEBLOCK:
1756       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1766       break;
1767
1768     case EL_BUG:
1769     case EL_BUG_RIGHT:
1770     case EL_BUG_UP:
1771     case EL_BUG_LEFT:
1772     case EL_BUG_DOWN:
1773     case EL_SPACESHIP:
1774     case EL_SPACESHIP_RIGHT:
1775     case EL_SPACESHIP_UP:
1776     case EL_SPACESHIP_LEFT:
1777     case EL_SPACESHIP_DOWN:
1778     case EL_BD_BUTTERFLY:
1779     case EL_BD_BUTTERFLY_RIGHT:
1780     case EL_BD_BUTTERFLY_UP:
1781     case EL_BD_BUTTERFLY_LEFT:
1782     case EL_BD_BUTTERFLY_DOWN:
1783     case EL_BD_FIREFLY:
1784     case EL_BD_FIREFLY_RIGHT:
1785     case EL_BD_FIREFLY_UP:
1786     case EL_BD_FIREFLY_LEFT:
1787     case EL_BD_FIREFLY_DOWN:
1788     case EL_PACMAN_RIGHT:
1789     case EL_PACMAN_UP:
1790     case EL_PACMAN_LEFT:
1791     case EL_PACMAN_DOWN:
1792     case EL_YAMYAM:
1793     case EL_YAMYAM_LEFT:
1794     case EL_YAMYAM_RIGHT:
1795     case EL_YAMYAM_UP:
1796     case EL_YAMYAM_DOWN:
1797     case EL_DARK_YAMYAM:
1798     case EL_ROBOT:
1799     case EL_PACMAN:
1800     case EL_SP_SNIKSNAK:
1801     case EL_SP_ELECTRON:
1802     case EL_MOLE:
1803     case EL_MOLE_LEFT:
1804     case EL_MOLE_RIGHT:
1805     case EL_MOLE_UP:
1806     case EL_MOLE_DOWN:
1807       InitMovDir(x, y);
1808       break;
1809
1810     case EL_AMOEBA_FULL:
1811     case EL_BD_AMOEBA:
1812       InitAmoebaNr(x, y);
1813       break;
1814
1815     case EL_AMOEBA_DROP:
1816       if (y == lev_fieldy - 1)
1817       {
1818         Feld[x][y] = EL_AMOEBA_GROWING;
1819         Store[x][y] = EL_AMOEBA_WET;
1820       }
1821       break;
1822
1823     case EL_DYNAMITE_ACTIVE:
1824     case EL_SP_DISK_RED_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829       MovDelay[x][y] = 96;
1830       break;
1831
1832     case EL_EM_DYNAMITE_ACTIVE:
1833       MovDelay[x][y] = 32;
1834       break;
1835
1836     case EL_LAMP:
1837       local_player->lights_still_needed++;
1838       break;
1839
1840     case EL_PENGUIN:
1841       local_player->friends_still_needed++;
1842       break;
1843
1844     case EL_PIG:
1845     case EL_DRAGON:
1846       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1847       break;
1848
1849     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1861       if (init_game)
1862       {
1863         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1866
1867         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1868         {
1869           game.belt_dir[belt_nr] = belt_dir;
1870           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1871         }
1872         else    /* more than one switch -- set it like the first switch */
1873         {
1874           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1875         }
1876       }
1877       break;
1878
1879     case EL_LIGHT_SWITCH_ACTIVE:
1880       if (init_game)
1881         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1882       break;
1883
1884     case EL_INVISIBLE_STEELWALL:
1885     case EL_INVISIBLE_WALL:
1886     case EL_INVISIBLE_SAND:
1887       if (game.light_time_left > 0 ||
1888           game.lenses_time_left > 0)
1889         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1895       break;
1896
1897     case EL_EMC_MAGIC_BALL_SWITCH:
1898       if (game.ball_state)
1899         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1900       break;
1901
1902     case EL_TRIGGER_PLAYER:
1903     case EL_TRIGGER_ELEMENT:
1904     case EL_TRIGGER_CE_VALUE:
1905     case EL_TRIGGER_CE_SCORE:
1906     case EL_SELF:
1907     case EL_ANY_ELEMENT:
1908     case EL_CURRENT_CE_VALUE:
1909     case EL_CURRENT_CE_SCORE:
1910     case EL_PREV_CE_1:
1911     case EL_PREV_CE_2:
1912     case EL_PREV_CE_3:
1913     case EL_PREV_CE_4:
1914     case EL_PREV_CE_5:
1915     case EL_PREV_CE_6:
1916     case EL_PREV_CE_7:
1917     case EL_PREV_CE_8:
1918     case EL_NEXT_CE_1:
1919     case EL_NEXT_CE_2:
1920     case EL_NEXT_CE_3:
1921     case EL_NEXT_CE_4:
1922     case EL_NEXT_CE_5:
1923     case EL_NEXT_CE_6:
1924     case EL_NEXT_CE_7:
1925     case EL_NEXT_CE_8:
1926       /* reference elements should not be used on the playfield */
1927       Feld[x][y] = EL_EMPTY;
1928       break;
1929
1930     default:
1931       if (IS_CUSTOM_ELEMENT(element))
1932       {
1933         if (CAN_MOVE(element))
1934           InitMovDir(x, y);
1935
1936         if (!element_info[element].use_last_ce_value || init_game)
1937           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1938       }
1939       else if (IS_GROUP_ELEMENT(element))
1940       {
1941         Feld[x][y] = GetElementFromGroupElement(element);
1942
1943         InitField(x, y, init_game);
1944       }
1945
1946       break;
1947   }
1948
1949   if (!init_game)
1950     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1951 }
1952
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1954 {
1955   InitField(x, y, init_game);
1956
1957   /* not needed to call InitMovDir() -- already done by InitField()! */
1958   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959       CAN_MOVE(Feld[x][y]))
1960     InitMovDir(x, y);
1961 }
1962
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1964 {
1965   int old_element = Feld[x][y];
1966
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(old_element) &&
1972       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1973     InitMovDir(x, y);
1974
1975   /* this case is in fact a combination of not less than three bugs:
1976      first, it calls InitMovDir() for elements that can move, although this is
1977      already done by InitField(); then, it checks the element that was at this
1978      field _before_ the call to InitField() (which can change it); lastly, it
1979      was not called for "mole with direction" elements, which were treated as
1980      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1981   */
1982 }
1983
1984 static int get_key_element_from_nr(int key_nr)
1985 {
1986   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988                           EL_EM_KEY_1 : EL_KEY_1);
1989
1990   return key_base_element + key_nr;
1991 }
1992
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1994 {
1995   return (player->inventory_size > 0 ?
1996           player->inventory_element[player->inventory_size - 1] :
1997           player->inventory_infinite_element != EL_UNDEFINED ?
1998           player->inventory_infinite_element :
1999           player->dynabombs_left > 0 ?
2000           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2001           EL_UNDEFINED);
2002 }
2003
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2005 {
2006   /* pos >= 0: get element from bottom of the stack;
2007      pos <  0: get element from top of the stack */
2008
2009   if (pos < 0)
2010   {
2011     int min_inventory_size = -pos;
2012     int inventory_pos = player->inventory_size - min_inventory_size;
2013     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2014
2015     return (player->inventory_size >= min_inventory_size ?
2016             player->inventory_element[inventory_pos] :
2017             player->inventory_infinite_element != EL_UNDEFINED ?
2018             player->inventory_infinite_element :
2019             player->dynabombs_left >= min_dynabombs_left ?
2020             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2021             EL_UNDEFINED);
2022   }
2023   else
2024   {
2025     int min_dynabombs_left = pos + 1;
2026     int min_inventory_size = pos + 1 - player->dynabombs_left;
2027     int inventory_pos = pos - player->dynabombs_left;
2028
2029     return (player->inventory_infinite_element != EL_UNDEFINED ?
2030             player->inventory_infinite_element :
2031             player->dynabombs_left >= min_dynabombs_left ?
2032             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033             player->inventory_size >= min_inventory_size ?
2034             player->inventory_element[inventory_pos] :
2035             EL_UNDEFINED);
2036   }
2037 }
2038
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2040 {
2041   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2043   int compare_result;
2044
2045   if (gpo1->sort_priority != gpo2->sort_priority)
2046     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2047   else
2048     compare_result = gpo1->nr - gpo2->nr;
2049
2050   return compare_result;
2051 }
2052
2053 void InitGameControlValues()
2054 {
2055   int i;
2056
2057   for (i = 0; game_panel_controls[i].nr != -1; i++)
2058   {
2059     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2060     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2061     struct TextPosInfo *pos = gpc->pos;
2062     int nr = gpc->nr;
2063     int type = gpc->type;
2064
2065     if (nr != i)
2066     {
2067       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2068       Error(ERR_EXIT, "this should not happen -- please debug");
2069     }
2070
2071     /* force update of game controls after initialization */
2072     gpc->value = gpc->last_value = -1;
2073     gpc->frame = gpc->last_frame = -1;
2074     gpc->gfx_frame = -1;
2075
2076     /* determine panel value width for later calculation of alignment */
2077     if (type == TYPE_INTEGER || type == TYPE_STRING)
2078     {
2079       pos->width = pos->size * getFontWidth(pos->font);
2080       pos->height = getFontHeight(pos->font);
2081     }
2082     else if (type == TYPE_ELEMENT)
2083     {
2084       pos->width = pos->size;
2085       pos->height = pos->size;
2086     }
2087
2088     /* fill structure for game panel draw order */
2089     gpo->nr = gpc->nr;
2090     gpo->sort_priority = pos->sort_priority;
2091   }
2092
2093   /* sort game panel controls according to sort_priority and control number */
2094   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2095         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2096 }
2097
2098 void UpdatePlayfieldElementCount()
2099 {
2100   boolean use_element_count = FALSE;
2101   int i, j, x, y;
2102
2103   /* first check if it is needed at all to calculate playfield element count */
2104   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2105     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2106       use_element_count = TRUE;
2107
2108   if (!use_element_count)
2109     return;
2110
2111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2112     element_info[i].element_count = 0;
2113
2114   SCAN_PLAYFIELD(x, y)
2115   {
2116     element_info[Feld[x][y]].element_count++;
2117   }
2118
2119   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2120     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2121       if (IS_IN_GROUP(j, i))
2122         element_info[EL_GROUP_START + i].element_count +=
2123           element_info[j].element_count;
2124 }
2125
2126 void UpdateGameControlValues()
2127 {
2128   int i, k;
2129   int time = (local_player->LevelSolved ?
2130               local_player->LevelSolved_CountingTime :
2131               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2132               level.native_em_level->lev->time :
2133               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2134               level.native_sp_level->game_sp->time_played :
2135               game.no_time_limit ? TimePlayed : TimeLeft);
2136   int score = (local_player->LevelSolved ?
2137                local_player->LevelSolved_CountingScore :
2138                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139                level.native_em_level->lev->score :
2140                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141                level.native_sp_level->game_sp->score :
2142                local_player->score);
2143   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144               level.native_em_level->lev->required :
2145               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146               level.native_sp_level->game_sp->infotrons_still_needed :
2147               local_player->gems_still_needed);
2148   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149                      level.native_em_level->lev->required > 0 :
2150                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2152                      local_player->gems_still_needed > 0 ||
2153                      local_player->sokobanfields_still_needed > 0 ||
2154                      local_player->lights_still_needed > 0);
2155
2156   UpdatePlayfieldElementCount();
2157
2158   /* update game panel control values */
2159
2160   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2161   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2162
2163   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2164   for (i = 0; i < MAX_NUM_KEYS; i++)
2165     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2167   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2168
2169   if (game.centered_player_nr == -1)
2170   {
2171     for (i = 0; i < MAX_PLAYERS; i++)
2172     {
2173       /* only one player in Supaplex game engine */
2174       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2175         break;
2176
2177       for (k = 0; k < MAX_NUM_KEYS; k++)
2178       {
2179         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2180         {
2181           if (level.native_em_level->ply[i]->keys & (1 << k))
2182             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2183               get_key_element_from_nr(k);
2184         }
2185         else if (stored_player[i].key[k])
2186           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2187             get_key_element_from_nr(k);
2188       }
2189
2190       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2192           level.native_em_level->ply[i]->dynamite;
2193       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2194         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195           level.native_sp_level->game_sp->red_disk_count;
2196       else
2197         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198           stored_player[i].inventory_size;
2199
2200       if (stored_player[i].num_white_keys > 0)
2201         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2202           EL_DC_KEY_WHITE;
2203
2204       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2205         stored_player[i].num_white_keys;
2206     }
2207   }
2208   else
2209   {
2210     int player_nr = game.centered_player_nr;
2211
2212     for (k = 0; k < MAX_NUM_KEYS; k++)
2213     {
2214       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2215       {
2216         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2217           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2218             get_key_element_from_nr(k);
2219       }
2220       else if (stored_player[player_nr].key[k])
2221         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2222           get_key_element_from_nr(k);
2223     }
2224
2225     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2227         level.native_em_level->ply[player_nr]->dynamite;
2228     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2229       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230         level.native_sp_level->game_sp->red_disk_count;
2231     else
2232       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233         stored_player[player_nr].inventory_size;
2234
2235     if (stored_player[player_nr].num_white_keys > 0)
2236       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2237
2238     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2239       stored_player[player_nr].num_white_keys;
2240   }
2241
2242   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2243   {
2244     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, i);
2246     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2247       get_inventory_element_from_pos(local_player, -i - 1);
2248   }
2249
2250   game_panel_controls[GAME_PANEL_SCORE].value = score;
2251   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2252
2253   game_panel_controls[GAME_PANEL_TIME].value = time;
2254
2255   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2256   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2257   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2258
2259   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2260
2261   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265     local_player->shield_normal_time_left;
2266   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2268      EL_EMPTY);
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270     local_player->shield_deadly_time_left;
2271
2272   game_panel_controls[GAME_PANEL_EXIT].value =
2273     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2274
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279      EL_EMC_MAGIC_BALL_SWITCH);
2280
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284     game.light_time_left;
2285
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289     game.timegate_time_left;
2290
2291   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2293
2294   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297     game.lenses_time_left;
2298
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302     game.magnify_time_left;
2303
2304   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2306      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2308      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2309      EL_BALLOON_SWITCH_NONE);
2310
2311   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312     local_player->dynabomb_count;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314     local_player->dynabomb_size;
2315   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2317
2318   game_panel_controls[GAME_PANEL_PENGUINS].value =
2319     local_player->friends_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322     local_player->sokobanfields_still_needed;
2323   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324     local_player->sokobanfields_still_needed;
2325
2326   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2328
2329   for (i = 0; i < NUM_BELTS; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336   }
2337
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341     game.magic_wall_time_left;
2342
2343   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2344     local_player->gravity;
2345
2346   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2347     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2348
2349   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2351       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2352        game.panel.element[i].id : EL_UNDEFINED);
2353
2354   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2355     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2356       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2357        element_info[game.panel.element_count[i].id].element_count : 0);
2358
2359   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2361       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2362        element_info[game.panel.ce_score[i].id].collect_score : 0);
2363
2364   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2365     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2366       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2367        element_info[game.panel.ce_score_element[i].id].collect_score :
2368        EL_UNDEFINED);
2369
2370   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2372   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2373
2374   /* update game panel control frames */
2375
2376   for (i = 0; game_panel_controls[i].nr != -1; i++)
2377   {
2378     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2379
2380     if (gpc->type == TYPE_ELEMENT)
2381     {
2382       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2383       {
2384         int last_anim_random_frame = gfx.anim_random_frame;
2385         int element = gpc->value;
2386         int graphic = el2panelimg(element);
2387
2388         if (gpc->value != gpc->last_value)
2389         {
2390           gpc->gfx_frame = 0;
2391           gpc->gfx_random = INIT_GFX_RANDOM();
2392         }
2393         else
2394         {
2395           gpc->gfx_frame++;
2396
2397           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2398               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2399             gpc->gfx_random = INIT_GFX_RANDOM();
2400         }
2401
2402         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2403           gfx.anim_random_frame = gpc->gfx_random;
2404
2405         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2406           gpc->gfx_frame = element_info[element].collect_score;
2407
2408         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2409                                               gpc->gfx_frame);
2410
2411         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2412           gfx.anim_random_frame = last_anim_random_frame;
2413       }
2414     }
2415   }
2416 }
2417
2418 void DisplayGameControlValues()
2419 {
2420   boolean redraw_panel = FALSE;
2421   int i;
2422
2423   for (i = 0; game_panel_controls[i].nr != -1; i++)
2424   {
2425     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2426
2427     if (PANEL_DEACTIVATED(gpc->pos))
2428       continue;
2429
2430     if (gpc->value == gpc->last_value &&
2431         gpc->frame == gpc->last_frame)
2432       continue;
2433
2434     redraw_panel = TRUE;
2435   }
2436
2437   if (!redraw_panel)
2438     return;
2439
2440   /* copy default game door content to main double buffer */
2441
2442   /* !!! CHECK AGAIN !!! */
2443   SetPanelBackground();
2444   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2445   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2446
2447   /* redraw game control buttons */
2448   RedrawGameButtons();
2449
2450   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2451
2452   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2453   {
2454     int nr = game_panel_order[i].nr;
2455     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2456     struct TextPosInfo *pos = gpc->pos;
2457     int type = gpc->type;
2458     int value = gpc->value;
2459     int frame = gpc->frame;
2460     int size = pos->size;
2461     int font = pos->font;
2462     boolean draw_masked = pos->draw_masked;
2463     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2464
2465     if (PANEL_DEACTIVATED(pos))
2466       continue;
2467
2468     gpc->last_value = value;
2469     gpc->last_frame = frame;
2470
2471     if (type == TYPE_INTEGER)
2472     {
2473       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2474           nr == GAME_PANEL_TIME)
2475       {
2476         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2477
2478         if (use_dynamic_size)           /* use dynamic number of digits */
2479         {
2480           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2481           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2482           int size2 = size1 + 1;
2483           int font1 = pos->font;
2484           int font2 = pos->font_alt;
2485
2486           size = (value < value_change ? size1 : size2);
2487           font = (value < value_change ? font1 : font2);
2488         }
2489       }
2490
2491       /* correct text size if "digits" is zero or less */
2492       if (size <= 0)
2493         size = strlen(int2str(value, size));
2494
2495       /* dynamically correct text alignment */
2496       pos->width = size * getFontWidth(font);
2497
2498       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2499                   int2str(value, size), font, mask_mode);
2500     }
2501     else if (type == TYPE_ELEMENT)
2502     {
2503       int element, graphic;
2504       Bitmap *src_bitmap;
2505       int src_x, src_y;
2506       int width, height;
2507       int dst_x = PANEL_XPOS(pos);
2508       int dst_y = PANEL_YPOS(pos);
2509
2510       if (value != EL_UNDEFINED && value != EL_EMPTY)
2511       {
2512         element = value;
2513         graphic = el2panelimg(value);
2514
2515         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2516
2517         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2518           size = TILESIZE;
2519
2520         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2521                               &src_x, &src_y);
2522
2523         width  = graphic_info[graphic].width  * size / TILESIZE;
2524         height = graphic_info[graphic].height * size / TILESIZE;
2525
2526         if (draw_masked)
2527           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2528                            dst_x, dst_y);
2529         else
2530           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2531                      dst_x, dst_y);
2532       }
2533     }
2534     else if (type == TYPE_STRING)
2535     {
2536       boolean active = (value != 0);
2537       char *state_normal = "off";
2538       char *state_active = "on";
2539       char *state = (active ? state_active : state_normal);
2540       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2541                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2542                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2543                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2544
2545       if (nr == GAME_PANEL_GRAVITY_STATE)
2546       {
2547         int font1 = pos->font;          /* (used for normal state) */
2548         int font2 = pos->font_alt;      /* (used for active state) */
2549
2550         font = (active ? font2 : font1);
2551       }
2552
2553       if (s != NULL)
2554       {
2555         char *s_cut;
2556
2557         if (size <= 0)
2558         {
2559           /* don't truncate output if "chars" is zero or less */
2560           size = strlen(s);
2561
2562           /* dynamically correct text alignment */
2563           pos->width = size * getFontWidth(font);
2564         }
2565
2566         s_cut = getStringCopyN(s, size);
2567
2568         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2569                     s_cut, font, mask_mode);
2570
2571         free(s_cut);
2572       }
2573     }
2574
2575     redraw_mask |= REDRAW_DOOR_1;
2576   }
2577
2578   SetGameStatus(GAME_MODE_PLAYING);
2579 }
2580
2581 void UpdateAndDisplayGameControlValues()
2582 {
2583   if (tape.deactivate_display)
2584     return;
2585
2586   UpdateGameControlValues();
2587   DisplayGameControlValues();
2588 }
2589
2590 void UpdateGameDoorValues()
2591 {
2592   UpdateGameControlValues();
2593 }
2594
2595 void DrawGameDoorValues()
2596 {
2597   DisplayGameControlValues();
2598 }
2599
2600
2601 /*
2602   =============================================================================
2603   InitGameEngine()
2604   -----------------------------------------------------------------------------
2605   initialize game engine due to level / tape version number
2606   =============================================================================
2607 */
2608
2609 static void InitGameEngine()
2610 {
2611   int i, j, k, l, x, y;
2612
2613   /* set game engine from tape file when re-playing, else from level file */
2614   game.engine_version = (tape.playing ? tape.engine_version :
2615                          level.game_version);
2616
2617   /* set single or multi-player game mode (needed for re-playing tapes) */
2618   game.team_mode = setup.team_mode;
2619
2620   if (tape.playing)
2621   {
2622     int num_players = 0;
2623
2624     for (i = 0; i < MAX_PLAYERS; i++)
2625       if (tape.player_participates[i])
2626         num_players++;
2627
2628     /* multi-player tapes contain input data for more than one player */
2629     game.team_mode = (num_players > 1);
2630   }
2631
2632   /* ---------------------------------------------------------------------- */
2633   /* set flags for bugs and changes according to active game engine version */
2634   /* ---------------------------------------------------------------------- */
2635
2636   /*
2637     Summary of bugfix/change:
2638     Fixed handling for custom elements that change when pushed by the player.
2639
2640     Fixed/changed in version:
2641     3.1.0
2642
2643     Description:
2644     Before 3.1.0, custom elements that "change when pushing" changed directly
2645     after the player started pushing them (until then handled in "DigField()").
2646     Since 3.1.0, these custom elements are not changed until the "pushing"
2647     move of the element is finished (now handled in "ContinueMoving()").
2648
2649     Affected levels/tapes:
2650     The first condition is generally needed for all levels/tapes before version
2651     3.1.0, which might use the old behaviour before it was changed; known tapes
2652     that are affected are some tapes from the level set "Walpurgis Gardens" by
2653     Jamie Cullen.
2654     The second condition is an exception from the above case and is needed for
2655     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2656     above (including some development versions of 3.1.0), but before it was
2657     known that this change would break tapes like the above and was fixed in
2658     3.1.1, so that the changed behaviour was active although the engine version
2659     while recording maybe was before 3.1.0. There is at least one tape that is
2660     affected by this exception, which is the tape for the one-level set "Bug
2661     Machine" by Juergen Bonhagen.
2662   */
2663
2664   game.use_change_when_pushing_bug =
2665     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2666      !(tape.playing &&
2667        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2668        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2669
2670   /*
2671     Summary of bugfix/change:
2672     Fixed handling for blocking the field the player leaves when moving.
2673
2674     Fixed/changed in version:
2675     3.1.1
2676
2677     Description:
2678     Before 3.1.1, when "block last field when moving" was enabled, the field
2679     the player is leaving when moving was blocked for the time of the move,
2680     and was directly unblocked afterwards. This resulted in the last field
2681     being blocked for exactly one less than the number of frames of one player
2682     move. Additionally, even when blocking was disabled, the last field was
2683     blocked for exactly one frame.
2684     Since 3.1.1, due to changes in player movement handling, the last field
2685     is not blocked at all when blocking is disabled. When blocking is enabled,
2686     the last field is blocked for exactly the number of frames of one player
2687     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2688     last field is blocked for exactly one more than the number of frames of
2689     one player move.
2690
2691     Affected levels/tapes:
2692     (!!! yet to be determined -- probably many !!!)
2693   */
2694
2695   game.use_block_last_field_bug =
2696     (game.engine_version < VERSION_IDENT(3,1,1,0));
2697
2698   /* ---------------------------------------------------------------------- */
2699
2700   /* set maximal allowed number of custom element changes per game frame */
2701   game.max_num_changes_per_frame = 1;
2702
2703   /* default scan direction: scan playfield from top/left to bottom/right */
2704   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2705
2706   /* dynamically adjust element properties according to game engine version */
2707   InitElementPropertiesEngine(game.engine_version);
2708
2709 #if 0
2710   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2711   printf("          tape version == %06d [%s] [file: %06d]\n",
2712          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2713          tape.file_version);
2714   printf("       => game.engine_version == %06d\n", game.engine_version);
2715 #endif
2716
2717   /* ---------- initialize player's initial move delay --------------------- */
2718
2719   /* dynamically adjust player properties according to level information */
2720   for (i = 0; i < MAX_PLAYERS; i++)
2721     game.initial_move_delay_value[i] =
2722       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2723
2724   /* dynamically adjust player properties according to game engine version */
2725   for (i = 0; i < MAX_PLAYERS; i++)
2726     game.initial_move_delay[i] =
2727       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2728        game.initial_move_delay_value[i] : 0);
2729
2730   /* ---------- initialize player's initial push delay --------------------- */
2731
2732   /* dynamically adjust player properties according to game engine version */
2733   game.initial_push_delay_value =
2734     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2735
2736   /* ---------- initialize changing elements ------------------------------- */
2737
2738   /* initialize changing elements information */
2739   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2740   {
2741     struct ElementInfo *ei = &element_info[i];
2742
2743     /* this pointer might have been changed in the level editor */
2744     ei->change = &ei->change_page[0];
2745
2746     if (!IS_CUSTOM_ELEMENT(i))
2747     {
2748       ei->change->target_element = EL_EMPTY_SPACE;
2749       ei->change->delay_fixed = 0;
2750       ei->change->delay_random = 0;
2751       ei->change->delay_frames = 1;
2752     }
2753
2754     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2755     {
2756       ei->has_change_event[j] = FALSE;
2757
2758       ei->event_page_nr[j] = 0;
2759       ei->event_page[j] = &ei->change_page[0];
2760     }
2761   }
2762
2763   /* add changing elements from pre-defined list */
2764   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2765   {
2766     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2767     struct ElementInfo *ei = &element_info[ch_delay->element];
2768
2769     ei->change->target_element       = ch_delay->target_element;
2770     ei->change->delay_fixed          = ch_delay->change_delay;
2771
2772     ei->change->pre_change_function  = ch_delay->pre_change_function;
2773     ei->change->change_function      = ch_delay->change_function;
2774     ei->change->post_change_function = ch_delay->post_change_function;
2775
2776     ei->change->can_change = TRUE;
2777     ei->change->can_change_or_has_action = TRUE;
2778
2779     ei->has_change_event[CE_DELAY] = TRUE;
2780
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2782     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2783   }
2784
2785   /* ---------- initialize internal run-time variables --------------------- */
2786
2787   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2788   {
2789     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2790
2791     for (j = 0; j < ei->num_change_pages; j++)
2792     {
2793       ei->change_page[j].can_change_or_has_action =
2794         (ei->change_page[j].can_change |
2795          ei->change_page[j].has_action);
2796     }
2797   }
2798
2799   /* add change events from custom element configuration */
2800   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2801   {
2802     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2803
2804     for (j = 0; j < ei->num_change_pages; j++)
2805     {
2806       if (!ei->change_page[j].can_change_or_has_action)
2807         continue;
2808
2809       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2810       {
2811         /* only add event page for the first page found with this event */
2812         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2813         {
2814           ei->has_change_event[k] = TRUE;
2815
2816           ei->event_page_nr[k] = j;
2817           ei->event_page[k] = &ei->change_page[j];
2818         }
2819       }
2820     }
2821   }
2822
2823   /* ---------- initialize reference elements in change conditions --------- */
2824
2825   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2826   {
2827     int element = EL_CUSTOM_START + i;
2828     struct ElementInfo *ei = &element_info[element];
2829
2830     for (j = 0; j < ei->num_change_pages; j++)
2831     {
2832       int trigger_element = ei->change_page[j].initial_trigger_element;
2833
2834       if (trigger_element >= EL_PREV_CE_8 &&
2835           trigger_element <= EL_NEXT_CE_8)
2836         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2837
2838       ei->change_page[j].trigger_element = trigger_element;
2839     }
2840   }
2841
2842   /* ---------- initialize run-time trigger player and element ------------- */
2843
2844   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2845   {
2846     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2847
2848     for (j = 0; j < ei->num_change_pages; j++)
2849     {
2850       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2852       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2853       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2854       ei->change_page[j].actual_trigger_ce_value = 0;
2855       ei->change_page[j].actual_trigger_ce_score = 0;
2856     }
2857   }
2858
2859   /* ---------- initialize trigger events ---------------------------------- */
2860
2861   /* initialize trigger events information */
2862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2864       trigger_events[i][j] = FALSE;
2865
2866   /* add trigger events from element change event properties */
2867   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2868   {
2869     struct ElementInfo *ei = &element_info[i];
2870
2871     for (j = 0; j < ei->num_change_pages; j++)
2872     {
2873       if (!ei->change_page[j].can_change_or_has_action)
2874         continue;
2875
2876       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2877       {
2878         int trigger_element = ei->change_page[j].trigger_element;
2879
2880         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2881         {
2882           if (ei->change_page[j].has_event[k])
2883           {
2884             if (IS_GROUP_ELEMENT(trigger_element))
2885             {
2886               struct ElementGroupInfo *group =
2887                 element_info[trigger_element].group;
2888
2889               for (l = 0; l < group->num_elements_resolved; l++)
2890                 trigger_events[group->element_resolved[l]][k] = TRUE;
2891             }
2892             else if (trigger_element == EL_ANY_ELEMENT)
2893               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2894                 trigger_events[l][k] = TRUE;
2895             else
2896               trigger_events[trigger_element][k] = TRUE;
2897           }
2898         }
2899       }
2900     }
2901   }
2902
2903   /* ---------- initialize push delay -------------------------------------- */
2904
2905   /* initialize push delay values to default */
2906   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2907   {
2908     if (!IS_CUSTOM_ELEMENT(i))
2909     {
2910       /* set default push delay values (corrected since version 3.0.7-1) */
2911       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2912       {
2913         element_info[i].push_delay_fixed = 2;
2914         element_info[i].push_delay_random = 8;
2915       }
2916       else
2917       {
2918         element_info[i].push_delay_fixed = 8;
2919         element_info[i].push_delay_random = 8;
2920       }
2921     }
2922   }
2923
2924   /* set push delay value for certain elements from pre-defined list */
2925   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2926   {
2927     int e = push_delay_list[i].element;
2928
2929     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2930     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2931   }
2932
2933   /* set push delay value for Supaplex elements for newer engine versions */
2934   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2935   {
2936     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2937     {
2938       if (IS_SP_ELEMENT(i))
2939       {
2940         /* set SP push delay to just enough to push under a falling zonk */
2941         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2942
2943         element_info[i].push_delay_fixed  = delay;
2944         element_info[i].push_delay_random = 0;
2945       }
2946     }
2947   }
2948
2949   /* ---------- initialize move stepsize ----------------------------------- */
2950
2951   /* initialize move stepsize values to default */
2952   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953     if (!IS_CUSTOM_ELEMENT(i))
2954       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2955
2956   /* set move stepsize value for certain elements from pre-defined list */
2957   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2958   {
2959     int e = move_stepsize_list[i].element;
2960
2961     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2962   }
2963
2964   /* ---------- initialize collect score ----------------------------------- */
2965
2966   /* initialize collect score values for custom elements from initial value */
2967   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2968     if (IS_CUSTOM_ELEMENT(i))
2969       element_info[i].collect_score = element_info[i].collect_score_initial;
2970
2971   /* ---------- initialize collect count ----------------------------------- */
2972
2973   /* initialize collect count values for non-custom elements */
2974   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2975     if (!IS_CUSTOM_ELEMENT(i))
2976       element_info[i].collect_count_initial = 0;
2977
2978   /* add collect count values for all elements from pre-defined list */
2979   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2980     element_info[collect_count_list[i].element].collect_count_initial =
2981       collect_count_list[i].count;
2982
2983   /* ---------- initialize access direction -------------------------------- */
2984
2985   /* initialize access direction values to default (access from every side) */
2986   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2987     if (!IS_CUSTOM_ELEMENT(i))
2988       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2989
2990   /* set access direction value for certain elements from pre-defined list */
2991   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2992     element_info[access_direction_list[i].element].access_direction =
2993       access_direction_list[i].direction;
2994
2995   /* ---------- initialize explosion content ------------------------------- */
2996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2997   {
2998     if (IS_CUSTOM_ELEMENT(i))
2999       continue;
3000
3001     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3002     {
3003       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3004
3005       element_info[i].content.e[x][y] =
3006         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3007          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3008          i == EL_PLAYER_3 ? EL_EMERALD :
3009          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3010          i == EL_MOLE ? EL_EMERALD_RED :
3011          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3012          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3013          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3014          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3015          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3016          i == EL_WALL_EMERALD ? EL_EMERALD :
3017          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3018          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3019          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3020          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3021          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3022          i == EL_WALL_PEARL ? EL_PEARL :
3023          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3024          EL_EMPTY);
3025     }
3026   }
3027
3028   /* ---------- initialize recursion detection ------------------------------ */
3029   recursion_loop_depth = 0;
3030   recursion_loop_detected = FALSE;
3031   recursion_loop_element = EL_UNDEFINED;
3032
3033   /* ---------- initialize graphics engine ---------------------------------- */
3034   game.scroll_delay_value =
3035     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3036      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3037   game.scroll_delay_value =
3038     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3039
3040   /* ---------- initialize game engine snapshots ---------------------------- */
3041   for (i = 0; i < MAX_PLAYERS; i++)
3042     game.snapshot.last_action[i] = 0;
3043   game.snapshot.changed_action = FALSE;
3044   game.snapshot.collected_item = FALSE;
3045   game.snapshot.mode =
3046     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3047      SNAPSHOT_MODE_EVERY_STEP :
3048      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3049      SNAPSHOT_MODE_EVERY_MOVE :
3050      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3051      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3052 }
3053
3054 int get_num_special_action(int element, int action_first, int action_last)
3055 {
3056   int num_special_action = 0;
3057   int i, j;
3058
3059   for (i = action_first; i <= action_last; i++)
3060   {
3061     boolean found = FALSE;
3062
3063     for (j = 0; j < NUM_DIRECTIONS; j++)
3064       if (el_act_dir2img(element, i, j) !=
3065           el_act_dir2img(element, ACTION_DEFAULT, j))
3066         found = TRUE;
3067
3068     if (found)
3069       num_special_action++;
3070     else
3071       break;
3072   }
3073
3074   return num_special_action;
3075 }
3076
3077
3078 /*
3079   =============================================================================
3080   InitGame()
3081   -----------------------------------------------------------------------------
3082   initialize and start new game
3083   =============================================================================
3084 */
3085
3086 void InitGame()
3087 {
3088   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3089   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3090   int fade_mask = REDRAW_FIELD;
3091
3092   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3093   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3094   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3095   int initial_move_dir = MV_DOWN;
3096   int i, j, x, y;
3097
3098   // required here to update video display before fading (FIX THIS)
3099   DrawMaskedBorder(REDRAW_DOOR_2);
3100
3101   if (!game.restart_level)
3102     CloseDoor(DOOR_CLOSE_1);
3103
3104   SetGameStatus(GAME_MODE_PLAYING);
3105
3106   if (level_editor_test_game)
3107     FadeSkipNextFadeIn();
3108   else
3109     FadeSetEnterScreen();
3110
3111   if (CheckIfGlobalBorderHasChanged())
3112     fade_mask = REDRAW_ALL;
3113
3114   FadeSoundsAndMusic();
3115
3116   ExpireSoundLoops(TRUE);
3117
3118   FadeOut(fade_mask);
3119
3120   /* needed if different viewport properties defined for playing */
3121   ChangeViewportPropertiesIfNeeded();
3122
3123   ClearField();
3124
3125   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3126
3127   DrawCompleteVideoDisplay();
3128
3129   InitGameEngine();
3130   InitGameControlValues();
3131
3132   /* don't play tapes over network */
3133   network_playing = (options.network && !tape.playing);
3134
3135   for (i = 0; i < MAX_PLAYERS; i++)
3136   {
3137     struct PlayerInfo *player = &stored_player[i];
3138
3139     player->index_nr = i;
3140     player->index_bit = (1 << i);
3141     player->element_nr = EL_PLAYER_1 + i;
3142
3143     player->present = FALSE;
3144     player->active = FALSE;
3145     player->mapped = FALSE;
3146
3147     player->killed = FALSE;
3148     player->reanimated = FALSE;
3149
3150     player->action = 0;
3151     player->effective_action = 0;
3152     player->programmed_action = 0;
3153
3154     player->score = 0;
3155     player->score_final = 0;
3156
3157     player->gems_still_needed = level.gems_needed;
3158     player->sokobanfields_still_needed = 0;
3159     player->lights_still_needed = 0;
3160     player->friends_still_needed = 0;
3161
3162     for (j = 0; j < MAX_NUM_KEYS; j++)
3163       player->key[j] = FALSE;
3164
3165     player->num_white_keys = 0;
3166
3167     player->dynabomb_count = 0;
3168     player->dynabomb_size = 1;
3169     player->dynabombs_left = 0;
3170     player->dynabomb_xl = FALSE;
3171
3172     player->MovDir = initial_move_dir;
3173     player->MovPos = 0;
3174     player->GfxPos = 0;
3175     player->GfxDir = initial_move_dir;
3176     player->GfxAction = ACTION_DEFAULT;
3177     player->Frame = 0;
3178     player->StepFrame = 0;
3179
3180     player->initial_element = player->element_nr;
3181     player->artwork_element =
3182       (level.use_artwork_element[i] ? level.artwork_element[i] :
3183        player->element_nr);
3184     player->use_murphy = FALSE;
3185
3186     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3187     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3188
3189     player->gravity = level.initial_player_gravity[i];
3190
3191     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3192
3193     player->actual_frame_counter = 0;
3194
3195     player->step_counter = 0;
3196
3197     player->last_move_dir = initial_move_dir;
3198
3199     player->is_active = FALSE;
3200
3201     player->is_waiting = FALSE;
3202     player->is_moving = FALSE;
3203     player->is_auto_moving = FALSE;
3204     player->is_digging = FALSE;
3205     player->is_snapping = FALSE;
3206     player->is_collecting = FALSE;
3207     player->is_pushing = FALSE;
3208     player->is_switching = FALSE;
3209     player->is_dropping = FALSE;
3210     player->is_dropping_pressed = FALSE;
3211
3212     player->is_bored = FALSE;
3213     player->is_sleeping = FALSE;
3214
3215     player->frame_counter_bored = -1;
3216     player->frame_counter_sleeping = -1;
3217
3218     player->anim_delay_counter = 0;
3219     player->post_delay_counter = 0;
3220
3221     player->dir_waiting = initial_move_dir;
3222     player->action_waiting = ACTION_DEFAULT;
3223     player->last_action_waiting = ACTION_DEFAULT;
3224     player->special_action_bored = ACTION_DEFAULT;
3225     player->special_action_sleeping = ACTION_DEFAULT;
3226
3227     player->switch_x = -1;
3228     player->switch_y = -1;
3229
3230     player->drop_x = -1;
3231     player->drop_y = -1;
3232
3233     player->show_envelope = 0;
3234
3235     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3236
3237     player->push_delay       = -1;      /* initialized when pushing starts */
3238     player->push_delay_value = game.initial_push_delay_value;
3239
3240     player->drop_delay = 0;
3241     player->drop_pressed_delay = 0;
3242
3243     player->last_jx = -1;
3244     player->last_jy = -1;
3245     player->jx = -1;
3246     player->jy = -1;
3247
3248     player->shield_normal_time_left = 0;
3249     player->shield_deadly_time_left = 0;
3250
3251     player->inventory_infinite_element = EL_UNDEFINED;
3252     player->inventory_size = 0;
3253
3254     if (level.use_initial_inventory[i])
3255     {
3256       for (j = 0; j < level.initial_inventory_size[i]; j++)
3257       {
3258         int element = level.initial_inventory_content[i][j];
3259         int collect_count = element_info[element].collect_count_initial;
3260         int k;
3261
3262         if (!IS_CUSTOM_ELEMENT(element))
3263           collect_count = 1;
3264
3265         if (collect_count == 0)
3266           player->inventory_infinite_element = element;
3267         else
3268           for (k = 0; k < collect_count; k++)
3269             if (player->inventory_size < MAX_INVENTORY_SIZE)
3270               player->inventory_element[player->inventory_size++] = element;
3271       }
3272     }
3273
3274     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3275     SnapField(player, 0, 0);
3276
3277     player->LevelSolved = FALSE;
3278     player->GameOver = FALSE;
3279
3280     player->LevelSolved_GameWon = FALSE;
3281     player->LevelSolved_GameEnd = FALSE;
3282     player->LevelSolved_PanelOff = FALSE;
3283     player->LevelSolved_SaveTape = FALSE;
3284     player->LevelSolved_SaveScore = FALSE;
3285     player->LevelSolved_CountingTime = 0;
3286     player->LevelSolved_CountingScore = 0;
3287
3288     map_player_action[i] = i;
3289   }
3290
3291   network_player_action_received = FALSE;
3292
3293 #if defined(NETWORK_AVALIABLE)
3294   /* initial null action */
3295   if (network_playing)
3296     SendToServer_MovePlayer(MV_NONE);
3297 #endif
3298
3299   ZX = ZY = -1;
3300   ExitX = ExitY = -1;
3301
3302   FrameCounter = 0;
3303   TimeFrames = 0;
3304   TimePlayed = 0;
3305   TimeLeft = level.time;
3306   TapeTime = 0;
3307
3308   ScreenMovDir = MV_NONE;
3309   ScreenMovPos = 0;
3310   ScreenGfxPos = 0;
3311
3312   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3313
3314   AllPlayersGone = FALSE;
3315
3316   game.no_time_limit = (level.time == 0);
3317
3318   game.yamyam_content_nr = 0;
3319   game.robot_wheel_active = FALSE;
3320   game.magic_wall_active = FALSE;
3321   game.magic_wall_time_left = 0;
3322   game.light_time_left = 0;
3323   game.timegate_time_left = 0;
3324   game.switchgate_pos = 0;
3325   game.wind_direction = level.wind_direction_initial;
3326
3327   game.lenses_time_left = 0;
3328   game.magnify_time_left = 0;
3329
3330   game.ball_state = level.ball_state_initial;
3331   game.ball_content_nr = 0;
3332
3333   game.envelope_active = FALSE;
3334
3335   /* set focus to local player for network games, else to all players */
3336   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3337   game.centered_player_nr_next = game.centered_player_nr;
3338   game.set_centered_player = FALSE;
3339
3340   if (network_playing && tape.recording)
3341   {
3342     /* store client dependent player focus when recording network games */
3343     tape.centered_player_nr_next = game.centered_player_nr_next;
3344     tape.set_centered_player = TRUE;
3345   }
3346
3347   for (i = 0; i < NUM_BELTS; i++)
3348   {
3349     game.belt_dir[i] = MV_NONE;
3350     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3351   }
3352
3353   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3354     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3355
3356 #if DEBUG_INIT_PLAYER
3357   if (options.debug)
3358   {
3359     printf("Player status at level initialization:\n");
3360   }
3361 #endif
3362
3363   SCAN_PLAYFIELD(x, y)
3364   {
3365     Feld[x][y] = level.field[x][y];
3366     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3367     ChangeDelay[x][y] = 0;
3368     ChangePage[x][y] = -1;
3369     CustomValue[x][y] = 0;              /* initialized in InitField() */
3370     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3371     AmoebaNr[x][y] = 0;
3372     WasJustMoving[x][y] = 0;
3373     WasJustFalling[x][y] = 0;
3374     CheckCollision[x][y] = 0;
3375     CheckImpact[x][y] = 0;
3376     Stop[x][y] = FALSE;
3377     Pushed[x][y] = FALSE;
3378
3379     ChangeCount[x][y] = 0;
3380     ChangeEvent[x][y] = -1;
3381
3382     ExplodePhase[x][y] = 0;
3383     ExplodeDelay[x][y] = 0;
3384     ExplodeField[x][y] = EX_TYPE_NONE;
3385
3386     RunnerVisit[x][y] = 0;
3387     PlayerVisit[x][y] = 0;
3388
3389     GfxFrame[x][y] = 0;
3390     GfxRandom[x][y] = INIT_GFX_RANDOM();
3391     GfxElement[x][y] = EL_UNDEFINED;
3392     GfxAction[x][y] = ACTION_DEFAULT;
3393     GfxDir[x][y] = MV_NONE;
3394     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3395   }
3396
3397   SCAN_PLAYFIELD(x, y)
3398   {
3399     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3400       emulate_bd = FALSE;
3401     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3402       emulate_sb = FALSE;
3403     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3404       emulate_sp = FALSE;
3405
3406     InitField(x, y, TRUE);
3407
3408     ResetGfxAnimation(x, y);
3409   }
3410
3411   InitBeltMovement();
3412
3413   for (i = 0; i < MAX_PLAYERS; i++)
3414   {
3415     struct PlayerInfo *player = &stored_player[i];
3416
3417     /* set number of special actions for bored and sleeping animation */
3418     player->num_special_action_bored =
3419       get_num_special_action(player->artwork_element,
3420                              ACTION_BORING_1, ACTION_BORING_LAST);
3421     player->num_special_action_sleeping =
3422       get_num_special_action(player->artwork_element,
3423                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3424   }
3425
3426   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3427                     emulate_sb ? EMU_SOKOBAN :
3428                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3429
3430   /* initialize type of slippery elements */
3431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432   {
3433     if (!IS_CUSTOM_ELEMENT(i))
3434     {
3435       /* default: elements slip down either to the left or right randomly */
3436       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3437
3438       /* SP style elements prefer to slip down on the left side */
3439       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3440         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3441
3442       /* BD style elements prefer to slip down on the left side */
3443       if (game.emulation == EMU_BOULDERDASH)
3444         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3445     }
3446   }
3447
3448   /* initialize explosion and ignition delay */
3449   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3450   {
3451     if (!IS_CUSTOM_ELEMENT(i))
3452     {
3453       int num_phase = 8;
3454       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3455                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3456                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3457       int last_phase = (num_phase + 1) * delay;
3458       int half_phase = (num_phase / 2) * delay;
3459
3460       element_info[i].explosion_delay = last_phase - 1;
3461       element_info[i].ignition_delay = half_phase;
3462
3463       if (i == EL_BLACK_ORB)
3464         element_info[i].ignition_delay = 1;
3465     }
3466   }
3467
3468   /* correct non-moving belts to start moving left */
3469   for (i = 0; i < NUM_BELTS; i++)
3470     if (game.belt_dir[i] == MV_NONE)
3471       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3472
3473 #if USE_NEW_PLAYER_ASSIGNMENTS
3474   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3475   /* choose default local player */
3476   local_player = &stored_player[0];
3477
3478   for (i = 0; i < MAX_PLAYERS; i++)
3479     stored_player[i].connected = FALSE;
3480
3481   local_player->connected = TRUE;
3482   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3483
3484   if (tape.playing)
3485   {
3486     for (i = 0; i < MAX_PLAYERS; i++)
3487       stored_player[i].connected = tape.player_participates[i];
3488   }
3489   else if (game.team_mode && !options.network)
3490   {
3491     /* try to guess locally connected team mode players (needed for correct
3492        assignment of player figures from level to locally playing players) */
3493
3494     for (i = 0; i < MAX_PLAYERS; i++)
3495       if (setup.input[i].use_joystick ||
3496           setup.input[i].key.left != KSYM_UNDEFINED)
3497         stored_player[i].connected = TRUE;
3498   }
3499
3500 #if DEBUG_INIT_PLAYER
3501   if (options.debug)
3502   {
3503     printf("Player status after level initialization:\n");
3504
3505     for (i = 0; i < MAX_PLAYERS; i++)
3506     {
3507       struct PlayerInfo *player = &stored_player[i];
3508
3509       printf("- player %d: present == %d, connected == %d, active == %d",
3510              i + 1,
3511              player->present,
3512              player->connected,
3513              player->active);
3514
3515       if (local_player == player)
3516         printf(" (local player)");
3517
3518       printf("\n");
3519     }
3520   }
3521 #endif
3522
3523 #if DEBUG_INIT_PLAYER
3524   if (options.debug)
3525     printf("Reassigning players ...\n");
3526 #endif
3527
3528   /* check if any connected player was not found in playfield */
3529   for (i = 0; i < MAX_PLAYERS; i++)
3530   {
3531     struct PlayerInfo *player = &stored_player[i];
3532
3533     if (player->connected && !player->present)
3534     {
3535       struct PlayerInfo *field_player = NULL;
3536
3537 #if DEBUG_INIT_PLAYER
3538       if (options.debug)
3539         printf("- looking for field player for player %d ...\n", i + 1);
3540 #endif
3541
3542       /* assign first free player found that is present in the playfield */
3543
3544       /* first try: look for unmapped playfield player that is not connected */
3545       for (j = 0; j < MAX_PLAYERS; j++)
3546         if (field_player == NULL &&
3547             stored_player[j].present &&
3548             !stored_player[j].mapped &&
3549             !stored_player[j].connected)
3550           field_player = &stored_player[j];
3551
3552       /* second try: look for *any* unmapped playfield player */
3553       for (j = 0; j < MAX_PLAYERS; j++)
3554         if (field_player == NULL &&
3555             stored_player[j].present &&
3556             !stored_player[j].mapped)
3557           field_player = &stored_player[j];
3558
3559       if (field_player != NULL)
3560       {
3561         int jx = field_player->jx, jy = field_player->jy;
3562
3563 #if DEBUG_INIT_PLAYER
3564         if (options.debug)
3565           printf("- found player %d\n", field_player->index_nr + 1);
3566 #endif
3567
3568         player->present = FALSE;
3569         player->active = FALSE;
3570
3571         field_player->present = TRUE;
3572         field_player->active = TRUE;
3573
3574         /*
3575         player->initial_element = field_player->initial_element;
3576         player->artwork_element = field_player->artwork_element;
3577
3578         player->block_last_field       = field_player->block_last_field;
3579         player->block_delay_adjustment = field_player->block_delay_adjustment;
3580         */
3581
3582         StorePlayer[jx][jy] = field_player->element_nr;
3583
3584         field_player->jx = field_player->last_jx = jx;
3585         field_player->jy = field_player->last_jy = jy;
3586
3587         if (local_player == player)
3588           local_player = field_player;
3589
3590         map_player_action[field_player->index_nr] = i;
3591
3592         field_player->mapped = TRUE;
3593
3594 #if DEBUG_INIT_PLAYER
3595         if (options.debug)
3596           printf("- map_player_action[%d] == %d\n",
3597                  field_player->index_nr + 1, i + 1);
3598 #endif
3599       }
3600     }
3601
3602     if (player->connected && player->present)
3603       player->mapped = TRUE;
3604   }
3605
3606 #if DEBUG_INIT_PLAYER
3607   if (options.debug)
3608   {
3609     printf("Player status after player assignment (first stage):\n");
3610
3611     for (i = 0; i < MAX_PLAYERS; i++)
3612     {
3613       struct PlayerInfo *player = &stored_player[i];
3614
3615       printf("- player %d: present == %d, connected == %d, active == %d",
3616              i + 1,
3617              player->present,
3618              player->connected,
3619              player->active);
3620
3621       if (local_player == player)
3622         printf(" (local player)");
3623
3624       printf("\n");
3625     }
3626   }
3627 #endif
3628
3629 #else
3630
3631   /* check if any connected player was not found in playfield */
3632   for (i = 0; i < MAX_PLAYERS; i++)
3633   {
3634     struct PlayerInfo *player = &stored_player[i];
3635
3636     if (player->connected && !player->present)
3637     {
3638       for (j = 0; j < MAX_PLAYERS; j++)
3639       {
3640         struct PlayerInfo *field_player = &stored_player[j];
3641         int jx = field_player->jx, jy = field_player->jy;
3642
3643         /* assign first free player found that is present in the playfield */
3644         if (field_player->present && !field_player->connected)
3645         {
3646           player->present = TRUE;
3647           player->active = TRUE;
3648
3649           field_player->present = FALSE;
3650           field_player->active = FALSE;
3651
3652           player->initial_element = field_player->initial_element;
3653           player->artwork_element = field_player->artwork_element;
3654
3655           player->block_last_field       = field_player->block_last_field;
3656           player->block_delay_adjustment = field_player->block_delay_adjustment;
3657
3658           StorePlayer[jx][jy] = player->element_nr;
3659
3660           player->jx = player->last_jx = jx;
3661           player->jy = player->last_jy = jy;
3662
3663           break;
3664         }
3665       }
3666     }
3667   }
3668 #endif
3669
3670 #if 0
3671   printf("::: local_player->present == %d\n", local_player->present);
3672 #endif
3673
3674   if (tape.playing)
3675   {
3676     /* when playing a tape, eliminate all players who do not participate */
3677
3678 #if USE_NEW_PLAYER_ASSIGNMENTS
3679
3680     if (!game.team_mode)
3681     {
3682       for (i = 0; i < MAX_PLAYERS; i++)
3683       {
3684         if (stored_player[i].active &&
3685             !tape.player_participates[map_player_action[i]])
3686         {
3687           struct PlayerInfo *player = &stored_player[i];
3688           int jx = player->jx, jy = player->jy;
3689
3690 #if DEBUG_INIT_PLAYER
3691           if (options.debug)
3692             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3693 #endif
3694
3695           player->active = FALSE;
3696           StorePlayer[jx][jy] = 0;
3697           Feld[jx][jy] = EL_EMPTY;
3698         }
3699       }
3700     }
3701
3702 #else
3703
3704     for (i = 0; i < MAX_PLAYERS; i++)
3705     {
3706       if (stored_player[i].active &&
3707           !tape.player_participates[i])
3708       {
3709         struct PlayerInfo *player = &stored_player[i];
3710         int jx = player->jx, jy = player->jy;
3711
3712         player->active = FALSE;
3713         StorePlayer[jx][jy] = 0;
3714         Feld[jx][jy] = EL_EMPTY;
3715       }
3716     }
3717 #endif
3718   }
3719   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3720   {
3721     /* when in single player mode, eliminate all but the first active player */
3722
3723     for (i = 0; i < MAX_PLAYERS; i++)
3724     {
3725       if (stored_player[i].active)
3726       {
3727         for (j = i + 1; j < MAX_PLAYERS; j++)
3728         {
3729           if (stored_player[j].active)
3730           {
3731             struct PlayerInfo *player = &stored_player[j];
3732             int jx = player->jx, jy = player->jy;
3733
3734             player->active = FALSE;
3735             player->present = FALSE;
3736
3737             StorePlayer[jx][jy] = 0;
3738             Feld[jx][jy] = EL_EMPTY;
3739           }
3740         }
3741       }
3742     }
3743   }
3744
3745   /* when recording the game, store which players take part in the game */
3746   if (tape.recording)
3747   {
3748 #if USE_NEW_PLAYER_ASSIGNMENTS
3749     for (i = 0; i < MAX_PLAYERS; i++)
3750       if (stored_player[i].connected)
3751         tape.player_participates[i] = TRUE;
3752 #else
3753     for (i = 0; i < MAX_PLAYERS; i++)
3754       if (stored_player[i].active)
3755         tape.player_participates[i] = TRUE;
3756 #endif
3757   }
3758
3759 #if DEBUG_INIT_PLAYER
3760   if (options.debug)
3761   {
3762     printf("Player status after player assignment (final stage):\n");
3763
3764     for (i = 0; i < MAX_PLAYERS; i++)
3765     {
3766       struct PlayerInfo *player = &stored_player[i];
3767
3768       printf("- player %d: present == %d, connected == %d, active == %d",
3769              i + 1,
3770              player->present,
3771              player->connected,
3772              player->active);
3773
3774       if (local_player == player)
3775         printf(" (local player)");
3776
3777       printf("\n");
3778     }
3779   }
3780 #endif
3781
3782   if (BorderElement == EL_EMPTY)
3783   {
3784     SBX_Left = 0;
3785     SBX_Right = lev_fieldx - SCR_FIELDX;
3786     SBY_Upper = 0;
3787     SBY_Lower = lev_fieldy - SCR_FIELDY;
3788   }
3789   else
3790   {
3791     SBX_Left = -1;
3792     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3793     SBY_Upper = -1;
3794     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3795   }
3796
3797   if (full_lev_fieldx <= SCR_FIELDX)
3798     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3799   if (full_lev_fieldy <= SCR_FIELDY)
3800     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3801
3802   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3803     SBX_Left--;
3804   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3805     SBY_Upper--;
3806
3807   /* if local player not found, look for custom element that might create
3808      the player (make some assumptions about the right custom element) */
3809   if (!local_player->present)
3810   {
3811     int start_x = 0, start_y = 0;
3812     int found_rating = 0;
3813     int found_element = EL_UNDEFINED;
3814     int player_nr = local_player->index_nr;
3815
3816     SCAN_PLAYFIELD(x, y)
3817     {
3818       int element = Feld[x][y];
3819       int content;
3820       int xx, yy;
3821       boolean is_player;
3822
3823       if (level.use_start_element[player_nr] &&
3824           level.start_element[player_nr] == element &&
3825           found_rating < 4)
3826       {
3827         start_x = x;
3828         start_y = y;
3829
3830         found_rating = 4;
3831         found_element = element;
3832       }
3833
3834       if (!IS_CUSTOM_ELEMENT(element))
3835         continue;
3836
3837       if (CAN_CHANGE(element))
3838       {
3839         for (i = 0; i < element_info[element].num_change_pages; i++)
3840         {
3841           /* check for player created from custom element as single target */
3842           content = element_info[element].change_page[i].target_element;
3843           is_player = ELEM_IS_PLAYER(content);
3844
3845           if (is_player && (found_rating < 3 ||
3846                             (found_rating == 3 && element < found_element)))
3847           {
3848             start_x = x;
3849             start_y = y;
3850
3851             found_rating = 3;
3852             found_element = element;
3853           }
3854         }
3855       }
3856
3857       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3858       {
3859         /* check for player created from custom element as explosion content */
3860         content = element_info[element].content.e[xx][yy];
3861         is_player = ELEM_IS_PLAYER(content);
3862
3863         if (is_player && (found_rating < 2 ||
3864                           (found_rating == 2 && element < found_element)))
3865         {
3866           start_x = x + xx - 1;
3867           start_y = y + yy - 1;
3868
3869           found_rating = 2;
3870           found_element = element;
3871         }
3872
3873         if (!CAN_CHANGE(element))
3874           continue;
3875
3876         for (i = 0; i < element_info[element].num_change_pages; i++)
3877         {
3878           /* check for player created from custom element as extended target */
3879           content =
3880             element_info[element].change_page[i].target_content.e[xx][yy];
3881
3882           is_player = ELEM_IS_PLAYER(content);
3883
3884           if (is_player && (found_rating < 1 ||
3885                             (found_rating == 1 && element < found_element)))
3886           {
3887             start_x = x + xx - 1;
3888             start_y = y + yy - 1;
3889
3890             found_rating = 1;
3891             found_element = element;
3892           }
3893         }
3894       }
3895     }
3896
3897     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3898                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3899                 start_x - MIDPOSX);
3900
3901     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3902                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3903                 start_y - MIDPOSY);
3904   }
3905   else
3906   {
3907     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3908                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3909                 local_player->jx - MIDPOSX);
3910
3911     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3912                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3913                 local_player->jy - MIDPOSY);
3914   }
3915
3916   /* !!! FIX THIS (START) !!! */
3917   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3918   {
3919     InitGameEngine_EM();
3920   }
3921   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3922   {
3923     InitGameEngine_SP();
3924   }
3925   else
3926   {
3927     DrawLevel(REDRAW_FIELD);
3928     DrawAllPlayers();
3929
3930     /* after drawing the level, correct some elements */
3931     if (game.timegate_time_left == 0)
3932       CloseAllOpenTimegates();
3933   }
3934
3935   /* blit playfield from scroll buffer to normal back buffer for fading in */
3936   BlitScreenToBitmap(backbuffer);
3937   /* !!! FIX THIS (END) !!! */
3938
3939   DrawMaskedBorder(fade_mask);
3940
3941   FadeIn(fade_mask);
3942
3943 #if 1
3944   // full screen redraw is required at this point in the following cases:
3945   // - special editor door undrawn when game was started from level editor
3946   // - drawing area (playfield) was changed and has to be removed completely
3947   redraw_mask = REDRAW_ALL;
3948   BackToFront();
3949 #endif
3950
3951   if (!game.restart_level)
3952   {
3953     /* copy default game door content to main double buffer */
3954
3955     /* !!! CHECK AGAIN !!! */
3956     SetPanelBackground();
3957     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3958     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3959   }
3960
3961   SetPanelBackground();
3962   SetDrawBackgroundMask(REDRAW_DOOR_1);
3963
3964   UpdateAndDisplayGameControlValues();
3965
3966   if (!game.restart_level)
3967   {
3968     UnmapGameButtons();
3969     UnmapTapeButtons();
3970
3971     FreeGameButtons();
3972     CreateGameButtons();
3973
3974     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3975     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3976     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3977
3978     MapGameButtons();
3979     MapTapeButtons();
3980
3981     /* copy actual game door content to door double buffer for OpenDoor() */
3982     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3983
3984     OpenDoor(DOOR_OPEN_ALL);
3985
3986     PlaySound(SND_GAME_STARTING);
3987
3988     if (setup.sound_music)
3989       PlayLevelMusic();
3990
3991     KeyboardAutoRepeatOffUnlessAutoplay();
3992
3993 #if DEBUG_INIT_PLAYER
3994     if (options.debug)
3995     {
3996       printf("Player status (final):\n");
3997
3998       for (i = 0; i < MAX_PLAYERS; i++)
3999       {
4000         struct PlayerInfo *player = &stored_player[i];
4001
4002         printf("- player %d: present == %d, connected == %d, active == %d",
4003                i + 1,
4004                player->present,
4005                player->connected,
4006                player->active);
4007
4008         if (local_player == player)
4009           printf(" (local player)");
4010
4011         printf("\n");
4012       }
4013     }
4014 #endif
4015   }
4016
4017   UnmapAllGadgets();
4018
4019   MapGameButtons();
4020   MapTapeButtons();
4021
4022   if (!game.restart_level && !tape.playing)
4023   {
4024     LevelStats_incPlayed(level_nr);
4025
4026     SaveLevelSetup_SeriesInfo();
4027   }
4028
4029   game.restart_level = FALSE;
4030
4031   SaveEngineSnapshotToListInitial();
4032 }
4033
4034 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4035 {
4036   /* this is used for non-R'n'D game engines to update certain engine values */
4037
4038   /* needed to determine if sounds are played within the visible screen area */
4039   scroll_x = actual_scroll_x;
4040   scroll_y = actual_scroll_y;
4041 }
4042
4043 void InitMovDir(int x, int y)
4044 {
4045   int i, element = Feld[x][y];
4046   static int xy[4][2] =
4047   {
4048     {  0, +1 },
4049     { +1,  0 },
4050     {  0, -1 },
4051     { -1,  0 }
4052   };
4053   static int direction[3][4] =
4054   {
4055     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4056     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4057     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4058   };
4059
4060   switch (element)
4061   {
4062     case EL_BUG_RIGHT:
4063     case EL_BUG_UP:
4064     case EL_BUG_LEFT:
4065     case EL_BUG_DOWN:
4066       Feld[x][y] = EL_BUG;
4067       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4068       break;
4069
4070     case EL_SPACESHIP_RIGHT:
4071     case EL_SPACESHIP_UP:
4072     case EL_SPACESHIP_LEFT:
4073     case EL_SPACESHIP_DOWN:
4074       Feld[x][y] = EL_SPACESHIP;
4075       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4076       break;
4077
4078     case EL_BD_BUTTERFLY_RIGHT:
4079     case EL_BD_BUTTERFLY_UP:
4080     case EL_BD_BUTTERFLY_LEFT:
4081     case EL_BD_BUTTERFLY_DOWN:
4082       Feld[x][y] = EL_BD_BUTTERFLY;
4083       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4084       break;
4085
4086     case EL_BD_FIREFLY_RIGHT:
4087     case EL_BD_FIREFLY_UP:
4088     case EL_BD_FIREFLY_LEFT:
4089     case EL_BD_FIREFLY_DOWN:
4090       Feld[x][y] = EL_BD_FIREFLY;
4091       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4092       break;
4093
4094     case EL_PACMAN_RIGHT:
4095     case EL_PACMAN_UP:
4096     case EL_PACMAN_LEFT:
4097     case EL_PACMAN_DOWN:
4098       Feld[x][y] = EL_PACMAN;
4099       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4100       break;
4101
4102     case EL_YAMYAM_LEFT:
4103     case EL_YAMYAM_RIGHT:
4104     case EL_YAMYAM_UP:
4105     case EL_YAMYAM_DOWN:
4106       Feld[x][y] = EL_YAMYAM;
4107       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4108       break;
4109
4110     case EL_SP_SNIKSNAK:
4111       MovDir[x][y] = MV_UP;
4112       break;
4113
4114     case EL_SP_ELECTRON:
4115       MovDir[x][y] = MV_LEFT;
4116       break;
4117
4118     case EL_MOLE_LEFT:
4119     case EL_MOLE_RIGHT:
4120     case EL_MOLE_UP:
4121     case EL_MOLE_DOWN:
4122       Feld[x][y] = EL_MOLE;
4123       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4124       break;
4125
4126     default:
4127       if (IS_CUSTOM_ELEMENT(element))
4128       {
4129         struct ElementInfo *ei = &element_info[element];
4130         int move_direction_initial = ei->move_direction_initial;
4131         int move_pattern = ei->move_pattern;
4132
4133         if (move_direction_initial == MV_START_PREVIOUS)
4134         {
4135           if (MovDir[x][y] != MV_NONE)
4136             return;
4137
4138           move_direction_initial = MV_START_AUTOMATIC;
4139         }
4140
4141         if (move_direction_initial == MV_START_RANDOM)
4142           MovDir[x][y] = 1 << RND(4);
4143         else if (move_direction_initial & MV_ANY_DIRECTION)
4144           MovDir[x][y] = move_direction_initial;
4145         else if (move_pattern == MV_ALL_DIRECTIONS ||
4146                  move_pattern == MV_TURNING_LEFT ||
4147                  move_pattern == MV_TURNING_RIGHT ||
4148                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4149                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4150                  move_pattern == MV_TURNING_RANDOM)
4151           MovDir[x][y] = 1 << RND(4);
4152         else if (move_pattern == MV_HORIZONTAL)
4153           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4154         else if (move_pattern == MV_VERTICAL)
4155           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4156         else if (move_pattern & MV_ANY_DIRECTION)
4157           MovDir[x][y] = element_info[element].move_pattern;
4158         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4159                  move_pattern == MV_ALONG_RIGHT_SIDE)
4160         {
4161           /* use random direction as default start direction */
4162           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4163             MovDir[x][y] = 1 << RND(4);
4164
4165           for (i = 0; i < NUM_DIRECTIONS; i++)
4166           {
4167             int x1 = x + xy[i][0];
4168             int y1 = y + xy[i][1];
4169
4170             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4171             {
4172               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4173                 MovDir[x][y] = direction[0][i];
4174               else
4175                 MovDir[x][y] = direction[1][i];
4176
4177               break;
4178             }
4179           }
4180         }                
4181       }
4182       else
4183       {
4184         MovDir[x][y] = 1 << RND(4);
4185
4186         if (element != EL_BUG &&
4187             element != EL_SPACESHIP &&
4188             element != EL_BD_BUTTERFLY &&
4189             element != EL_BD_FIREFLY)
4190           break;
4191
4192         for (i = 0; i < NUM_DIRECTIONS; i++)
4193         {
4194           int x1 = x + xy[i][0];
4195           int y1 = y + xy[i][1];
4196
4197           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4198           {
4199             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4200             {
4201               MovDir[x][y] = direction[0][i];
4202               break;
4203             }
4204             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4205                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4206             {
4207               MovDir[x][y] = direction[1][i];
4208               break;
4209             }
4210           }
4211         }
4212       }
4213       break;
4214   }
4215
4216   GfxDir[x][y] = MovDir[x][y];
4217 }
4218
4219 void InitAmoebaNr(int x, int y)
4220 {
4221   int i;
4222   int group_nr = AmoebeNachbarNr(x, y);
4223
4224   if (group_nr == 0)
4225   {
4226     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4227     {
4228       if (AmoebaCnt[i] == 0)
4229       {
4230         group_nr = i;
4231         break;
4232       }
4233     }
4234   }
4235
4236   AmoebaNr[x][y] = group_nr;
4237   AmoebaCnt[group_nr]++;
4238   AmoebaCnt2[group_nr]++;
4239 }
4240
4241 static void PlayerWins(struct PlayerInfo *player)
4242 {
4243   player->LevelSolved = TRUE;
4244   player->GameOver = TRUE;
4245
4246   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4247                          level.native_em_level->lev->score : player->score);
4248
4249   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4250                                       TimeLeft);
4251   player->LevelSolved_CountingScore = player->score_final;
4252 }
4253
4254 void GameWon()
4255 {
4256   static int time, time_final;
4257   static int score, score_final;
4258   static int game_over_delay_1 = 0;
4259   static int game_over_delay_2 = 0;
4260   int game_over_delay_value_1 = 50;
4261   int game_over_delay_value_2 = 50;
4262
4263   if (!local_player->LevelSolved_GameWon)
4264   {
4265     int i;
4266
4267     /* do not start end game actions before the player stops moving (to exit) */
4268     if (local_player->MovPos)
4269       return;
4270
4271     local_player->LevelSolved_GameWon = TRUE;
4272     local_player->LevelSolved_SaveTape = tape.recording;
4273     local_player->LevelSolved_SaveScore = !tape.playing;
4274
4275     if (!tape.playing)
4276     {
4277       LevelStats_incSolved(level_nr);
4278
4279       SaveLevelSetup_SeriesInfo();
4280     }
4281
4282     if (tape.auto_play)         /* tape might already be stopped here */
4283       tape.auto_play_level_solved = TRUE;
4284
4285     TapeStop();
4286
4287     game_over_delay_1 = game_over_delay_value_1;
4288     game_over_delay_2 = game_over_delay_value_2;
4289
4290     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4291     score = score_final = local_player->score_final;
4292
4293     if (TimeLeft > 0)
4294     {
4295       time_final = 0;
4296       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4297     }
4298     else if (game.no_time_limit && TimePlayed < 999)
4299     {
4300       time_final = 999;
4301       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4302     }
4303
4304     local_player->score_final = score_final;
4305
4306     if (level_editor_test_game)
4307     {
4308       time = time_final;
4309       score = score_final;
4310
4311       local_player->LevelSolved_CountingTime = time;
4312       local_player->LevelSolved_CountingScore = score;
4313
4314       game_panel_controls[GAME_PANEL_TIME].value = time;
4315       game_panel_controls[GAME_PANEL_SCORE].value = score;
4316
4317       DisplayGameControlValues();
4318     }
4319
4320     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4321     {
4322       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4323       {
4324         /* close exit door after last player */
4325         if ((AllPlayersGone &&
4326              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4327               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4328               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4329             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4330             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4331         {
4332           int element = Feld[ExitX][ExitY];
4333
4334           Feld[ExitX][ExitY] =
4335             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4336              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4337              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4338              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4339              EL_EM_STEEL_EXIT_CLOSING);
4340
4341           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4342         }
4343
4344         /* player disappears */
4345         DrawLevelField(ExitX, ExitY);
4346       }
4347
4348       for (i = 0; i < MAX_PLAYERS; i++)
4349       {
4350         struct PlayerInfo *player = &stored_player[i];
4351
4352         if (player->present)
4353         {
4354           RemovePlayer(player);
4355
4356           /* player disappears */
4357           DrawLevelField(player->jx, player->jy);
4358         }
4359       }
4360     }
4361
4362     PlaySound(SND_GAME_WINNING);
4363   }
4364
4365   if (game_over_delay_1 > 0)
4366   {
4367     game_over_delay_1--;
4368
4369     return;
4370   }
4371
4372   if (time != time_final)
4373   {
4374     int time_to_go = ABS(time_final - time);
4375     int time_count_dir = (time < time_final ? +1 : -1);
4376     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4377
4378     time  += time_count_steps * time_count_dir;
4379     score += time_count_steps * level.score[SC_TIME_BONUS];
4380
4381     local_player->LevelSolved_CountingTime = time;
4382     local_player->LevelSolved_CountingScore = score;
4383
4384     game_panel_controls[GAME_PANEL_TIME].value = time;
4385     game_panel_controls[GAME_PANEL_SCORE].value = score;
4386
4387     DisplayGameControlValues();
4388
4389     if (time == time_final)
4390       StopSound(SND_GAME_LEVELTIME_BONUS);
4391     else if (setup.sound_loops)
4392       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4393     else
4394       PlaySound(SND_GAME_LEVELTIME_BONUS);
4395
4396     return;
4397   }
4398
4399   local_player->LevelSolved_PanelOff = TRUE;
4400
4401   if (game_over_delay_2 > 0)
4402   {
4403     game_over_delay_2--;
4404
4405     return;
4406   }
4407
4408   GameEnd();
4409 }
4410
4411 void GameEnd()
4412 {
4413   int hi_pos;
4414   boolean raise_level = FALSE;
4415
4416   local_player->LevelSolved_GameEnd = TRUE;
4417
4418   if (!global.use_envelope_request)
4419     CloseDoor(DOOR_CLOSE_1);
4420
4421   if (local_player->LevelSolved_SaveTape)
4422   {
4423     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4424   }
4425
4426   CloseDoor(DOOR_CLOSE_ALL);
4427
4428   if (level_editor_test_game)
4429   {
4430     SetGameStatus(GAME_MODE_MAIN);
4431
4432     DrawMainMenu();
4433
4434     return;
4435   }
4436
4437   if (!local_player->LevelSolved_SaveScore)
4438   {
4439     SetGameStatus(GAME_MODE_MAIN);
4440
4441     DrawMainMenu();
4442
4443     return;
4444   }
4445
4446   if (level_nr == leveldir_current->handicap_level)
4447   {
4448     leveldir_current->handicap_level++;
4449
4450     SaveLevelSetup_SeriesInfo();
4451   }
4452
4453   if (level_nr < leveldir_current->last_level)
4454     raise_level = TRUE;                 /* advance to next level */
4455
4456   if ((hi_pos = NewHiScore()) >= 0) 
4457   {
4458     SetGameStatus(GAME_MODE_SCORES);
4459
4460     DrawHallOfFame(hi_pos);
4461
4462     if (raise_level)
4463     {
4464       level_nr++;
4465       TapeErase();
4466     }
4467   }
4468   else
4469   {
4470     SetGameStatus(GAME_MODE_MAIN);
4471
4472     if (raise_level)
4473     {
4474       level_nr++;
4475       TapeErase();
4476     }
4477
4478     DrawMainMenu();
4479   }
4480 }
4481
4482 int NewHiScore()
4483 {
4484   int k, l;
4485   int position = -1;
4486
4487   LoadScore(level_nr);
4488
4489   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4490       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4491     return -1;
4492
4493   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4494   {
4495     if (local_player->score_final > highscore[k].Score)
4496     {
4497       /* player has made it to the hall of fame */
4498
4499       if (k < MAX_SCORE_ENTRIES - 1)
4500       {
4501         int m = MAX_SCORE_ENTRIES - 1;
4502
4503 #ifdef ONE_PER_NAME
4504         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4505           if (strEqual(setup.player_name, highscore[l].Name))
4506             m = l;
4507         if (m == k)     /* player's new highscore overwrites his old one */
4508           goto put_into_list;
4509 #endif
4510
4511         for (l = m; l > k; l--)
4512         {
4513           strcpy(highscore[l].Name, highscore[l - 1].Name);
4514           highscore[l].Score = highscore[l - 1].Score;
4515         }
4516       }
4517
4518 #ifdef ONE_PER_NAME
4519       put_into_list:
4520 #endif
4521       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4522       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4523       highscore[k].Score = local_player->score_final; 
4524       position = k;
4525       break;
4526     }
4527
4528 #ifdef ONE_PER_NAME
4529     else if (!strncmp(setup.player_name, highscore[k].Name,
4530                       MAX_PLAYER_NAME_LEN))
4531       break;    /* player already there with a higher score */
4532 #endif
4533
4534   }
4535
4536   if (position >= 0) 
4537     SaveScore(level_nr);
4538
4539   return position;
4540 }
4541
4542 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4543 {
4544   int element = Feld[x][y];
4545   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4546   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4547   int horiz_move = (dx != 0);
4548   int sign = (horiz_move ? dx : dy);
4549   int step = sign * element_info[element].move_stepsize;
4550
4551   /* special values for move stepsize for spring and things on conveyor belt */
4552   if (horiz_move)
4553   {
4554     if (CAN_FALL(element) &&
4555         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4556       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4557     else if (element == EL_SPRING)
4558       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4559   }
4560
4561   return step;
4562 }
4563
4564 inline static int getElementMoveStepsize(int x, int y)
4565 {
4566   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4567 }
4568
4569 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4570 {
4571   if (player->GfxAction != action || player->GfxDir != dir)
4572   {
4573     player->GfxAction = action;
4574     player->GfxDir = dir;
4575     player->Frame = 0;
4576     player->StepFrame = 0;
4577   }
4578 }
4579
4580 static void ResetGfxFrame(int x, int y, boolean redraw)
4581 {
4582   int element = Feld[x][y];
4583   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4584   int last_gfx_frame = GfxFrame[x][y];
4585
4586   if (graphic_info[graphic].anim_global_sync)
4587     GfxFrame[x][y] = FrameCounter;
4588   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4589     GfxFrame[x][y] = CustomValue[x][y];
4590   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4591     GfxFrame[x][y] = element_info[element].collect_score;
4592   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4593     GfxFrame[x][y] = ChangeDelay[x][y];
4594
4595   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4596     DrawLevelGraphicAnimation(x, y, graphic);
4597 }
4598
4599 static void ResetGfxAnimation(int x, int y)
4600 {
4601   GfxAction[x][y] = ACTION_DEFAULT;
4602   GfxDir[x][y] = MovDir[x][y];
4603   GfxFrame[x][y] = 0;
4604
4605   ResetGfxFrame(x, y, FALSE);
4606 }
4607
4608 static void ResetRandomAnimationValue(int x, int y)
4609 {
4610   GfxRandom[x][y] = INIT_GFX_RANDOM();
4611 }
4612
4613 void InitMovingField(int x, int y, int direction)
4614 {
4615   int element = Feld[x][y];
4616   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4617   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4618   int newx = x + dx;
4619   int newy = y + dy;
4620   boolean is_moving_before, is_moving_after;
4621
4622   /* check if element was/is moving or being moved before/after mode change */
4623   is_moving_before = (WasJustMoving[x][y] != 0);
4624   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4625
4626   /* reset animation only for moving elements which change direction of moving
4627      or which just started or stopped moving
4628      (else CEs with property "can move" / "not moving" are reset each frame) */
4629   if (is_moving_before != is_moving_after ||
4630       direction != MovDir[x][y])
4631     ResetGfxAnimation(x, y);
4632
4633   MovDir[x][y] = direction;
4634   GfxDir[x][y] = direction;
4635
4636   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4637                      direction == MV_DOWN && CAN_FALL(element) ?
4638                      ACTION_FALLING : ACTION_MOVING);
4639
4640   /* this is needed for CEs with property "can move" / "not moving" */
4641
4642   if (is_moving_after)
4643   {
4644     if (Feld[newx][newy] == EL_EMPTY)
4645       Feld[newx][newy] = EL_BLOCKED;
4646
4647     MovDir[newx][newy] = MovDir[x][y];
4648
4649     CustomValue[newx][newy] = CustomValue[x][y];
4650
4651     GfxFrame[newx][newy] = GfxFrame[x][y];
4652     GfxRandom[newx][newy] = GfxRandom[x][y];
4653     GfxAction[newx][newy] = GfxAction[x][y];
4654     GfxDir[newx][newy] = GfxDir[x][y];
4655   }
4656 }
4657
4658 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4659 {
4660   int direction = MovDir[x][y];
4661   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4662   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4663
4664   *goes_to_x = newx;
4665   *goes_to_y = newy;
4666 }
4667
4668 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4669 {
4670   int oldx = x, oldy = y;
4671   int direction = MovDir[x][y];
4672
4673   if (direction == MV_LEFT)
4674     oldx++;
4675   else if (direction == MV_RIGHT)
4676     oldx--;
4677   else if (direction == MV_UP)
4678     oldy++;
4679   else if (direction == MV_DOWN)
4680     oldy--;
4681
4682   *comes_from_x = oldx;
4683   *comes_from_y = oldy;
4684 }
4685
4686 int MovingOrBlocked2Element(int x, int y)
4687 {
4688   int element = Feld[x][y];
4689
4690   if (element == EL_BLOCKED)
4691   {
4692     int oldx, oldy;
4693
4694     Blocked2Moving(x, y, &oldx, &oldy);
4695     return Feld[oldx][oldy];
4696   }
4697   else
4698     return element;
4699 }
4700
4701 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4702 {
4703   /* like MovingOrBlocked2Element(), but if element is moving
4704      and (x,y) is the field the moving element is just leaving,
4705      return EL_BLOCKED instead of the element value */
4706   int element = Feld[x][y];
4707
4708   if (IS_MOVING(x, y))
4709   {
4710     if (element == EL_BLOCKED)
4711     {
4712       int oldx, oldy;
4713
4714       Blocked2Moving(x, y, &oldx, &oldy);
4715       return Feld[oldx][oldy];
4716     }
4717     else
4718       return EL_BLOCKED;
4719   }
4720   else
4721     return element;
4722 }
4723
4724 static void RemoveField(int x, int y)
4725 {
4726   Feld[x][y] = EL_EMPTY;
4727
4728   MovPos[x][y] = 0;
4729   MovDir[x][y] = 0;
4730   MovDelay[x][y] = 0;
4731
4732   CustomValue[x][y] = 0;
4733
4734   AmoebaNr[x][y] = 0;
4735   ChangeDelay[x][y] = 0;
4736   ChangePage[x][y] = -1;
4737   Pushed[x][y] = FALSE;
4738
4739   GfxElement[x][y] = EL_UNDEFINED;
4740   GfxAction[x][y] = ACTION_DEFAULT;
4741   GfxDir[x][y] = MV_NONE;
4742 }
4743
4744 void RemoveMovingField(int x, int y)
4745 {
4746   int oldx = x, oldy = y, newx = x, newy = y;
4747   int element = Feld[x][y];
4748   int next_element = EL_UNDEFINED;
4749
4750   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4751     return;
4752
4753   if (IS_MOVING(x, y))
4754   {
4755     Moving2Blocked(x, y, &newx, &newy);
4756
4757     if (Feld[newx][newy] != EL_BLOCKED)
4758     {
4759       /* element is moving, but target field is not free (blocked), but
4760          already occupied by something different (example: acid pool);
4761          in this case, only remove the moving field, but not the target */
4762
4763       RemoveField(oldx, oldy);
4764
4765       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4766
4767       TEST_DrawLevelField(oldx, oldy);
4768
4769       return;
4770     }
4771   }
4772   else if (element == EL_BLOCKED)
4773   {
4774     Blocked2Moving(x, y, &oldx, &oldy);
4775     if (!IS_MOVING(oldx, oldy))
4776       return;
4777   }
4778
4779   if (element == EL_BLOCKED &&
4780       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4781        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4782        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4783        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4784        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4785        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4786     next_element = get_next_element(Feld[oldx][oldy]);
4787
4788   RemoveField(oldx, oldy);
4789   RemoveField(newx, newy);
4790
4791   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4792
4793   if (next_element != EL_UNDEFINED)
4794     Feld[oldx][oldy] = next_element;
4795
4796   TEST_DrawLevelField(oldx, oldy);
4797   TEST_DrawLevelField(newx, newy);
4798 }
4799
4800 void DrawDynamite(int x, int y)
4801 {
4802   int sx = SCREENX(x), sy = SCREENY(y);
4803   int graphic = el2img(Feld[x][y]);
4804   int frame;
4805
4806   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4807     return;
4808
4809   if (IS_WALKABLE_INSIDE(Back[x][y]))
4810     return;
4811
4812   if (Back[x][y])
4813     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4814   else if (Store[x][y])
4815     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4816
4817   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4818
4819   if (Back[x][y] || Store[x][y])
4820     DrawGraphicThruMask(sx, sy, graphic, frame);
4821   else
4822     DrawGraphic(sx, sy, graphic, frame);
4823 }
4824
4825 void CheckDynamite(int x, int y)
4826 {
4827   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4828   {
4829     MovDelay[x][y]--;
4830
4831     if (MovDelay[x][y] != 0)
4832     {
4833       DrawDynamite(x, y);
4834       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4835
4836       return;
4837     }
4838   }
4839
4840   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4841
4842   Bang(x, y);
4843 }
4844
4845 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4846 {
4847   boolean num_checked_players = 0;
4848   int i;
4849
4850   for (i = 0; i < MAX_PLAYERS; i++)
4851   {
4852     if (stored_player[i].active)
4853     {
4854       int sx = stored_player[i].jx;
4855       int sy = stored_player[i].jy;
4856
4857       if (num_checked_players == 0)
4858       {
4859         *sx1 = *sx2 = sx;
4860         *sy1 = *sy2 = sy;
4861       }
4862       else
4863       {
4864         *sx1 = MIN(*sx1, sx);
4865         *sy1 = MIN(*sy1, sy);
4866         *sx2 = MAX(*sx2, sx);
4867         *sy2 = MAX(*sy2, sy);
4868       }
4869
4870       num_checked_players++;
4871     }
4872   }
4873 }
4874
4875 static boolean checkIfAllPlayersFitToScreen_RND()
4876 {
4877   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4878
4879   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4880
4881   return (sx2 - sx1 < SCR_FIELDX &&
4882           sy2 - sy1 < SCR_FIELDY);
4883 }
4884
4885 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4886 {
4887   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4888
4889   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4890
4891   *sx = (sx1 + sx2) / 2;
4892   *sy = (sy1 + sy2) / 2;
4893 }
4894
4895 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4896                         boolean center_screen, boolean quick_relocation)
4897 {
4898   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4899   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4900   boolean no_delay = (tape.warp_forward);
4901   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4902   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4903   int new_scroll_x, new_scroll_y;
4904
4905   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4906   {
4907     /* case 1: quick relocation inside visible screen (without scrolling) */
4908
4909     RedrawPlayfield();
4910
4911     return;
4912   }
4913
4914   if (!level.shifted_relocation || center_screen)
4915   {
4916     /* relocation _with_ centering of screen */
4917
4918     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4919                     x > SBX_Right + MIDPOSX ? SBX_Right :
4920                     x - MIDPOSX);
4921
4922     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4923                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4924                     y - MIDPOSY);
4925   }
4926   else
4927   {
4928     /* relocation _without_ centering of screen */
4929
4930     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4931                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4932                            old_x - MIDPOSX);
4933
4934     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4935                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4936                            old_y - MIDPOSY);
4937
4938     int offset_x = x + (scroll_x - center_scroll_x);
4939     int offset_y = y + (scroll_y - center_scroll_y);
4940
4941     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4942                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4943                     offset_x - MIDPOSX);
4944
4945     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4946                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4947                     offset_y - MIDPOSY);
4948   }
4949
4950   if (quick_relocation)
4951   {
4952     /* case 2: quick relocation (redraw without visible scrolling) */
4953
4954     scroll_x = new_scroll_x;
4955     scroll_y = new_scroll_y;
4956
4957     RedrawPlayfield();
4958
4959     return;
4960   }
4961
4962   /* case 3: visible relocation (with scrolling to new position) */
4963
4964   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4965
4966   SetVideoFrameDelay(wait_delay_value);
4967
4968   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4969   {
4970     int dx = 0, dy = 0;
4971     int fx = FX, fy = FY;
4972
4973     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4974     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4975
4976     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4977       break;
4978
4979     scroll_x -= dx;
4980     scroll_y -= dy;
4981
4982     fx += dx * TILEX / 2;
4983     fy += dy * TILEY / 2;
4984
4985     ScrollLevel(dx, dy);
4986     DrawAllPlayers();
4987
4988     /* scroll in two steps of half tile size to make things smoother */
4989     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4990
4991     /* scroll second step to align at full tile size */
4992     BlitScreenToBitmap(window);
4993   }
4994
4995   DrawAllPlayers();
4996   BackToFront();
4997
4998   SetVideoFrameDelay(frame_delay_value_old);
4999 }
5000
5001 void RelocatePlayer(int jx, int jy, int el_player_raw)
5002 {
5003   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5004   int player_nr = GET_PLAYER_NR(el_player);
5005   struct PlayerInfo *player = &stored_player[player_nr];
5006   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5007   boolean no_delay = (tape.warp_forward);
5008   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5009   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5010   int old_jx = player->jx;
5011   int old_jy = player->jy;
5012   int old_element = Feld[old_jx][old_jy];
5013   int element = Feld[jx][jy];
5014   boolean player_relocated = (old_jx != jx || old_jy != jy);
5015
5016   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5017   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5018   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5019   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5020   int leave_side_horiz = move_dir_horiz;
5021   int leave_side_vert  = move_dir_vert;
5022   int enter_side = enter_side_horiz | enter_side_vert;
5023   int leave_side = leave_side_horiz | leave_side_vert;
5024
5025   if (player->GameOver)         /* do not reanimate dead player */
5026     return;
5027
5028   if (!player_relocated)        /* no need to relocate the player */
5029     return;
5030
5031   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5032   {
5033     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5034     DrawLevelField(jx, jy);
5035   }
5036
5037   if (player->present)
5038   {
5039     while (player->MovPos)
5040     {
5041       ScrollPlayer(player, SCROLL_GO_ON);
5042       ScrollScreen(NULL, SCROLL_GO_ON);
5043
5044       AdvanceFrameAndPlayerCounters(player->index_nr);
5045
5046       DrawPlayer(player);
5047
5048       BackToFront_WithFrameDelay(wait_delay_value);
5049     }
5050
5051     DrawPlayer(player);         /* needed here only to cleanup last field */
5052     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5053
5054     player->is_moving = FALSE;
5055   }
5056
5057   if (IS_CUSTOM_ELEMENT(old_element))
5058     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5059                                CE_LEFT_BY_PLAYER,
5060                                player->index_bit, leave_side);
5061
5062   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5063                                       CE_PLAYER_LEAVES_X,
5064                                       player->index_bit, leave_side);
5065
5066   Feld[jx][jy] = el_player;
5067   InitPlayerField(jx, jy, el_player, TRUE);
5068
5069   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5070      possible that the relocation target field did not contain a player element,
5071      but a walkable element, to which the new player was relocated -- in this
5072      case, restore that (already initialized!) element on the player field */
5073   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5074   {
5075     Feld[jx][jy] = element;     /* restore previously existing element */
5076   }
5077
5078   /* only visually relocate centered player */
5079   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5080                      FALSE, level.instant_relocation);
5081
5082   TestIfPlayerTouchesBadThing(jx, jy);
5083   TestIfPlayerTouchesCustomElement(jx, jy);
5084
5085   if (IS_CUSTOM_ELEMENT(element))
5086     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5087                                player->index_bit, enter_side);
5088
5089   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5090                                       player->index_bit, enter_side);
5091
5092   if (player->is_switching)
5093   {
5094     /* ensure that relocation while still switching an element does not cause
5095        a new element to be treated as also switched directly after relocation
5096        (this is important for teleporter switches that teleport the player to
5097        a place where another teleporter switch is in the same direction, which
5098        would then incorrectly be treated as immediately switched before the
5099        direction key that caused the switch was released) */
5100
5101     player->switch_x += jx - old_jx;
5102     player->switch_y += jy - old_jy;
5103   }
5104 }
5105
5106 void Explode(int ex, int ey, int phase, int mode)
5107 {
5108   int x, y;
5109   int last_phase;
5110   int border_element;
5111
5112   /* !!! eliminate this variable !!! */
5113   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5114
5115   if (game.explosions_delayed)
5116   {
5117     ExplodeField[ex][ey] = mode;
5118     return;
5119   }
5120
5121   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5122   {
5123     int center_element = Feld[ex][ey];
5124     int artwork_element, explosion_element;     /* set these values later */
5125
5126     /* remove things displayed in background while burning dynamite */
5127     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5128       Back[ex][ey] = 0;
5129
5130     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5131     {
5132       /* put moving element to center field (and let it explode there) */
5133       center_element = MovingOrBlocked2Element(ex, ey);
5134       RemoveMovingField(ex, ey);
5135       Feld[ex][ey] = center_element;
5136     }
5137
5138     /* now "center_element" is finally determined -- set related values now */
5139     artwork_element = center_element;           /* for custom player artwork */
5140     explosion_element = center_element;         /* for custom player artwork */
5141
5142     if (IS_PLAYER(ex, ey))
5143     {
5144       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5145
5146       artwork_element = stored_player[player_nr].artwork_element;
5147
5148       if (level.use_explosion_element[player_nr])
5149       {
5150         explosion_element = level.explosion_element[player_nr];
5151         artwork_element = explosion_element;
5152       }
5153     }
5154
5155     if (mode == EX_TYPE_NORMAL ||
5156         mode == EX_TYPE_CENTER ||
5157         mode == EX_TYPE_CROSS)
5158       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5159
5160     last_phase = element_info[explosion_element].explosion_delay + 1;
5161
5162     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5163     {
5164       int xx = x - ex + 1;
5165       int yy = y - ey + 1;
5166       int element;
5167
5168       if (!IN_LEV_FIELD(x, y) ||
5169           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5170           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5171         continue;
5172
5173       element = Feld[x][y];
5174
5175       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5176       {
5177         element = MovingOrBlocked2Element(x, y);
5178
5179         if (!IS_EXPLOSION_PROOF(element))
5180           RemoveMovingField(x, y);
5181       }
5182
5183       /* indestructible elements can only explode in center (but not flames) */
5184       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5185                                            mode == EX_TYPE_BORDER)) ||
5186           element == EL_FLAMES)
5187         continue;
5188
5189       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5190          behaviour, for example when touching a yamyam that explodes to rocks
5191          with active deadly shield, a rock is created under the player !!! */
5192       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5193 #if 0
5194       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5195           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5196            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5197 #else
5198       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5199 #endif
5200       {
5201         if (IS_ACTIVE_BOMB(element))
5202         {
5203           /* re-activate things under the bomb like gate or penguin */
5204           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5205           Back[x][y] = 0;
5206         }
5207
5208         continue;
5209       }
5210
5211       /* save walkable background elements while explosion on same tile */
5212       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5213           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5214         Back[x][y] = element;
5215
5216       /* ignite explodable elements reached by other explosion */
5217       if (element == EL_EXPLOSION)
5218         element = Store2[x][y];
5219
5220       if (AmoebaNr[x][y] &&
5221           (element == EL_AMOEBA_FULL ||
5222            element == EL_BD_AMOEBA ||
5223            element == EL_AMOEBA_GROWING))
5224       {
5225         AmoebaCnt[AmoebaNr[x][y]]--;
5226         AmoebaCnt2[AmoebaNr[x][y]]--;
5227       }
5228
5229       RemoveField(x, y);
5230
5231       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5232       {
5233         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5234
5235         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5236
5237         if (PLAYERINFO(ex, ey)->use_murphy)
5238           Store[x][y] = EL_EMPTY;
5239       }
5240
5241       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5242          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5243       else if (ELEM_IS_PLAYER(center_element))
5244         Store[x][y] = EL_EMPTY;
5245       else if (center_element == EL_YAMYAM)
5246         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5247       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5248         Store[x][y] = element_info[center_element].content.e[xx][yy];
5249 #if 1
5250       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5251          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5252          otherwise) -- FIX THIS !!! */
5253       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5254         Store[x][y] = element_info[element].content.e[1][1];
5255 #else
5256       else if (!CAN_EXPLODE(element))
5257         Store[x][y] = element_info[element].content.e[1][1];
5258 #endif
5259       else
5260         Store[x][y] = EL_EMPTY;
5261
5262       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5263           center_element == EL_AMOEBA_TO_DIAMOND)
5264         Store2[x][y] = element;
5265
5266       Feld[x][y] = EL_EXPLOSION;
5267       GfxElement[x][y] = artwork_element;
5268
5269       ExplodePhase[x][y] = 1;
5270       ExplodeDelay[x][y] = last_phase;
5271
5272       Stop[x][y] = TRUE;
5273     }
5274
5275     if (center_element == EL_YAMYAM)
5276       game.yamyam_content_nr =
5277         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5278
5279     return;
5280   }
5281
5282   if (Stop[ex][ey])
5283     return;
5284
5285   x = ex;
5286   y = ey;
5287
5288   if (phase == 1)
5289     GfxFrame[x][y] = 0;         /* restart explosion animation */
5290
5291   last_phase = ExplodeDelay[x][y];
5292
5293   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5294
5295   /* this can happen if the player leaves an explosion just in time */
5296   if (GfxElement[x][y] == EL_UNDEFINED)
5297     GfxElement[x][y] = EL_EMPTY;
5298
5299   border_element = Store2[x][y];
5300   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5301     border_element = StorePlayer[x][y];
5302
5303   if (phase == element_info[border_element].ignition_delay ||
5304       phase == last_phase)
5305   {
5306     boolean border_explosion = FALSE;
5307
5308     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5309         !PLAYER_EXPLOSION_PROTECTED(x, y))
5310     {
5311       KillPlayerUnlessExplosionProtected(x, y);
5312       border_explosion = TRUE;
5313     }
5314     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5315     {
5316       Feld[x][y] = Store2[x][y];
5317       Store2[x][y] = 0;
5318       Bang(x, y);
5319       border_explosion = TRUE;
5320     }
5321     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5322     {
5323       AmoebeUmwandeln(x, y);
5324       Store2[x][y] = 0;
5325       border_explosion = TRUE;
5326     }
5327
5328     /* if an element just explodes due to another explosion (chain-reaction),
5329        do not immediately end the new explosion when it was the last frame of
5330        the explosion (as it would be done in the following "if"-statement!) */
5331     if (border_explosion && phase == last_phase)
5332       return;
5333   }
5334
5335   if (phase == last_phase)
5336   {
5337     int element;
5338
5339     element = Feld[x][y] = Store[x][y];
5340     Store[x][y] = Store2[x][y] = 0;
5341     GfxElement[x][y] = EL_UNDEFINED;
5342
5343     /* player can escape from explosions and might therefore be still alive */
5344     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5345         element <= EL_PLAYER_IS_EXPLODING_4)
5346     {
5347       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5348       int explosion_element = EL_PLAYER_1 + player_nr;
5349       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5350       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5351
5352       if (level.use_explosion_element[player_nr])
5353         explosion_element = level.explosion_element[player_nr];
5354
5355       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5356                     element_info[explosion_element].content.e[xx][yy]);
5357     }
5358
5359     /* restore probably existing indestructible background element */
5360     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5361       element = Feld[x][y] = Back[x][y];
5362     Back[x][y] = 0;
5363
5364     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5365     GfxDir[x][y] = MV_NONE;
5366     ChangeDelay[x][y] = 0;
5367     ChangePage[x][y] = -1;
5368
5369     CustomValue[x][y] = 0;
5370
5371     InitField_WithBug2(x, y, FALSE);
5372
5373     TEST_DrawLevelField(x, y);
5374
5375     TestIfElementTouchesCustomElement(x, y);
5376
5377     if (GFX_CRUMBLED(element))
5378       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5379
5380     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5381       StorePlayer[x][y] = 0;
5382
5383     if (ELEM_IS_PLAYER(element))
5384       RelocatePlayer(x, y, element);
5385   }
5386   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5387   {
5388     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5389     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5390
5391     if (phase == delay)
5392       TEST_DrawLevelFieldCrumbled(x, y);
5393
5394     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5395     {
5396       DrawLevelElement(x, y, Back[x][y]);
5397       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5398     }
5399     else if (IS_WALKABLE_UNDER(Back[x][y]))
5400     {
5401       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5402       DrawLevelElementThruMask(x, y, Back[x][y]);
5403     }
5404     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5405       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5406   }
5407 }
5408
5409 void DynaExplode(int ex, int ey)
5410 {
5411   int i, j;
5412   int dynabomb_element = Feld[ex][ey];
5413   int dynabomb_size = 1;
5414   boolean dynabomb_xl = FALSE;
5415   struct PlayerInfo *player;
5416   static int xy[4][2] =
5417   {
5418     { 0, -1 },
5419     { -1, 0 },
5420     { +1, 0 },
5421     { 0, +1 }
5422   };
5423
5424   if (IS_ACTIVE_BOMB(dynabomb_element))
5425   {
5426     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5427     dynabomb_size = player->dynabomb_size;
5428     dynabomb_xl = player->dynabomb_xl;
5429     player->dynabombs_left++;
5430   }
5431
5432   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5433
5434   for (i = 0; i < NUM_DIRECTIONS; i++)
5435   {
5436     for (j = 1; j <= dynabomb_size; j++)
5437     {
5438       int x = ex + j * xy[i][0];
5439       int y = ey + j * xy[i][1];
5440       int element;
5441
5442       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5443         break;
5444
5445       element = Feld[x][y];
5446
5447       /* do not restart explosions of fields with active bombs */
5448       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5449         continue;
5450
5451       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5452
5453       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5454           !IS_DIGGABLE(element) && !dynabomb_xl)
5455         break;
5456     }
5457   }
5458 }
5459
5460 void Bang(int x, int y)
5461 {
5462   int element = MovingOrBlocked2Element(x, y);
5463   int explosion_type = EX_TYPE_NORMAL;
5464
5465   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5466   {
5467     struct PlayerInfo *player = PLAYERINFO(x, y);
5468
5469     element = Feld[x][y] = player->initial_element;
5470
5471     if (level.use_explosion_element[player->index_nr])
5472     {
5473       int explosion_element = level.explosion_element[player->index_nr];
5474
5475       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5476         explosion_type = EX_TYPE_CROSS;
5477       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5478         explosion_type = EX_TYPE_CENTER;
5479     }
5480   }
5481
5482   switch (element)
5483   {
5484     case EL_BUG:
5485     case EL_SPACESHIP:
5486     case EL_BD_BUTTERFLY:
5487     case EL_BD_FIREFLY:
5488     case EL_YAMYAM:
5489     case EL_DARK_YAMYAM:
5490     case EL_ROBOT:
5491     case EL_PACMAN:
5492     case EL_MOLE:
5493       RaiseScoreElement(element);
5494       break;
5495
5496     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5497     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5498     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5499     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5500     case EL_DYNABOMB_INCREASE_NUMBER:
5501     case EL_DYNABOMB_INCREASE_SIZE:
5502     case EL_DYNABOMB_INCREASE_POWER:
5503       explosion_type = EX_TYPE_DYNA;
5504       break;
5505
5506     case EL_DC_LANDMINE:
5507       explosion_type = EX_TYPE_CENTER;
5508       break;
5509
5510     case EL_PENGUIN:
5511     case EL_LAMP:
5512     case EL_LAMP_ACTIVE:
5513     case EL_AMOEBA_TO_DIAMOND:
5514       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5515         explosion_type = EX_TYPE_CENTER;
5516       break;
5517
5518     default:
5519       if (element_info[element].explosion_type == EXPLODES_CROSS)
5520         explosion_type = EX_TYPE_CROSS;
5521       else if (element_info[element].explosion_type == EXPLODES_1X1)
5522         explosion_type = EX_TYPE_CENTER;
5523       break;
5524   }
5525
5526   if (explosion_type == EX_TYPE_DYNA)
5527     DynaExplode(x, y);
5528   else
5529     Explode(x, y, EX_PHASE_START, explosion_type);
5530
5531   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5532 }
5533
5534 void SplashAcid(int x, int y)
5535 {
5536   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5537       (!IN_LEV_FIELD(x - 1, y - 2) ||
5538        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5539     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5540
5541   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5542       (!IN_LEV_FIELD(x + 1, y - 2) ||
5543        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5544     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5545
5546   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5547 }
5548
5549 static void InitBeltMovement()
5550 {
5551   static int belt_base_element[4] =
5552   {
5553     EL_CONVEYOR_BELT_1_LEFT,
5554     EL_CONVEYOR_BELT_2_LEFT,
5555     EL_CONVEYOR_BELT_3_LEFT,
5556     EL_CONVEYOR_BELT_4_LEFT
5557   };
5558   static int belt_base_active_element[4] =
5559   {
5560     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5561     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5562     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5563     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5564   };
5565
5566   int x, y, i, j;
5567
5568   /* set frame order for belt animation graphic according to belt direction */
5569   for (i = 0; i < NUM_BELTS; i++)
5570   {
5571     int belt_nr = i;
5572
5573     for (j = 0; j < NUM_BELT_PARTS; j++)
5574     {
5575       int element = belt_base_active_element[belt_nr] + j;
5576       int graphic_1 = el2img(element);
5577       int graphic_2 = el2panelimg(element);
5578
5579       if (game.belt_dir[i] == MV_LEFT)
5580       {
5581         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5582         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5583       }
5584       else
5585       {
5586         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5587         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5588       }
5589     }
5590   }
5591
5592   SCAN_PLAYFIELD(x, y)
5593   {
5594     int element = Feld[x][y];
5595
5596     for (i = 0; i < NUM_BELTS; i++)
5597     {
5598       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5599       {
5600         int e_belt_nr = getBeltNrFromBeltElement(element);
5601         int belt_nr = i;
5602
5603         if (e_belt_nr == belt_nr)
5604         {
5605           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5606
5607           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5608         }
5609       }
5610     }
5611   }
5612 }
5613
5614 static void ToggleBeltSwitch(int x, int y)
5615 {
5616   static int belt_base_element[4] =
5617   {
5618     EL_CONVEYOR_BELT_1_LEFT,
5619     EL_CONVEYOR_BELT_2_LEFT,
5620     EL_CONVEYOR_BELT_3_LEFT,
5621     EL_CONVEYOR_BELT_4_LEFT
5622   };
5623   static int belt_base_active_element[4] =
5624   {
5625     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5626     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5627     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5628     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5629   };
5630   static int belt_base_switch_element[4] =
5631   {
5632     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5633     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5634     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5635     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5636   };
5637   static int belt_move_dir[4] =
5638   {
5639     MV_LEFT,
5640     MV_NONE,
5641     MV_RIGHT,
5642     MV_NONE,
5643   };
5644
5645   int element = Feld[x][y];
5646   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5647   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5648   int belt_dir = belt_move_dir[belt_dir_nr];
5649   int xx, yy, i;
5650
5651   if (!IS_BELT_SWITCH(element))
5652     return;
5653
5654   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5655   game.belt_dir[belt_nr] = belt_dir;
5656
5657   if (belt_dir_nr == 3)
5658     belt_dir_nr = 1;
5659
5660   /* set frame order for belt animation graphic according to belt direction */
5661   for (i = 0; i < NUM_BELT_PARTS; i++)
5662   {
5663     int element = belt_base_active_element[belt_nr] + i;
5664     int graphic_1 = el2img(element);
5665     int graphic_2 = el2panelimg(element);
5666
5667     if (belt_dir == MV_LEFT)
5668     {
5669       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5670       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5671     }
5672     else
5673     {
5674       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5675       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5676     }
5677   }
5678
5679   SCAN_PLAYFIELD(xx, yy)
5680   {
5681     int element = Feld[xx][yy];
5682
5683     if (IS_BELT_SWITCH(element))
5684     {
5685       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5686
5687       if (e_belt_nr == belt_nr)
5688       {
5689         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5690         TEST_DrawLevelField(xx, yy);
5691       }
5692     }
5693     else if (IS_BELT(element) && belt_dir != MV_NONE)
5694     {
5695       int e_belt_nr = getBeltNrFromBeltElement(element);
5696
5697       if (e_belt_nr == belt_nr)
5698       {
5699         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5700
5701         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5702         TEST_DrawLevelField(xx, yy);
5703       }
5704     }
5705     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5706     {
5707       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5708
5709       if (e_belt_nr == belt_nr)
5710       {
5711         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5712
5713         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5714         TEST_DrawLevelField(xx, yy);
5715       }
5716     }
5717   }
5718 }
5719
5720 static void ToggleSwitchgateSwitch(int x, int y)
5721 {
5722   int xx, yy;
5723
5724   game.switchgate_pos = !game.switchgate_pos;
5725
5726   SCAN_PLAYFIELD(xx, yy)
5727   {
5728     int element = Feld[xx][yy];
5729
5730     if (element == EL_SWITCHGATE_SWITCH_UP)
5731     {
5732       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5733       TEST_DrawLevelField(xx, yy);
5734     }
5735     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5736     {
5737       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5738       TEST_DrawLevelField(xx, yy);
5739     }
5740     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5741     {
5742       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5743       TEST_DrawLevelField(xx, yy);
5744     }
5745     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5746     {
5747       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5748       TEST_DrawLevelField(xx, yy);
5749     }
5750     else if (element == EL_SWITCHGATE_OPEN ||
5751              element == EL_SWITCHGATE_OPENING)
5752     {
5753       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5754
5755       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5756     }
5757     else if (element == EL_SWITCHGATE_CLOSED ||
5758              element == EL_SWITCHGATE_CLOSING)
5759     {
5760       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5761
5762       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5763     }
5764   }
5765 }
5766
5767 static int getInvisibleActiveFromInvisibleElement(int element)
5768 {
5769   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5770           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5771           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5772           element);
5773 }
5774
5775 static int getInvisibleFromInvisibleActiveElement(int element)
5776 {
5777   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5778           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5779           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5780           element);
5781 }
5782
5783 static void RedrawAllLightSwitchesAndInvisibleElements()
5784 {
5785   int x, y;
5786
5787   SCAN_PLAYFIELD(x, y)
5788   {
5789     int element = Feld[x][y];
5790
5791     if (element == EL_LIGHT_SWITCH &&
5792         game.light_time_left > 0)
5793     {
5794       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5795       TEST_DrawLevelField(x, y);
5796     }
5797     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5798              game.light_time_left == 0)
5799     {
5800       Feld[x][y] = EL_LIGHT_SWITCH;
5801       TEST_DrawLevelField(x, y);
5802     }
5803     else if (element == EL_EMC_DRIPPER &&
5804              game.light_time_left > 0)
5805     {
5806       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5807       TEST_DrawLevelField(x, y);
5808     }
5809     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5810              game.light_time_left == 0)
5811     {
5812       Feld[x][y] = EL_EMC_DRIPPER;
5813       TEST_DrawLevelField(x, y);
5814     }
5815     else if (element == EL_INVISIBLE_STEELWALL ||
5816              element == EL_INVISIBLE_WALL ||
5817              element == EL_INVISIBLE_SAND)
5818     {
5819       if (game.light_time_left > 0)
5820         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5821
5822       TEST_DrawLevelField(x, y);
5823
5824       /* uncrumble neighbour fields, if needed */
5825       if (element == EL_INVISIBLE_SAND)
5826         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5827     }
5828     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5829              element == EL_INVISIBLE_WALL_ACTIVE ||
5830              element == EL_INVISIBLE_SAND_ACTIVE)
5831     {
5832       if (game.light_time_left == 0)
5833         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5834
5835       TEST_DrawLevelField(x, y);
5836
5837       /* re-crumble neighbour fields, if needed */
5838       if (element == EL_INVISIBLE_SAND)
5839         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5840     }
5841   }
5842 }
5843
5844 static void RedrawAllInvisibleElementsForLenses()
5845 {
5846   int x, y;
5847
5848   SCAN_PLAYFIELD(x, y)
5849   {
5850     int element = Feld[x][y];
5851
5852     if (element == EL_EMC_DRIPPER &&
5853         game.lenses_time_left > 0)
5854     {
5855       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5856       TEST_DrawLevelField(x, y);
5857     }
5858     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5859              game.lenses_time_left == 0)
5860     {
5861       Feld[x][y] = EL_EMC_DRIPPER;
5862       TEST_DrawLevelField(x, y);
5863     }
5864     else if (element == EL_INVISIBLE_STEELWALL ||
5865              element == EL_INVISIBLE_WALL ||
5866              element == EL_INVISIBLE_SAND)
5867     {
5868       if (game.lenses_time_left > 0)
5869         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5870
5871       TEST_DrawLevelField(x, y);
5872
5873       /* uncrumble neighbour fields, if needed */
5874       if (element == EL_INVISIBLE_SAND)
5875         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5876     }
5877     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5878              element == EL_INVISIBLE_WALL_ACTIVE ||
5879              element == EL_INVISIBLE_SAND_ACTIVE)
5880     {
5881       if (game.lenses_time_left == 0)
5882         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5883
5884       TEST_DrawLevelField(x, y);
5885
5886       /* re-crumble neighbour fields, if needed */
5887       if (element == EL_INVISIBLE_SAND)
5888         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5889     }
5890   }
5891 }
5892
5893 static void RedrawAllInvisibleElementsForMagnifier()
5894 {
5895   int x, y;
5896
5897   SCAN_PLAYFIELD(x, y)
5898   {
5899     int element = Feld[x][y];
5900
5901     if (element == EL_EMC_FAKE_GRASS &&
5902         game.magnify_time_left > 0)
5903     {
5904       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5905       TEST_DrawLevelField(x, y);
5906     }
5907     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5908              game.magnify_time_left == 0)
5909     {
5910       Feld[x][y] = EL_EMC_FAKE_GRASS;
5911       TEST_DrawLevelField(x, y);
5912     }
5913     else if (IS_GATE_GRAY(element) &&
5914              game.magnify_time_left > 0)
5915     {
5916       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5917                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5918                     IS_EM_GATE_GRAY(element) ?
5919                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5920                     IS_EMC_GATE_GRAY(element) ?
5921                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5922                     IS_DC_GATE_GRAY(element) ?
5923                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5924                     element);
5925       TEST_DrawLevelField(x, y);
5926     }
5927     else if (IS_GATE_GRAY_ACTIVE(element) &&
5928              game.magnify_time_left == 0)
5929     {
5930       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5931                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5932                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5933                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5934                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5935                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5936                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5937                     EL_DC_GATE_WHITE_GRAY :
5938                     element);
5939       TEST_DrawLevelField(x, y);
5940     }
5941   }
5942 }
5943
5944 static void ToggleLightSwitch(int x, int y)
5945 {
5946   int element = Feld[x][y];
5947
5948   game.light_time_left =
5949     (element == EL_LIGHT_SWITCH ?
5950      level.time_light * FRAMES_PER_SECOND : 0);
5951
5952   RedrawAllLightSwitchesAndInvisibleElements();
5953 }
5954
5955 static void ActivateTimegateSwitch(int x, int y)
5956 {
5957   int xx, yy;
5958
5959   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5960
5961   SCAN_PLAYFIELD(xx, yy)
5962   {
5963     int element = Feld[xx][yy];
5964
5965     if (element == EL_TIMEGATE_CLOSED ||
5966         element == EL_TIMEGATE_CLOSING)
5967     {
5968       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5969       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5970     }
5971
5972     /*
5973     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5974     {
5975       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5976       TEST_DrawLevelField(xx, yy);
5977     }
5978     */
5979
5980   }
5981
5982   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5983                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5984 }
5985
5986 void Impact(int x, int y)
5987 {
5988   boolean last_line = (y == lev_fieldy - 1);
5989   boolean object_hit = FALSE;
5990   boolean impact = (last_line || object_hit);
5991   int element = Feld[x][y];
5992   int smashed = EL_STEELWALL;
5993
5994   if (!last_line)       /* check if element below was hit */
5995   {
5996     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5997       return;
5998
5999     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6000                                          MovDir[x][y + 1] != MV_DOWN ||
6001                                          MovPos[x][y + 1] <= TILEY / 2));
6002
6003     /* do not smash moving elements that left the smashed field in time */
6004     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6005         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6006       object_hit = FALSE;
6007
6008 #if USE_QUICKSAND_IMPACT_BUGFIX
6009     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6010     {
6011       RemoveMovingField(x, y + 1);
6012       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6013       Feld[x][y + 2] = EL_ROCK;
6014       TEST_DrawLevelField(x, y + 2);
6015
6016       object_hit = TRUE;
6017     }
6018
6019     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6020     {
6021       RemoveMovingField(x, y + 1);
6022       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6023       Feld[x][y + 2] = EL_ROCK;
6024       TEST_DrawLevelField(x, y + 2);
6025
6026       object_hit = TRUE;
6027     }
6028 #endif
6029
6030     if (object_hit)
6031       smashed = MovingOrBlocked2Element(x, y + 1);
6032
6033     impact = (last_line || object_hit);
6034   }
6035
6036   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6037   {
6038     SplashAcid(x, y + 1);
6039     return;
6040   }
6041
6042   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6043   /* only reset graphic animation if graphic really changes after impact */
6044   if (impact &&
6045       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6046   {
6047     ResetGfxAnimation(x, y);
6048     TEST_DrawLevelField(x, y);
6049   }
6050
6051   if (impact && CAN_EXPLODE_IMPACT(element))
6052   {
6053     Bang(x, y);
6054     return;
6055   }
6056   else if (impact && element == EL_PEARL &&
6057            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6058   {
6059     ResetGfxAnimation(x, y);
6060
6061     Feld[x][y] = EL_PEARL_BREAKING;
6062     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6063     return;
6064   }
6065   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6066   {
6067     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6068
6069     return;
6070   }
6071
6072   if (impact && element == EL_AMOEBA_DROP)
6073   {
6074     if (object_hit && IS_PLAYER(x, y + 1))
6075       KillPlayerUnlessEnemyProtected(x, y + 1);
6076     else if (object_hit && smashed == EL_PENGUIN)
6077       Bang(x, y + 1);
6078     else
6079     {
6080       Feld[x][y] = EL_AMOEBA_GROWING;
6081       Store[x][y] = EL_AMOEBA_WET;
6082
6083       ResetRandomAnimationValue(x, y);
6084     }
6085     return;
6086   }
6087
6088   if (object_hit)               /* check which object was hit */
6089   {
6090     if ((CAN_PASS_MAGIC_WALL(element) && 
6091          (smashed == EL_MAGIC_WALL ||
6092           smashed == EL_BD_MAGIC_WALL)) ||
6093         (CAN_PASS_DC_MAGIC_WALL(element) &&
6094          smashed == EL_DC_MAGIC_WALL))
6095     {
6096       int xx, yy;
6097       int activated_magic_wall =
6098         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6099          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6100          EL_DC_MAGIC_WALL_ACTIVE);
6101
6102       /* activate magic wall / mill */
6103       SCAN_PLAYFIELD(xx, yy)
6104       {
6105         if (Feld[xx][yy] == smashed)
6106           Feld[xx][yy] = activated_magic_wall;
6107       }
6108
6109       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6110       game.magic_wall_active = TRUE;
6111
6112       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6113                             SND_MAGIC_WALL_ACTIVATING :
6114                             smashed == EL_BD_MAGIC_WALL ?
6115                             SND_BD_MAGIC_WALL_ACTIVATING :
6116                             SND_DC_MAGIC_WALL_ACTIVATING));
6117     }
6118
6119     if (IS_PLAYER(x, y + 1))
6120     {
6121       if (CAN_SMASH_PLAYER(element))
6122       {
6123         KillPlayerUnlessEnemyProtected(x, y + 1);
6124         return;
6125       }
6126     }
6127     else if (smashed == EL_PENGUIN)
6128     {
6129       if (CAN_SMASH_PLAYER(element))
6130       {
6131         Bang(x, y + 1);
6132         return;
6133       }
6134     }
6135     else if (element == EL_BD_DIAMOND)
6136     {
6137       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6138       {
6139         Bang(x, y + 1);
6140         return;
6141       }
6142     }
6143     else if (((element == EL_SP_INFOTRON ||
6144                element == EL_SP_ZONK) &&
6145               (smashed == EL_SP_SNIKSNAK ||
6146                smashed == EL_SP_ELECTRON ||
6147                smashed == EL_SP_DISK_ORANGE)) ||
6148              (element == EL_SP_INFOTRON &&
6149               smashed == EL_SP_DISK_YELLOW))
6150     {
6151       Bang(x, y + 1);
6152       return;
6153     }
6154     else if (CAN_SMASH_EVERYTHING(element))
6155     {
6156       if (IS_CLASSIC_ENEMY(smashed) ||
6157           CAN_EXPLODE_SMASHED(smashed))
6158       {
6159         Bang(x, y + 1);
6160         return;
6161       }
6162       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6163       {
6164         if (smashed == EL_LAMP ||
6165             smashed == EL_LAMP_ACTIVE)
6166         {
6167           Bang(x, y + 1);
6168           return;
6169         }
6170         else if (smashed == EL_NUT)
6171         {
6172           Feld[x][y + 1] = EL_NUT_BREAKING;
6173           PlayLevelSound(x, y, SND_NUT_BREAKING);
6174           RaiseScoreElement(EL_NUT);
6175           return;
6176         }
6177         else if (smashed == EL_PEARL)
6178         {
6179           ResetGfxAnimation(x, y);
6180
6181           Feld[x][y + 1] = EL_PEARL_BREAKING;
6182           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6183           return;
6184         }
6185         else if (smashed == EL_DIAMOND)
6186         {
6187           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6188           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6189           return;
6190         }
6191         else if (IS_BELT_SWITCH(smashed))
6192         {
6193           ToggleBeltSwitch(x, y + 1);
6194         }
6195         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6196                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6197                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6198                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6199         {
6200           ToggleSwitchgateSwitch(x, y + 1);
6201         }
6202         else if (smashed == EL_LIGHT_SWITCH ||
6203                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6204         {
6205           ToggleLightSwitch(x, y + 1);
6206         }
6207         else
6208         {
6209           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6210
6211           CheckElementChangeBySide(x, y + 1, smashed, element,
6212                                    CE_SWITCHED, CH_SIDE_TOP);
6213           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6214                                             CH_SIDE_TOP);
6215         }
6216       }
6217       else
6218       {
6219         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6220       }
6221     }
6222   }
6223
6224   /* play sound of magic wall / mill */
6225   if (!last_line &&
6226       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6227        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6228        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6229   {
6230     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6231       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6232     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6233       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6234     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6235       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6236
6237     return;
6238   }
6239
6240   /* play sound of object that hits the ground */
6241   if (last_line || object_hit)
6242     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6243 }
6244
6245 inline static void TurnRoundExt(int x, int y)
6246 {
6247   static struct
6248   {
6249     int dx, dy;
6250   } move_xy[] =
6251   {
6252     {  0,  0 },
6253     { -1,  0 },
6254     { +1,  0 },
6255     {  0,  0 },
6256     {  0, -1 },
6257     {  0,  0 }, { 0, 0 }, { 0, 0 },
6258     {  0, +1 }
6259   };
6260   static struct
6261   {
6262     int left, right, back;
6263   } turn[] =
6264   {
6265     { 0,        0,              0        },
6266     { MV_DOWN,  MV_UP,          MV_RIGHT },
6267     { MV_UP,    MV_DOWN,        MV_LEFT  },
6268     { 0,        0,              0        },
6269     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6270     { 0,        0,              0        },
6271     { 0,        0,              0        },
6272     { 0,        0,              0        },
6273     { MV_RIGHT, MV_LEFT,        MV_UP    }
6274   };
6275
6276   int element = Feld[x][y];
6277   int move_pattern = element_info[element].move_pattern;
6278
6279   int old_move_dir = MovDir[x][y];
6280   int left_dir  = turn[old_move_dir].left;
6281   int right_dir = turn[old_move_dir].right;
6282   int back_dir  = turn[old_move_dir].back;
6283
6284   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6285   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6286   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6287   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6288
6289   int left_x  = x + left_dx,  left_y  = y + left_dy;
6290   int right_x = x + right_dx, right_y = y + right_dy;
6291   int move_x  = x + move_dx,  move_y  = y + move_dy;
6292
6293   int xx, yy;
6294
6295   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6296   {
6297     TestIfBadThingTouchesOtherBadThing(x, y);
6298
6299     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6300       MovDir[x][y] = right_dir;
6301     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6302       MovDir[x][y] = left_dir;
6303
6304     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6305       MovDelay[x][y] = 9;
6306     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6307       MovDelay[x][y] = 1;
6308   }
6309   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6310   {
6311     TestIfBadThingTouchesOtherBadThing(x, y);
6312
6313     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6314       MovDir[x][y] = left_dir;
6315     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6316       MovDir[x][y] = right_dir;
6317
6318     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6319       MovDelay[x][y] = 9;
6320     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6321       MovDelay[x][y] = 1;
6322   }
6323   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6324   {
6325     TestIfBadThingTouchesOtherBadThing(x, y);
6326
6327     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6328       MovDir[x][y] = left_dir;
6329     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6330       MovDir[x][y] = right_dir;
6331
6332     if (MovDir[x][y] != old_move_dir)
6333       MovDelay[x][y] = 9;
6334   }
6335   else if (element == EL_YAMYAM)
6336   {
6337     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6338     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6339
6340     if (can_turn_left && can_turn_right)
6341       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6342     else if (can_turn_left)
6343       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6344     else if (can_turn_right)
6345       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6346     else
6347       MovDir[x][y] = back_dir;
6348
6349     MovDelay[x][y] = 16 + 16 * RND(3);
6350   }
6351   else if (element == EL_DARK_YAMYAM)
6352   {
6353     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6354                                                          left_x, left_y);
6355     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6356                                                          right_x, right_y);
6357
6358     if (can_turn_left && can_turn_right)
6359       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6360     else if (can_turn_left)
6361       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6362     else if (can_turn_right)
6363       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6364     else
6365       MovDir[x][y] = back_dir;
6366
6367     MovDelay[x][y] = 16 + 16 * RND(3);
6368   }
6369   else if (element == EL_PACMAN)
6370   {
6371     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6372     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6373
6374     if (can_turn_left && can_turn_right)
6375       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6376     else if (can_turn_left)
6377       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6378     else if (can_turn_right)
6379       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6380     else
6381       MovDir[x][y] = back_dir;
6382
6383     MovDelay[x][y] = 6 + RND(40);
6384   }
6385   else if (element == EL_PIG)
6386   {
6387     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6388     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6389     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6390     boolean should_turn_left, should_turn_right, should_move_on;
6391     int rnd_value = 24;
6392     int rnd = RND(rnd_value);
6393
6394     should_turn_left = (can_turn_left &&
6395                         (!can_move_on ||
6396                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6397                                                    y + back_dy + left_dy)));
6398     should_turn_right = (can_turn_right &&
6399                          (!can_move_on ||
6400                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6401                                                     y + back_dy + right_dy)));
6402     should_move_on = (can_move_on &&
6403                       (!can_turn_left ||
6404                        !can_turn_right ||
6405                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6406                                                  y + move_dy + left_dy) ||
6407                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6408                                                  y + move_dy + right_dy)));
6409
6410     if (should_turn_left || should_turn_right || should_move_on)
6411     {
6412       if (should_turn_left && should_turn_right && should_move_on)
6413         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6414                         rnd < 2 * rnd_value / 3 ? right_dir :
6415                         old_move_dir);
6416       else if (should_turn_left && should_turn_right)
6417         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6418       else if (should_turn_left && should_move_on)
6419         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6420       else if (should_turn_right && should_move_on)
6421         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6422       else if (should_turn_left)
6423         MovDir[x][y] = left_dir;
6424       else if (should_turn_right)
6425         MovDir[x][y] = right_dir;
6426       else if (should_move_on)
6427         MovDir[x][y] = old_move_dir;
6428     }
6429     else if (can_move_on && rnd > rnd_value / 8)
6430       MovDir[x][y] = old_move_dir;
6431     else if (can_turn_left && can_turn_right)
6432       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6433     else if (can_turn_left && rnd > rnd_value / 8)
6434       MovDir[x][y] = left_dir;
6435     else if (can_turn_right && rnd > rnd_value/8)
6436       MovDir[x][y] = right_dir;
6437     else
6438       MovDir[x][y] = back_dir;
6439
6440     xx = x + move_xy[MovDir[x][y]].dx;
6441     yy = y + move_xy[MovDir[x][y]].dy;
6442
6443     if (!IN_LEV_FIELD(xx, yy) ||
6444         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6445       MovDir[x][y] = old_move_dir;
6446
6447     MovDelay[x][y] = 0;
6448   }
6449   else if (element == EL_DRAGON)
6450   {
6451     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6452     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6453     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6454     int rnd_value = 24;
6455     int rnd = RND(rnd_value);
6456
6457     if (can_move_on && rnd > rnd_value / 8)
6458       MovDir[x][y] = old_move_dir;
6459     else if (can_turn_left && can_turn_right)
6460       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6461     else if (can_turn_left && rnd > rnd_value / 8)
6462       MovDir[x][y] = left_dir;
6463     else if (can_turn_right && rnd > rnd_value / 8)
6464       MovDir[x][y] = right_dir;
6465     else
6466       MovDir[x][y] = back_dir;
6467
6468     xx = x + move_xy[MovDir[x][y]].dx;
6469     yy = y + move_xy[MovDir[x][y]].dy;
6470
6471     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6472       MovDir[x][y] = old_move_dir;
6473
6474     MovDelay[x][y] = 0;
6475   }
6476   else if (element == EL_MOLE)
6477   {
6478     boolean can_move_on =
6479       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6480                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6481                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6482     if (!can_move_on)
6483     {
6484       boolean can_turn_left =
6485         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6486                               IS_AMOEBOID(Feld[left_x][left_y])));
6487
6488       boolean can_turn_right =
6489         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6490                               IS_AMOEBOID(Feld[right_x][right_y])));
6491
6492       if (can_turn_left && can_turn_right)
6493         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6494       else if (can_turn_left)
6495         MovDir[x][y] = left_dir;
6496       else
6497         MovDir[x][y] = right_dir;
6498     }
6499
6500     if (MovDir[x][y] != old_move_dir)
6501       MovDelay[x][y] = 9;
6502   }
6503   else if (element == EL_BALLOON)
6504   {
6505     MovDir[x][y] = game.wind_direction;
6506     MovDelay[x][y] = 0;
6507   }
6508   else if (element == EL_SPRING)
6509   {
6510     if (MovDir[x][y] & MV_HORIZONTAL)
6511     {
6512       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6513           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6514       {
6515         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6516         ResetGfxAnimation(move_x, move_y);
6517         TEST_DrawLevelField(move_x, move_y);
6518
6519         MovDir[x][y] = back_dir;
6520       }
6521       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6522                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6523         MovDir[x][y] = MV_NONE;
6524     }
6525
6526     MovDelay[x][y] = 0;
6527   }
6528   else if (element == EL_ROBOT ||
6529            element == EL_SATELLITE ||
6530            element == EL_PENGUIN ||
6531            element == EL_EMC_ANDROID)
6532   {
6533     int attr_x = -1, attr_y = -1;
6534
6535     if (AllPlayersGone)
6536     {
6537       attr_x = ExitX;
6538       attr_y = ExitY;
6539     }
6540     else
6541     {
6542       int i;
6543
6544       for (i = 0; i < MAX_PLAYERS; i++)
6545       {
6546         struct PlayerInfo *player = &stored_player[i];
6547         int jx = player->jx, jy = player->jy;
6548
6549         if (!player->active)
6550           continue;
6551
6552         if (attr_x == -1 ||
6553             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6554         {
6555           attr_x = jx;
6556           attr_y = jy;
6557         }
6558       }
6559     }
6560
6561     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6562         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6563          game.engine_version < VERSION_IDENT(3,1,0,0)))
6564     {
6565       attr_x = ZX;
6566       attr_y = ZY;
6567     }
6568
6569     if (element == EL_PENGUIN)
6570     {
6571       int i;
6572       static int xy[4][2] =
6573       {
6574         { 0, -1 },
6575         { -1, 0 },
6576         { +1, 0 },
6577         { 0, +1 }
6578       };
6579
6580       for (i = 0; i < NUM_DIRECTIONS; i++)
6581       {
6582         int ex = x + xy[i][0];
6583         int ey = y + xy[i][1];
6584
6585         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6586                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6587                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6588                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6589         {
6590           attr_x = ex;
6591           attr_y = ey;
6592           break;
6593         }
6594       }
6595     }
6596
6597     MovDir[x][y] = MV_NONE;
6598     if (attr_x < x)
6599       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6600     else if (attr_x > x)
6601       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6602     if (attr_y < y)
6603       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6604     else if (attr_y > y)
6605       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6606
6607     if (element == EL_ROBOT)
6608     {
6609       int newx, newy;
6610
6611       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6612         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6613       Moving2Blocked(x, y, &newx, &newy);
6614
6615       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6616         MovDelay[x][y] = 8 + 8 * !RND(3);
6617       else
6618         MovDelay[x][y] = 16;
6619     }
6620     else if (element == EL_PENGUIN)
6621     {
6622       int newx, newy;
6623
6624       MovDelay[x][y] = 1;
6625
6626       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6627       {
6628         boolean first_horiz = RND(2);
6629         int new_move_dir = MovDir[x][y];
6630
6631         MovDir[x][y] =
6632           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6633         Moving2Blocked(x, y, &newx, &newy);
6634
6635         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6636           return;
6637
6638         MovDir[x][y] =
6639           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6640         Moving2Blocked(x, y, &newx, &newy);
6641
6642         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6643           return;
6644
6645         MovDir[x][y] = old_move_dir;
6646         return;
6647       }
6648     }
6649     else if (element == EL_SATELLITE)
6650     {
6651       int newx, newy;
6652
6653       MovDelay[x][y] = 1;
6654
6655       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6656       {
6657         boolean first_horiz = RND(2);
6658         int new_move_dir = MovDir[x][y];
6659
6660         MovDir[x][y] =
6661           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6662         Moving2Blocked(x, y, &newx, &newy);
6663
6664         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6665           return;
6666
6667         MovDir[x][y] =
6668           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6669         Moving2Blocked(x, y, &newx, &newy);
6670
6671         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6672           return;
6673
6674         MovDir[x][y] = old_move_dir;
6675         return;
6676       }
6677     }
6678     else if (element == EL_EMC_ANDROID)
6679     {
6680       static int check_pos[16] =
6681       {
6682         -1,             /*  0 => (invalid)          */
6683         7,              /*  1 => MV_LEFT            */
6684         3,              /*  2 => MV_RIGHT           */
6685         -1,             /*  3 => (invalid)          */
6686         1,              /*  4 =>            MV_UP   */
6687         0,              /*  5 => MV_LEFT  | MV_UP   */
6688         2,              /*  6 => MV_RIGHT | MV_UP   */
6689         -1,             /*  7 => (invalid)          */
6690         5,              /*  8 =>            MV_DOWN */
6691         6,              /*  9 => MV_LEFT  | MV_DOWN */
6692         4,              /* 10 => MV_RIGHT | MV_DOWN */
6693         -1,             /* 11 => (invalid)          */
6694         -1,             /* 12 => (invalid)          */
6695         -1,             /* 13 => (invalid)          */
6696         -1,             /* 14 => (invalid)          */
6697         -1,             /* 15 => (invalid)          */
6698       };
6699       static struct
6700       {
6701         int dx, dy;
6702         int dir;
6703       } check_xy[8] =
6704       {
6705         { -1, -1,       MV_LEFT  | MV_UP   },
6706         {  0, -1,                  MV_UP   },
6707         { +1, -1,       MV_RIGHT | MV_UP   },
6708         { +1,  0,       MV_RIGHT           },
6709         { +1, +1,       MV_RIGHT | MV_DOWN },
6710         {  0, +1,                  MV_DOWN },
6711         { -1, +1,       MV_LEFT  | MV_DOWN },
6712         { -1,  0,       MV_LEFT            },
6713       };
6714       int start_pos, check_order;
6715       boolean can_clone = FALSE;
6716       int i;
6717
6718       /* check if there is any free field around current position */
6719       for (i = 0; i < 8; i++)
6720       {
6721         int newx = x + check_xy[i].dx;
6722         int newy = y + check_xy[i].dy;
6723
6724         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6725         {
6726           can_clone = TRUE;
6727
6728           break;
6729         }
6730       }
6731
6732       if (can_clone)            /* randomly find an element to clone */
6733       {
6734         can_clone = FALSE;
6735
6736         start_pos = check_pos[RND(8)];
6737         check_order = (RND(2) ? -1 : +1);
6738
6739         for (i = 0; i < 8; i++)
6740         {
6741           int pos_raw = start_pos + i * check_order;
6742           int pos = (pos_raw + 8) % 8;
6743           int newx = x + check_xy[pos].dx;
6744           int newy = y + check_xy[pos].dy;
6745
6746           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6747           {
6748             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6749             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6750
6751             Store[x][y] = Feld[newx][newy];
6752
6753             can_clone = TRUE;
6754
6755             break;
6756           }
6757         }
6758       }
6759
6760       if (can_clone)            /* randomly find a direction to move */
6761       {
6762         can_clone = FALSE;
6763
6764         start_pos = check_pos[RND(8)];
6765         check_order = (RND(2) ? -1 : +1);
6766
6767         for (i = 0; i < 8; i++)
6768         {
6769           int pos_raw = start_pos + i * check_order;
6770           int pos = (pos_raw + 8) % 8;
6771           int newx = x + check_xy[pos].dx;
6772           int newy = y + check_xy[pos].dy;
6773           int new_move_dir = check_xy[pos].dir;
6774
6775           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6776           {
6777             MovDir[x][y] = new_move_dir;
6778             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6779
6780             can_clone = TRUE;
6781
6782             break;
6783           }
6784         }
6785       }
6786
6787       if (can_clone)            /* cloning and moving successful */
6788         return;
6789
6790       /* cannot clone -- try to move towards player */
6791
6792       start_pos = check_pos[MovDir[x][y] & 0x0f];
6793       check_order = (RND(2) ? -1 : +1);
6794
6795       for (i = 0; i < 3; i++)
6796       {
6797         /* first check start_pos, then previous/next or (next/previous) pos */
6798         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6799         int pos = (pos_raw + 8) % 8;
6800         int newx = x + check_xy[pos].dx;
6801         int newy = y + check_xy[pos].dy;
6802         int new_move_dir = check_xy[pos].dir;
6803
6804         if (IS_PLAYER(newx, newy))
6805           break;
6806
6807         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6808         {
6809           MovDir[x][y] = new_move_dir;
6810           MovDelay[x][y] = level.android_move_time * 8 + 1;
6811
6812           break;
6813         }
6814       }
6815     }
6816   }
6817   else if (move_pattern == MV_TURNING_LEFT ||
6818            move_pattern == MV_TURNING_RIGHT ||
6819            move_pattern == MV_TURNING_LEFT_RIGHT ||
6820            move_pattern == MV_TURNING_RIGHT_LEFT ||
6821            move_pattern == MV_TURNING_RANDOM ||
6822            move_pattern == MV_ALL_DIRECTIONS)
6823   {
6824     boolean can_turn_left =
6825       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6826     boolean can_turn_right =
6827       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6828
6829     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6830       return;
6831
6832     if (move_pattern == MV_TURNING_LEFT)
6833       MovDir[x][y] = left_dir;
6834     else if (move_pattern == MV_TURNING_RIGHT)
6835       MovDir[x][y] = right_dir;
6836     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6837       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6838     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6839       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6840     else if (move_pattern == MV_TURNING_RANDOM)
6841       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6842                       can_turn_right && !can_turn_left ? right_dir :
6843                       RND(2) ? left_dir : right_dir);
6844     else if (can_turn_left && can_turn_right)
6845       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6846     else if (can_turn_left)
6847       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6848     else if (can_turn_right)
6849       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6850     else
6851       MovDir[x][y] = back_dir;
6852
6853     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6854   }
6855   else if (move_pattern == MV_HORIZONTAL ||
6856            move_pattern == MV_VERTICAL)
6857   {
6858     if (move_pattern & old_move_dir)
6859       MovDir[x][y] = back_dir;
6860     else if (move_pattern == MV_HORIZONTAL)
6861       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6862     else if (move_pattern == MV_VERTICAL)
6863       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6864
6865     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6866   }
6867   else if (move_pattern & MV_ANY_DIRECTION)
6868   {
6869     MovDir[x][y] = move_pattern;
6870     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6871   }
6872   else if (move_pattern & MV_WIND_DIRECTION)
6873   {
6874     MovDir[x][y] = game.wind_direction;
6875     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6876   }
6877   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6878   {
6879     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6880       MovDir[x][y] = left_dir;
6881     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6882       MovDir[x][y] = right_dir;
6883
6884     if (MovDir[x][y] != old_move_dir)
6885       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6886   }
6887   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6888   {
6889     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6890       MovDir[x][y] = right_dir;
6891     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6892       MovDir[x][y] = left_dir;
6893
6894     if (MovDir[x][y] != old_move_dir)
6895       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896   }
6897   else if (move_pattern == MV_TOWARDS_PLAYER ||
6898            move_pattern == MV_AWAY_FROM_PLAYER)
6899   {
6900     int attr_x = -1, attr_y = -1;
6901     int newx, newy;
6902     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6903
6904     if (AllPlayersGone)
6905     {
6906       attr_x = ExitX;
6907       attr_y = ExitY;
6908     }
6909     else
6910     {
6911       int i;
6912
6913       for (i = 0; i < MAX_PLAYERS; i++)
6914       {
6915         struct PlayerInfo *player = &stored_player[i];
6916         int jx = player->jx, jy = player->jy;
6917
6918         if (!player->active)
6919           continue;
6920
6921         if (attr_x == -1 ||
6922             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6923         {
6924           attr_x = jx;
6925           attr_y = jy;
6926         }
6927       }
6928     }
6929
6930     MovDir[x][y] = MV_NONE;
6931     if (attr_x < x)
6932       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6933     else if (attr_x > x)
6934       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6935     if (attr_y < y)
6936       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6937     else if (attr_y > y)
6938       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6939
6940     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6941
6942     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6943     {
6944       boolean first_horiz = RND(2);
6945       int new_move_dir = MovDir[x][y];
6946
6947       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6948       {
6949         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6950         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6951
6952         return;
6953       }
6954
6955       MovDir[x][y] =
6956         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6957       Moving2Blocked(x, y, &newx, &newy);
6958
6959       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6960         return;
6961
6962       MovDir[x][y] =
6963         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6964       Moving2Blocked(x, y, &newx, &newy);
6965
6966       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6967         return;
6968
6969       MovDir[x][y] = old_move_dir;
6970     }
6971   }
6972   else if (move_pattern == MV_WHEN_PUSHED ||
6973            move_pattern == MV_WHEN_DROPPED)
6974   {
6975     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6976       MovDir[x][y] = MV_NONE;
6977
6978     MovDelay[x][y] = 0;
6979   }
6980   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6981   {
6982     static int test_xy[7][2] =
6983     {
6984       { 0, -1 },
6985       { -1, 0 },
6986       { +1, 0 },
6987       { 0, +1 },
6988       { 0, -1 },
6989       { -1, 0 },
6990       { +1, 0 },
6991     };
6992     static int test_dir[7] =
6993     {
6994       MV_UP,
6995       MV_LEFT,
6996       MV_RIGHT,
6997       MV_DOWN,
6998       MV_UP,
6999       MV_LEFT,
7000       MV_RIGHT,
7001     };
7002     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7003     int move_preference = -1000000;     /* start with very low preference */
7004     int new_move_dir = MV_NONE;
7005     int start_test = RND(4);
7006     int i;
7007
7008     for (i = 0; i < NUM_DIRECTIONS; i++)
7009     {
7010       int move_dir = test_dir[start_test + i];
7011       int move_dir_preference;
7012
7013       xx = x + test_xy[start_test + i][0];
7014       yy = y + test_xy[start_test + i][1];
7015
7016       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7017           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7018       {
7019         new_move_dir = move_dir;
7020
7021         break;
7022       }
7023
7024       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7025         continue;
7026
7027       move_dir_preference = -1 * RunnerVisit[xx][yy];
7028       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7029         move_dir_preference = PlayerVisit[xx][yy];
7030
7031       if (move_dir_preference > move_preference)
7032       {
7033         /* prefer field that has not been visited for the longest time */
7034         move_preference = move_dir_preference;
7035         new_move_dir = move_dir;
7036       }
7037       else if (move_dir_preference == move_preference &&
7038                move_dir == old_move_dir)
7039       {
7040         /* prefer last direction when all directions are preferred equally */
7041         move_preference = move_dir_preference;
7042         new_move_dir = move_dir;
7043       }
7044     }
7045
7046     MovDir[x][y] = new_move_dir;
7047     if (old_move_dir != new_move_dir)
7048       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7049   }
7050 }
7051
7052 static void TurnRound(int x, int y)
7053 {
7054   int direction = MovDir[x][y];
7055
7056   TurnRoundExt(x, y);
7057
7058   GfxDir[x][y] = MovDir[x][y];
7059
7060   if (direction != MovDir[x][y])
7061     GfxFrame[x][y] = 0;
7062
7063   if (MovDelay[x][y])
7064     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7065
7066   ResetGfxFrame(x, y, FALSE);
7067 }
7068
7069 static boolean JustBeingPushed(int x, int y)
7070 {
7071   int i;
7072
7073   for (i = 0; i < MAX_PLAYERS; i++)
7074   {
7075     struct PlayerInfo *player = &stored_player[i];
7076
7077     if (player->active && player->is_pushing && player->MovPos)
7078     {
7079       int next_jx = player->jx + (player->jx - player->last_jx);
7080       int next_jy = player->jy + (player->jy - player->last_jy);
7081
7082       if (x == next_jx && y == next_jy)
7083         return TRUE;
7084     }
7085   }
7086
7087   return FALSE;
7088 }
7089
7090 void StartMoving(int x, int y)
7091 {
7092   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7093   int element = Feld[x][y];
7094
7095   if (Stop[x][y])
7096     return;
7097
7098   if (MovDelay[x][y] == 0)
7099     GfxAction[x][y] = ACTION_DEFAULT;
7100
7101   if (CAN_FALL(element) && y < lev_fieldy - 1)
7102   {
7103     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7104         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7105       if (JustBeingPushed(x, y))
7106         return;
7107
7108     if (element == EL_QUICKSAND_FULL)
7109     {
7110       if (IS_FREE(x, y + 1))
7111       {
7112         InitMovingField(x, y, MV_DOWN);
7113         started_moving = TRUE;
7114
7115         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7116 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7117         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7118           Store[x][y] = EL_ROCK;
7119 #else
7120         Store[x][y] = EL_ROCK;
7121 #endif
7122
7123         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7124       }
7125       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7126       {
7127         if (!MovDelay[x][y])
7128         {
7129           MovDelay[x][y] = TILEY + 1;
7130
7131           ResetGfxAnimation(x, y);
7132           ResetGfxAnimation(x, y + 1);
7133         }
7134
7135         if (MovDelay[x][y])
7136         {
7137           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7138           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7139
7140           MovDelay[x][y]--;
7141           if (MovDelay[x][y])
7142             return;
7143         }
7144
7145         Feld[x][y] = EL_QUICKSAND_EMPTY;
7146         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7147         Store[x][y + 1] = Store[x][y];
7148         Store[x][y] = 0;
7149
7150         PlayLevelSoundAction(x, y, ACTION_FILLING);
7151       }
7152       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7153       {
7154         if (!MovDelay[x][y])
7155         {
7156           MovDelay[x][y] = TILEY + 1;
7157
7158           ResetGfxAnimation(x, y);
7159           ResetGfxAnimation(x, y + 1);
7160         }
7161
7162         if (MovDelay[x][y])
7163         {
7164           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7165           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7166
7167           MovDelay[x][y]--;
7168           if (MovDelay[x][y])
7169             return;
7170         }
7171
7172         Feld[x][y] = EL_QUICKSAND_EMPTY;
7173         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7174         Store[x][y + 1] = Store[x][y];
7175         Store[x][y] = 0;
7176
7177         PlayLevelSoundAction(x, y, ACTION_FILLING);
7178       }
7179     }
7180     else if (element == EL_QUICKSAND_FAST_FULL)
7181     {
7182       if (IS_FREE(x, y + 1))
7183       {
7184         InitMovingField(x, y, MV_DOWN);
7185         started_moving = TRUE;
7186
7187         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7188 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7189         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7190           Store[x][y] = EL_ROCK;
7191 #else
7192         Store[x][y] = EL_ROCK;
7193 #endif
7194
7195         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7196       }
7197       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7198       {
7199         if (!MovDelay[x][y])
7200         {
7201           MovDelay[x][y] = TILEY + 1;
7202
7203           ResetGfxAnimation(x, y);
7204           ResetGfxAnimation(x, y + 1);
7205         }
7206
7207         if (MovDelay[x][y])
7208         {
7209           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7210           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7211
7212           MovDelay[x][y]--;
7213           if (MovDelay[x][y])
7214             return;
7215         }
7216
7217         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7218         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7219         Store[x][y + 1] = Store[x][y];
7220         Store[x][y] = 0;
7221
7222         PlayLevelSoundAction(x, y, ACTION_FILLING);
7223       }
7224       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7225       {
7226         if (!MovDelay[x][y])
7227         {
7228           MovDelay[x][y] = TILEY + 1;
7229
7230           ResetGfxAnimation(x, y);
7231           ResetGfxAnimation(x, y + 1);
7232         }
7233
7234         if (MovDelay[x][y])
7235         {
7236           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7237           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7238
7239           MovDelay[x][y]--;
7240           if (MovDelay[x][y])
7241             return;
7242         }
7243
7244         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7245         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7246         Store[x][y + 1] = Store[x][y];
7247         Store[x][y] = 0;
7248
7249         PlayLevelSoundAction(x, y, ACTION_FILLING);
7250       }
7251     }
7252     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7253              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7254     {
7255       InitMovingField(x, y, MV_DOWN);
7256       started_moving = TRUE;
7257
7258       Feld[x][y] = EL_QUICKSAND_FILLING;
7259       Store[x][y] = element;
7260
7261       PlayLevelSoundAction(x, y, ACTION_FILLING);
7262     }
7263     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7264              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7265     {
7266       InitMovingField(x, y, MV_DOWN);
7267       started_moving = TRUE;
7268
7269       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7270       Store[x][y] = element;
7271
7272       PlayLevelSoundAction(x, y, ACTION_FILLING);
7273     }
7274     else if (element == EL_MAGIC_WALL_FULL)
7275     {
7276       if (IS_FREE(x, y + 1))
7277       {
7278         InitMovingField(x, y, MV_DOWN);
7279         started_moving = TRUE;
7280
7281         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7282         Store[x][y] = EL_CHANGED(Store[x][y]);
7283       }
7284       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7285       {
7286         if (!MovDelay[x][y])
7287           MovDelay[x][y] = TILEY / 4 + 1;
7288
7289         if (MovDelay[x][y])
7290         {
7291           MovDelay[x][y]--;
7292           if (MovDelay[x][y])
7293             return;
7294         }
7295
7296         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7297         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7298         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7299         Store[x][y] = 0;
7300       }
7301     }
7302     else if (element == EL_BD_MAGIC_WALL_FULL)
7303     {
7304       if (IS_FREE(x, y + 1))
7305       {
7306         InitMovingField(x, y, MV_DOWN);
7307         started_moving = TRUE;
7308
7309         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7310         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7311       }
7312       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7313       {
7314         if (!MovDelay[x][y])
7315           MovDelay[x][y] = TILEY / 4 + 1;
7316
7317         if (MovDelay[x][y])
7318         {
7319           MovDelay[x][y]--;
7320           if (MovDelay[x][y])
7321             return;
7322         }
7323
7324         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7325         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7326         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7327         Store[x][y] = 0;
7328       }
7329     }
7330     else if (element == EL_DC_MAGIC_WALL_FULL)
7331     {
7332       if (IS_FREE(x, y + 1))
7333       {
7334         InitMovingField(x, y, MV_DOWN);
7335         started_moving = TRUE;
7336
7337         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7338         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7339       }
7340       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7341       {
7342         if (!MovDelay[x][y])
7343           MovDelay[x][y] = TILEY / 4 + 1;
7344
7345         if (MovDelay[x][y])
7346         {
7347           MovDelay[x][y]--;
7348           if (MovDelay[x][y])
7349             return;
7350         }
7351
7352         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7353         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7354         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7355         Store[x][y] = 0;
7356       }
7357     }
7358     else if ((CAN_PASS_MAGIC_WALL(element) &&
7359               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7360                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7361              (CAN_PASS_DC_MAGIC_WALL(element) &&
7362               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7363
7364     {
7365       InitMovingField(x, y, MV_DOWN);
7366       started_moving = TRUE;
7367
7368       Feld[x][y] =
7369         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7370          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7371          EL_DC_MAGIC_WALL_FILLING);
7372       Store[x][y] = element;
7373     }
7374     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7375     {
7376       SplashAcid(x, y + 1);
7377
7378       InitMovingField(x, y, MV_DOWN);
7379       started_moving = TRUE;
7380
7381       Store[x][y] = EL_ACID;
7382     }
7383     else if (
7384              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7385               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7386              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7387               CAN_FALL(element) && WasJustFalling[x][y] &&
7388               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7389
7390              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7391               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7392               (Feld[x][y + 1] == EL_BLOCKED)))
7393     {
7394       /* this is needed for a special case not covered by calling "Impact()"
7395          from "ContinueMoving()": if an element moves to a tile directly below
7396          another element which was just falling on that tile (which was empty
7397          in the previous frame), the falling element above would just stop
7398          instead of smashing the element below (in previous version, the above
7399          element was just checked for "moving" instead of "falling", resulting
7400          in incorrect smashes caused by horizontal movement of the above
7401          element; also, the case of the player being the element to smash was
7402          simply not covered here... :-/ ) */
7403
7404       CheckCollision[x][y] = 0;
7405       CheckImpact[x][y] = 0;
7406
7407       Impact(x, y);
7408     }
7409     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7410     {
7411       if (MovDir[x][y] == MV_NONE)
7412       {
7413         InitMovingField(x, y, MV_DOWN);
7414         started_moving = TRUE;
7415       }
7416     }
7417     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7418     {
7419       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7420         MovDir[x][y] = MV_DOWN;
7421
7422       InitMovingField(x, y, MV_DOWN);
7423       started_moving = TRUE;
7424     }
7425     else if (element == EL_AMOEBA_DROP)
7426     {
7427       Feld[x][y] = EL_AMOEBA_GROWING;
7428       Store[x][y] = EL_AMOEBA_WET;
7429     }
7430     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7431               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7432              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7433              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7434     {
7435       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7436                                 (IS_FREE(x - 1, y + 1) ||
7437                                  Feld[x - 1][y + 1] == EL_ACID));
7438       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7439                                 (IS_FREE(x + 1, y + 1) ||
7440                                  Feld[x + 1][y + 1] == EL_ACID));
7441       boolean can_fall_any  = (can_fall_left || can_fall_right);
7442       boolean can_fall_both = (can_fall_left && can_fall_right);
7443       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7444
7445       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7446       {
7447         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7448           can_fall_right = FALSE;
7449         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7450           can_fall_left = FALSE;
7451         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7452           can_fall_right = FALSE;
7453         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7454           can_fall_left = FALSE;
7455
7456         can_fall_any  = (can_fall_left || can_fall_right);
7457         can_fall_both = FALSE;
7458       }
7459
7460       if (can_fall_both)
7461       {
7462         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7463           can_fall_right = FALSE;       /* slip down on left side */
7464         else
7465           can_fall_left = !(can_fall_right = RND(2));
7466
7467         can_fall_both = FALSE;
7468       }
7469
7470       if (can_fall_any)
7471       {
7472         /* if not determined otherwise, prefer left side for slipping down */
7473         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7474         started_moving = TRUE;
7475       }
7476     }
7477     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7478     {
7479       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7480       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7481       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7482       int belt_dir = game.belt_dir[belt_nr];
7483
7484       if ((belt_dir == MV_LEFT  && left_is_free) ||
7485           (belt_dir == MV_RIGHT && right_is_free))
7486       {
7487         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7488
7489         InitMovingField(x, y, belt_dir);
7490         started_moving = TRUE;
7491
7492         Pushed[x][y] = TRUE;
7493         Pushed[nextx][y] = TRUE;
7494
7495         GfxAction[x][y] = ACTION_DEFAULT;
7496       }
7497       else
7498       {
7499         MovDir[x][y] = 0;       /* if element was moving, stop it */
7500       }
7501     }
7502   }
7503
7504   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7505   if (CAN_MOVE(element) && !started_moving)
7506   {
7507     int move_pattern = element_info[element].move_pattern;
7508     int newx, newy;
7509
7510     Moving2Blocked(x, y, &newx, &newy);
7511
7512     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7513       return;
7514
7515     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7516         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7517     {
7518       WasJustMoving[x][y] = 0;
7519       CheckCollision[x][y] = 0;
7520
7521       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7522
7523       if (Feld[x][y] != element)        /* element has changed */
7524         return;
7525     }
7526
7527     if (!MovDelay[x][y])        /* start new movement phase */
7528     {
7529       /* all objects that can change their move direction after each step
7530          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7531
7532       if (element != EL_YAMYAM &&
7533           element != EL_DARK_YAMYAM &&
7534           element != EL_PACMAN &&
7535           !(move_pattern & MV_ANY_DIRECTION) &&
7536           move_pattern != MV_TURNING_LEFT &&
7537           move_pattern != MV_TURNING_RIGHT &&
7538           move_pattern != MV_TURNING_LEFT_RIGHT &&
7539           move_pattern != MV_TURNING_RIGHT_LEFT &&
7540           move_pattern != MV_TURNING_RANDOM)
7541       {
7542         TurnRound(x, y);
7543
7544         if (MovDelay[x][y] && (element == EL_BUG ||
7545                                element == EL_SPACESHIP ||
7546                                element == EL_SP_SNIKSNAK ||
7547                                element == EL_SP_ELECTRON ||
7548                                element == EL_MOLE))
7549           TEST_DrawLevelField(x, y);
7550       }
7551     }
7552
7553     if (MovDelay[x][y])         /* wait some time before next movement */
7554     {
7555       MovDelay[x][y]--;
7556
7557       if (element == EL_ROBOT ||
7558           element == EL_YAMYAM ||
7559           element == EL_DARK_YAMYAM)
7560       {
7561         DrawLevelElementAnimationIfNeeded(x, y, element);
7562         PlayLevelSoundAction(x, y, ACTION_WAITING);
7563       }
7564       else if (element == EL_SP_ELECTRON)
7565         DrawLevelElementAnimationIfNeeded(x, y, element);
7566       else if (element == EL_DRAGON)
7567       {
7568         int i;
7569         int dir = MovDir[x][y];
7570         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7571         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7572         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7573                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7574                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7575                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7576         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7577
7578         GfxAction[x][y] = ACTION_ATTACKING;
7579
7580         if (IS_PLAYER(x, y))
7581           DrawPlayerField(x, y);
7582         else
7583           TEST_DrawLevelField(x, y);
7584
7585         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7586
7587         for (i = 1; i <= 3; i++)
7588         {
7589           int xx = x + i * dx;
7590           int yy = y + i * dy;
7591           int sx = SCREENX(xx);
7592           int sy = SCREENY(yy);
7593           int flame_graphic = graphic + (i - 1);
7594
7595           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7596             break;
7597
7598           if (MovDelay[x][y])
7599           {
7600             int flamed = MovingOrBlocked2Element(xx, yy);
7601
7602             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7603               Bang(xx, yy);
7604             else
7605               RemoveMovingField(xx, yy);
7606
7607             ChangeDelay[xx][yy] = 0;
7608
7609             Feld[xx][yy] = EL_FLAMES;
7610
7611             if (IN_SCR_FIELD(sx, sy))
7612             {
7613               TEST_DrawLevelFieldCrumbled(xx, yy);
7614               DrawGraphic(sx, sy, flame_graphic, frame);
7615             }
7616           }
7617           else
7618           {
7619             if (Feld[xx][yy] == EL_FLAMES)
7620               Feld[xx][yy] = EL_EMPTY;
7621             TEST_DrawLevelField(xx, yy);
7622           }
7623         }
7624       }
7625
7626       if (MovDelay[x][y])       /* element still has to wait some time */
7627       {
7628         PlayLevelSoundAction(x, y, ACTION_WAITING);
7629
7630         return;
7631       }
7632     }
7633
7634     /* now make next step */
7635
7636     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7637
7638     if (DONT_COLLIDE_WITH(element) &&
7639         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7640         !PLAYER_ENEMY_PROTECTED(newx, newy))
7641     {
7642       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7643
7644       return;
7645     }
7646
7647     else if (CAN_MOVE_INTO_ACID(element) &&
7648              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7649              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7650              (MovDir[x][y] == MV_DOWN ||
7651               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7652     {
7653       SplashAcid(newx, newy);
7654       Store[x][y] = EL_ACID;
7655     }
7656     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7657     {
7658       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7659           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7660           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7661           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7662       {
7663         RemoveField(x, y);
7664         TEST_DrawLevelField(x, y);
7665
7666         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7667         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7668           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7669
7670         local_player->friends_still_needed--;
7671         if (!local_player->friends_still_needed &&
7672             !local_player->GameOver && AllPlayersGone)
7673           PlayerWins(local_player);
7674
7675         return;
7676       }
7677       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7678       {
7679         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7680           TEST_DrawLevelField(newx, newy);
7681         else
7682           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7683       }
7684       else if (!IS_FREE(newx, newy))
7685       {
7686         GfxAction[x][y] = ACTION_WAITING;
7687
7688         if (IS_PLAYER(x, y))
7689           DrawPlayerField(x, y);
7690         else
7691           TEST_DrawLevelField(x, y);
7692
7693         return;
7694       }
7695     }
7696     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7697     {
7698       if (IS_FOOD_PIG(Feld[newx][newy]))
7699       {
7700         if (IS_MOVING(newx, newy))
7701           RemoveMovingField(newx, newy);
7702         else
7703         {
7704           Feld[newx][newy] = EL_EMPTY;
7705           TEST_DrawLevelField(newx, newy);
7706         }
7707
7708         PlayLevelSound(x, y, SND_PIG_DIGGING);
7709       }
7710       else if (!IS_FREE(newx, newy))
7711       {
7712         if (IS_PLAYER(x, y))
7713           DrawPlayerField(x, y);
7714         else
7715           TEST_DrawLevelField(x, y);
7716
7717         return;
7718       }
7719     }
7720     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7721     {
7722       if (Store[x][y] != EL_EMPTY)
7723       {
7724         boolean can_clone = FALSE;
7725         int xx, yy;
7726
7727         /* check if element to clone is still there */
7728         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7729         {
7730           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7731           {
7732             can_clone = TRUE;
7733
7734             break;
7735           }
7736         }
7737
7738         /* cannot clone or target field not free anymore -- do not clone */
7739         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7740           Store[x][y] = EL_EMPTY;
7741       }
7742
7743       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7744       {
7745         if (IS_MV_DIAGONAL(MovDir[x][y]))
7746         {
7747           int diagonal_move_dir = MovDir[x][y];
7748           int stored = Store[x][y];
7749           int change_delay = 8;
7750           int graphic;
7751
7752           /* android is moving diagonally */
7753
7754           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7755
7756           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7757           GfxElement[x][y] = EL_EMC_ANDROID;
7758           GfxAction[x][y] = ACTION_SHRINKING;
7759           GfxDir[x][y] = diagonal_move_dir;
7760           ChangeDelay[x][y] = change_delay;
7761
7762           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7763                                    GfxDir[x][y]);
7764
7765           DrawLevelGraphicAnimation(x, y, graphic);
7766           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7767
7768           if (Feld[newx][newy] == EL_ACID)
7769           {
7770             SplashAcid(newx, newy);
7771
7772             return;
7773           }
7774
7775           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7776
7777           Store[newx][newy] = EL_EMC_ANDROID;
7778           GfxElement[newx][newy] = EL_EMC_ANDROID;
7779           GfxAction[newx][newy] = ACTION_GROWING;
7780           GfxDir[newx][newy] = diagonal_move_dir;
7781           ChangeDelay[newx][newy] = change_delay;
7782
7783           graphic = el_act_dir2img(GfxElement[newx][newy],
7784                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7785
7786           DrawLevelGraphicAnimation(newx, newy, graphic);
7787           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7788
7789           return;
7790         }
7791         else
7792         {
7793           Feld[newx][newy] = EL_EMPTY;
7794           TEST_DrawLevelField(newx, newy);
7795
7796           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7797         }
7798       }
7799       else if (!IS_FREE(newx, newy))
7800       {
7801         return;
7802       }
7803     }
7804     else if (IS_CUSTOM_ELEMENT(element) &&
7805              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7806     {
7807       if (!DigFieldByCE(newx, newy, element))
7808         return;
7809
7810       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7811       {
7812         RunnerVisit[x][y] = FrameCounter;
7813         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7814       }
7815     }
7816     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7817     {
7818       if (!IS_FREE(newx, newy))
7819       {
7820         if (IS_PLAYER(x, y))
7821           DrawPlayerField(x, y);
7822         else
7823           TEST_DrawLevelField(x, y);
7824
7825         return;
7826       }
7827       else
7828       {
7829         boolean wanna_flame = !RND(10);
7830         int dx = newx - x, dy = newy - y;
7831         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7832         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7833         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7834                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7835         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7836                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7837
7838         if ((wanna_flame ||
7839              IS_CLASSIC_ENEMY(element1) ||
7840              IS_CLASSIC_ENEMY(element2)) &&
7841             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7842             element1 != EL_FLAMES && element2 != EL_FLAMES)
7843         {
7844           ResetGfxAnimation(x, y);
7845           GfxAction[x][y] = ACTION_ATTACKING;
7846
7847           if (IS_PLAYER(x, y))
7848             DrawPlayerField(x, y);
7849           else
7850             TEST_DrawLevelField(x, y);
7851
7852           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7853
7854           MovDelay[x][y] = 50;
7855
7856           Feld[newx][newy] = EL_FLAMES;
7857           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7858             Feld[newx1][newy1] = EL_FLAMES;
7859           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7860             Feld[newx2][newy2] = EL_FLAMES;
7861
7862           return;
7863         }
7864       }
7865     }
7866     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7867              Feld[newx][newy] == EL_DIAMOND)
7868     {
7869       if (IS_MOVING(newx, newy))
7870         RemoveMovingField(newx, newy);
7871       else
7872       {
7873         Feld[newx][newy] = EL_EMPTY;
7874         TEST_DrawLevelField(newx, newy);
7875       }
7876
7877       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7878     }
7879     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7880              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7881     {
7882       if (AmoebaNr[newx][newy])
7883       {
7884         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7885         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7886             Feld[newx][newy] == EL_BD_AMOEBA)
7887           AmoebaCnt[AmoebaNr[newx][newy]]--;
7888       }
7889
7890       if (IS_MOVING(newx, newy))
7891       {
7892         RemoveMovingField(newx, newy);
7893       }
7894       else
7895       {
7896         Feld[newx][newy] = EL_EMPTY;
7897         TEST_DrawLevelField(newx, newy);
7898       }
7899
7900       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7901     }
7902     else if ((element == EL_PACMAN || element == EL_MOLE)
7903              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7904     {
7905       if (AmoebaNr[newx][newy])
7906       {
7907         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7908         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7909             Feld[newx][newy] == EL_BD_AMOEBA)
7910           AmoebaCnt[AmoebaNr[newx][newy]]--;
7911       }
7912
7913       if (element == EL_MOLE)
7914       {
7915         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7916         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7917
7918         ResetGfxAnimation(x, y);
7919         GfxAction[x][y] = ACTION_DIGGING;
7920         TEST_DrawLevelField(x, y);
7921
7922         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7923
7924         return;                         /* wait for shrinking amoeba */
7925       }
7926       else      /* element == EL_PACMAN */
7927       {
7928         Feld[newx][newy] = EL_EMPTY;
7929         TEST_DrawLevelField(newx, newy);
7930         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7931       }
7932     }
7933     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7934              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7935               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7936     {
7937       /* wait for shrinking amoeba to completely disappear */
7938       return;
7939     }
7940     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7941     {
7942       /* object was running against a wall */
7943
7944       TurnRound(x, y);
7945
7946       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7947         DrawLevelElementAnimation(x, y, element);
7948
7949       if (DONT_TOUCH(element))
7950         TestIfBadThingTouchesPlayer(x, y);
7951
7952       return;
7953     }
7954
7955     InitMovingField(x, y, MovDir[x][y]);
7956
7957     PlayLevelSoundAction(x, y, ACTION_MOVING);
7958   }
7959
7960   if (MovDir[x][y])
7961     ContinueMoving(x, y);
7962 }
7963
7964 void ContinueMoving(int x, int y)
7965 {
7966   int element = Feld[x][y];
7967   struct ElementInfo *ei = &element_info[element];
7968   int direction = MovDir[x][y];
7969   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7970   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7971   int newx = x + dx, newy = y + dy;
7972   int stored = Store[x][y];
7973   int stored_new = Store[newx][newy];
7974   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7975   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7976   boolean last_line = (newy == lev_fieldy - 1);
7977
7978   MovPos[x][y] += getElementMoveStepsize(x, y);
7979
7980   if (pushed_by_player) /* special case: moving object pushed by player */
7981     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7982
7983   if (ABS(MovPos[x][y]) < TILEX)
7984   {
7985     TEST_DrawLevelField(x, y);
7986
7987     return;     /* element is still moving */
7988   }
7989
7990   /* element reached destination field */
7991
7992   Feld[x][y] = EL_EMPTY;
7993   Feld[newx][newy] = element;
7994   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7995
7996   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7997   {
7998     element = Feld[newx][newy] = EL_ACID;
7999   }
8000   else if (element == EL_MOLE)
8001   {
8002     Feld[x][y] = EL_SAND;
8003
8004     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8005   }
8006   else if (element == EL_QUICKSAND_FILLING)
8007   {
8008     element = Feld[newx][newy] = get_next_element(element);
8009     Store[newx][newy] = Store[x][y];
8010   }
8011   else if (element == EL_QUICKSAND_EMPTYING)
8012   {
8013     Feld[x][y] = get_next_element(element);
8014     element = Feld[newx][newy] = Store[x][y];
8015   }
8016   else if (element == EL_QUICKSAND_FAST_FILLING)
8017   {
8018     element = Feld[newx][newy] = get_next_element(element);
8019     Store[newx][newy] = Store[x][y];
8020   }
8021   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8022   {
8023     Feld[x][y] = get_next_element(element);
8024     element = Feld[newx][newy] = Store[x][y];
8025   }
8026   else if (element == EL_MAGIC_WALL_FILLING)
8027   {
8028     element = Feld[newx][newy] = get_next_element(element);
8029     if (!game.magic_wall_active)
8030       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8031     Store[newx][newy] = Store[x][y];
8032   }
8033   else if (element == EL_MAGIC_WALL_EMPTYING)
8034   {
8035     Feld[x][y] = get_next_element(element);
8036     if (!game.magic_wall_active)
8037       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8038     element = Feld[newx][newy] = Store[x][y];
8039
8040     InitField(newx, newy, FALSE);
8041   }
8042   else if (element == EL_BD_MAGIC_WALL_FILLING)
8043   {
8044     element = Feld[newx][newy] = get_next_element(element);
8045     if (!game.magic_wall_active)
8046       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8047     Store[newx][newy] = Store[x][y];
8048   }
8049   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8050   {
8051     Feld[x][y] = get_next_element(element);
8052     if (!game.magic_wall_active)
8053       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8054     element = Feld[newx][newy] = Store[x][y];
8055
8056     InitField(newx, newy, FALSE);
8057   }
8058   else if (element == EL_DC_MAGIC_WALL_FILLING)
8059   {
8060     element = Feld[newx][newy] = get_next_element(element);
8061     if (!game.magic_wall_active)
8062       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8063     Store[newx][newy] = Store[x][y];
8064   }
8065   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8066   {
8067     Feld[x][y] = get_next_element(element);
8068     if (!game.magic_wall_active)
8069       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8070     element = Feld[newx][newy] = Store[x][y];
8071
8072     InitField(newx, newy, FALSE);
8073   }
8074   else if (element == EL_AMOEBA_DROPPING)
8075   {
8076     Feld[x][y] = get_next_element(element);
8077     element = Feld[newx][newy] = Store[x][y];
8078   }
8079   else if (element == EL_SOKOBAN_OBJECT)
8080   {
8081     if (Back[x][y])
8082       Feld[x][y] = Back[x][y];
8083
8084     if (Back[newx][newy])
8085       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8086
8087     Back[x][y] = Back[newx][newy] = 0;
8088   }
8089
8090   Store[x][y] = EL_EMPTY;
8091   MovPos[x][y] = 0;
8092   MovDir[x][y] = 0;
8093   MovDelay[x][y] = 0;
8094
8095   MovDelay[newx][newy] = 0;
8096
8097   if (CAN_CHANGE_OR_HAS_ACTION(element))
8098   {
8099     /* copy element change control values to new field */
8100     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8101     ChangePage[newx][newy]  = ChangePage[x][y];
8102     ChangeCount[newx][newy] = ChangeCount[x][y];
8103     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8104   }
8105
8106   CustomValue[newx][newy] = CustomValue[x][y];
8107
8108   ChangeDelay[x][y] = 0;
8109   ChangePage[x][y] = -1;
8110   ChangeCount[x][y] = 0;
8111   ChangeEvent[x][y] = -1;
8112
8113   CustomValue[x][y] = 0;
8114
8115   /* copy animation control values to new field */
8116   GfxFrame[newx][newy]  = GfxFrame[x][y];
8117   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8118   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8119   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8120
8121   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8122
8123   /* some elements can leave other elements behind after moving */
8124   if (ei->move_leave_element != EL_EMPTY &&
8125       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8126       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8127   {
8128     int move_leave_element = ei->move_leave_element;
8129
8130     /* this makes it possible to leave the removed element again */
8131     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8132       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8133
8134     Feld[x][y] = move_leave_element;
8135
8136     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8137       MovDir[x][y] = direction;
8138
8139     InitField(x, y, FALSE);
8140
8141     if (GFX_CRUMBLED(Feld[x][y]))
8142       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8143
8144     if (ELEM_IS_PLAYER(move_leave_element))
8145       RelocatePlayer(x, y, move_leave_element);
8146   }
8147
8148   /* do this after checking for left-behind element */
8149   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8150
8151   if (!CAN_MOVE(element) ||
8152       (CAN_FALL(element) && direction == MV_DOWN &&
8153        (element == EL_SPRING ||
8154         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8155         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8156     GfxDir[x][y] = MovDir[newx][newy] = 0;
8157
8158   TEST_DrawLevelField(x, y);
8159   TEST_DrawLevelField(newx, newy);
8160
8161   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8162
8163   /* prevent pushed element from moving on in pushed direction */
8164   if (pushed_by_player && CAN_MOVE(element) &&
8165       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8166       !(element_info[element].move_pattern & direction))
8167     TurnRound(newx, newy);
8168
8169   /* prevent elements on conveyor belt from moving on in last direction */
8170   if (pushed_by_conveyor && CAN_FALL(element) &&
8171       direction & MV_HORIZONTAL)
8172     MovDir[newx][newy] = 0;
8173
8174   if (!pushed_by_player)
8175   {
8176     int nextx = newx + dx, nexty = newy + dy;
8177     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8178
8179     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8180
8181     if (CAN_FALL(element) && direction == MV_DOWN)
8182       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8183
8184     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8185       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8186
8187     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8188       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8189   }
8190
8191   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8192   {
8193     TestIfBadThingTouchesPlayer(newx, newy);
8194     TestIfBadThingTouchesFriend(newx, newy);
8195
8196     if (!IS_CUSTOM_ELEMENT(element))
8197       TestIfBadThingTouchesOtherBadThing(newx, newy);
8198   }
8199   else if (element == EL_PENGUIN)
8200     TestIfFriendTouchesBadThing(newx, newy);
8201
8202   if (DONT_GET_HIT_BY(element))
8203   {
8204     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8205   }
8206
8207   /* give the player one last chance (one more frame) to move away */
8208   if (CAN_FALL(element) && direction == MV_DOWN &&
8209       (last_line || (!IS_FREE(x, newy + 1) &&
8210                      (!IS_PLAYER(x, newy + 1) ||
8211                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8212     Impact(x, newy);
8213
8214   if (pushed_by_player && !game.use_change_when_pushing_bug)
8215   {
8216     int push_side = MV_DIR_OPPOSITE(direction);
8217     struct PlayerInfo *player = PLAYERINFO(x, y);
8218
8219     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8220                                player->index_bit, push_side);
8221     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8222                                         player->index_bit, push_side);
8223   }
8224
8225   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8226     MovDelay[newx][newy] = 1;
8227
8228   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8229
8230   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8231   TestIfElementHitsCustomElement(newx, newy, direction);
8232   TestIfPlayerTouchesCustomElement(newx, newy);
8233   TestIfElementTouchesCustomElement(newx, newy);
8234
8235   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8236       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8237     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8238                              MV_DIR_OPPOSITE(direction));
8239 }
8240
8241 int AmoebeNachbarNr(int ax, int ay)
8242 {
8243   int i;
8244   int element = Feld[ax][ay];
8245   int group_nr = 0;
8246   static int xy[4][2] =
8247   {
8248     { 0, -1 },
8249     { -1, 0 },
8250     { +1, 0 },
8251     { 0, +1 }
8252   };
8253
8254   for (i = 0; i < NUM_DIRECTIONS; i++)
8255   {
8256     int x = ax + xy[i][0];
8257     int y = ay + xy[i][1];
8258
8259     if (!IN_LEV_FIELD(x, y))
8260       continue;
8261
8262     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8263       group_nr = AmoebaNr[x][y];
8264   }
8265
8266   return group_nr;
8267 }
8268
8269 void AmoebenVereinigen(int ax, int ay)
8270 {
8271   int i, x, y, xx, yy;
8272   int new_group_nr = AmoebaNr[ax][ay];
8273   static int xy[4][2] =
8274   {
8275     { 0, -1 },
8276     { -1, 0 },
8277     { +1, 0 },
8278     { 0, +1 }
8279   };
8280
8281   if (new_group_nr == 0)
8282     return;
8283
8284   for (i = 0; i < NUM_DIRECTIONS; i++)
8285   {
8286     x = ax + xy[i][0];
8287     y = ay + xy[i][1];
8288
8289     if (!IN_LEV_FIELD(x, y))
8290       continue;
8291
8292     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8293          Feld[x][y] == EL_BD_AMOEBA ||
8294          Feld[x][y] == EL_AMOEBA_DEAD) &&
8295         AmoebaNr[x][y] != new_group_nr)
8296     {
8297       int old_group_nr = AmoebaNr[x][y];
8298
8299       if (old_group_nr == 0)
8300         return;
8301
8302       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8303       AmoebaCnt[old_group_nr] = 0;
8304       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8305       AmoebaCnt2[old_group_nr] = 0;
8306
8307       SCAN_PLAYFIELD(xx, yy)
8308       {
8309         if (AmoebaNr[xx][yy] == old_group_nr)
8310           AmoebaNr[xx][yy] = new_group_nr;
8311       }
8312     }
8313   }
8314 }
8315
8316 void AmoebeUmwandeln(int ax, int ay)
8317 {
8318   int i, x, y;
8319
8320   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8321   {
8322     int group_nr = AmoebaNr[ax][ay];
8323
8324 #ifdef DEBUG
8325     if (group_nr == 0)
8326     {
8327       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8328       printf("AmoebeUmwandeln(): This should never happen!\n");
8329       return;
8330     }
8331 #endif
8332
8333     SCAN_PLAYFIELD(x, y)
8334     {
8335       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8336       {
8337         AmoebaNr[x][y] = 0;
8338         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8339       }
8340     }
8341
8342     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8343                             SND_AMOEBA_TURNING_TO_GEM :
8344                             SND_AMOEBA_TURNING_TO_ROCK));
8345     Bang(ax, ay);
8346   }
8347   else
8348   {
8349     static int xy[4][2] =
8350     {
8351       { 0, -1 },
8352       { -1, 0 },
8353       { +1, 0 },
8354       { 0, +1 }
8355     };
8356
8357     for (i = 0; i < NUM_DIRECTIONS; i++)
8358     {
8359       x = ax + xy[i][0];
8360       y = ay + xy[i][1];
8361
8362       if (!IN_LEV_FIELD(x, y))
8363         continue;
8364
8365       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8366       {
8367         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8368                               SND_AMOEBA_TURNING_TO_GEM :
8369                               SND_AMOEBA_TURNING_TO_ROCK));
8370         Bang(x, y);
8371       }
8372     }
8373   }
8374 }
8375
8376 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8377 {
8378   int x, y;
8379   int group_nr = AmoebaNr[ax][ay];
8380   boolean done = FALSE;
8381
8382 #ifdef DEBUG
8383   if (group_nr == 0)
8384   {
8385     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8386     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8387     return;
8388   }
8389 #endif
8390
8391   SCAN_PLAYFIELD(x, y)
8392   {
8393     if (AmoebaNr[x][y] == group_nr &&
8394         (Feld[x][y] == EL_AMOEBA_DEAD ||
8395          Feld[x][y] == EL_BD_AMOEBA ||
8396          Feld[x][y] == EL_AMOEBA_GROWING))
8397     {
8398       AmoebaNr[x][y] = 0;
8399       Feld[x][y] = new_element;
8400       InitField(x, y, FALSE);
8401       TEST_DrawLevelField(x, y);
8402       done = TRUE;
8403     }
8404   }
8405
8406   if (done)
8407     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8408                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8409                             SND_BD_AMOEBA_TURNING_TO_GEM));
8410 }
8411
8412 void AmoebeWaechst(int x, int y)
8413 {
8414   static unsigned int sound_delay = 0;
8415   static unsigned int sound_delay_value = 0;
8416
8417   if (!MovDelay[x][y])          /* start new growing cycle */
8418   {
8419     MovDelay[x][y] = 7;
8420
8421     if (DelayReached(&sound_delay, sound_delay_value))
8422     {
8423       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8424       sound_delay_value = 30;
8425     }
8426   }
8427
8428   if (MovDelay[x][y])           /* wait some time before growing bigger */
8429   {
8430     MovDelay[x][y]--;
8431     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8432     {
8433       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8434                                            6 - MovDelay[x][y]);
8435
8436       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8437     }
8438
8439     if (!MovDelay[x][y])
8440     {
8441       Feld[x][y] = Store[x][y];
8442       Store[x][y] = 0;
8443       TEST_DrawLevelField(x, y);
8444     }
8445   }
8446 }
8447
8448 void AmoebaDisappearing(int x, int y)
8449 {
8450   static unsigned int sound_delay = 0;
8451   static unsigned int sound_delay_value = 0;
8452
8453   if (!MovDelay[x][y])          /* start new shrinking cycle */
8454   {
8455     MovDelay[x][y] = 7;
8456
8457     if (DelayReached(&sound_delay, sound_delay_value))
8458       sound_delay_value = 30;
8459   }
8460
8461   if (MovDelay[x][y])           /* wait some time before shrinking */
8462   {
8463     MovDelay[x][y]--;
8464     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8465     {
8466       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8467                                            6 - MovDelay[x][y]);
8468
8469       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8470     }
8471
8472     if (!MovDelay[x][y])
8473     {
8474       Feld[x][y] = EL_EMPTY;
8475       TEST_DrawLevelField(x, y);
8476
8477       /* don't let mole enter this field in this cycle;
8478          (give priority to objects falling to this field from above) */
8479       Stop[x][y] = TRUE;
8480     }
8481   }
8482 }
8483
8484 void AmoebeAbleger(int ax, int ay)
8485 {
8486   int i;
8487   int element = Feld[ax][ay];
8488   int graphic = el2img(element);
8489   int newax = ax, neway = ay;
8490   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8491   static int xy[4][2] =
8492   {
8493     { 0, -1 },
8494     { -1, 0 },
8495     { +1, 0 },
8496     { 0, +1 }
8497   };
8498
8499   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8500   {
8501     Feld[ax][ay] = EL_AMOEBA_DEAD;
8502     TEST_DrawLevelField(ax, ay);
8503     return;
8504   }
8505
8506   if (IS_ANIMATED(graphic))
8507     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8508
8509   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8510     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8511
8512   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8513   {
8514     MovDelay[ax][ay]--;
8515     if (MovDelay[ax][ay])
8516       return;
8517   }
8518
8519   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8520   {
8521     int start = RND(4);
8522     int x = ax + xy[start][0];
8523     int y = ay + xy[start][1];
8524
8525     if (!IN_LEV_FIELD(x, y))
8526       return;
8527
8528     if (IS_FREE(x, y) ||
8529         CAN_GROW_INTO(Feld[x][y]) ||
8530         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8531         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8532     {
8533       newax = x;
8534       neway = y;
8535     }
8536
8537     if (newax == ax && neway == ay)
8538       return;
8539   }
8540   else                          /* normal or "filled" (BD style) amoeba */
8541   {
8542     int start = RND(4);
8543     boolean waiting_for_player = FALSE;
8544
8545     for (i = 0; i < NUM_DIRECTIONS; i++)
8546     {
8547       int j = (start + i) % 4;
8548       int x = ax + xy[j][0];
8549       int y = ay + xy[j][1];
8550
8551       if (!IN_LEV_FIELD(x, y))
8552         continue;
8553
8554       if (IS_FREE(x, y) ||
8555           CAN_GROW_INTO(Feld[x][y]) ||
8556           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8557           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8558       {
8559         newax = x;
8560         neway = y;
8561         break;
8562       }
8563       else if (IS_PLAYER(x, y))
8564         waiting_for_player = TRUE;
8565     }
8566
8567     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8568     {
8569       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8570       {
8571         Feld[ax][ay] = EL_AMOEBA_DEAD;
8572         TEST_DrawLevelField(ax, ay);
8573         AmoebaCnt[AmoebaNr[ax][ay]]--;
8574
8575         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8576         {
8577           if (element == EL_AMOEBA_FULL)
8578             AmoebeUmwandeln(ax, ay);
8579           else if (element == EL_BD_AMOEBA)
8580             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8581         }
8582       }
8583       return;
8584     }
8585     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8586     {
8587       /* amoeba gets larger by growing in some direction */
8588
8589       int new_group_nr = AmoebaNr[ax][ay];
8590
8591 #ifdef DEBUG
8592   if (new_group_nr == 0)
8593   {
8594     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8595     printf("AmoebeAbleger(): This should never happen!\n");
8596     return;
8597   }
8598 #endif
8599
8600       AmoebaNr[newax][neway] = new_group_nr;
8601       AmoebaCnt[new_group_nr]++;
8602       AmoebaCnt2[new_group_nr]++;
8603
8604       /* if amoeba touches other amoeba(s) after growing, unify them */
8605       AmoebenVereinigen(newax, neway);
8606
8607       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8608       {
8609         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8610         return;
8611       }
8612     }
8613   }
8614
8615   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8616       (neway == lev_fieldy - 1 && newax != ax))
8617   {
8618     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8619     Store[newax][neway] = element;
8620   }
8621   else if (neway == ay || element == EL_EMC_DRIPPER)
8622   {
8623     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8624
8625     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8626   }
8627   else
8628   {
8629     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8630     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8631     Store[ax][ay] = EL_AMOEBA_DROP;
8632     ContinueMoving(ax, ay);
8633     return;
8634   }
8635
8636   TEST_DrawLevelField(newax, neway);
8637 }
8638
8639 void Life(int ax, int ay)
8640 {
8641   int x1, y1, x2, y2;
8642   int life_time = 40;
8643   int element = Feld[ax][ay];
8644   int graphic = el2img(element);
8645   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8646                          level.biomaze);
8647   boolean changed = FALSE;
8648
8649   if (IS_ANIMATED(graphic))
8650     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8651
8652   if (Stop[ax][ay])
8653     return;
8654
8655   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8656     MovDelay[ax][ay] = life_time;
8657
8658   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8659   {
8660     MovDelay[ax][ay]--;
8661     if (MovDelay[ax][ay])
8662       return;
8663   }
8664
8665   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8666   {
8667     int xx = ax+x1, yy = ay+y1;
8668     int nachbarn = 0;
8669
8670     if (!IN_LEV_FIELD(xx, yy))
8671       continue;
8672
8673     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8674     {
8675       int x = xx+x2, y = yy+y2;
8676
8677       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8678         continue;
8679
8680       if (((Feld[x][y] == element ||
8681             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8682            !Stop[x][y]) ||
8683           (IS_FREE(x, y) && Stop[x][y]))
8684         nachbarn++;
8685     }
8686
8687     if (xx == ax && yy == ay)           /* field in the middle */
8688     {
8689       if (nachbarn < life_parameter[0] ||
8690           nachbarn > life_parameter[1])
8691       {
8692         Feld[xx][yy] = EL_EMPTY;
8693         if (!Stop[xx][yy])
8694           TEST_DrawLevelField(xx, yy);
8695         Stop[xx][yy] = TRUE;
8696         changed = TRUE;
8697       }
8698     }
8699     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8700     {                                   /* free border field */
8701       if (nachbarn >= life_parameter[2] &&
8702           nachbarn <= life_parameter[3])
8703       {
8704         Feld[xx][yy] = element;
8705         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8706         if (!Stop[xx][yy])
8707           TEST_DrawLevelField(xx, yy);
8708         Stop[xx][yy] = TRUE;
8709         changed = TRUE;
8710       }
8711     }
8712   }
8713
8714   if (changed)
8715     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8716                    SND_GAME_OF_LIFE_GROWING);
8717 }
8718
8719 static void InitRobotWheel(int x, int y)
8720 {
8721   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8722 }
8723
8724 static void RunRobotWheel(int x, int y)
8725 {
8726   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8727 }
8728
8729 static void StopRobotWheel(int x, int y)
8730 {
8731   if (ZX == x && ZY == y)
8732   {
8733     ZX = ZY = -1;
8734
8735     game.robot_wheel_active = FALSE;
8736   }
8737 }
8738
8739 static void InitTimegateWheel(int x, int y)
8740 {
8741   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8742 }
8743
8744 static void RunTimegateWheel(int x, int y)
8745 {
8746   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8747 }
8748
8749 static void InitMagicBallDelay(int x, int y)
8750 {
8751   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8752 }
8753
8754 static void ActivateMagicBall(int bx, int by)
8755 {
8756   int x, y;
8757
8758   if (level.ball_random)
8759   {
8760     int pos_border = RND(8);    /* select one of the eight border elements */
8761     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8762     int xx = pos_content % 3;
8763     int yy = pos_content / 3;
8764
8765     x = bx - 1 + xx;
8766     y = by - 1 + yy;
8767
8768     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8769       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8770   }
8771   else
8772   {
8773     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8774     {
8775       int xx = x - bx + 1;
8776       int yy = y - by + 1;
8777
8778       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8779         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8780     }
8781   }
8782
8783   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8784 }
8785
8786 void CheckExit(int x, int y)
8787 {
8788   if (local_player->gems_still_needed > 0 ||
8789       local_player->sokobanfields_still_needed > 0 ||
8790       local_player->lights_still_needed > 0)
8791   {
8792     int element = Feld[x][y];
8793     int graphic = el2img(element);
8794
8795     if (IS_ANIMATED(graphic))
8796       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8797
8798     return;
8799   }
8800
8801   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8802     return;
8803
8804   Feld[x][y] = EL_EXIT_OPENING;
8805
8806   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8807 }
8808
8809 void CheckExitEM(int x, int y)
8810 {
8811   if (local_player->gems_still_needed > 0 ||
8812       local_player->sokobanfields_still_needed > 0 ||
8813       local_player->lights_still_needed > 0)
8814   {
8815     int element = Feld[x][y];
8816     int graphic = el2img(element);
8817
8818     if (IS_ANIMATED(graphic))
8819       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8820
8821     return;
8822   }
8823
8824   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8825     return;
8826
8827   Feld[x][y] = EL_EM_EXIT_OPENING;
8828
8829   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8830 }
8831
8832 void CheckExitSteel(int x, int y)
8833 {
8834   if (local_player->gems_still_needed > 0 ||
8835       local_player->sokobanfields_still_needed > 0 ||
8836       local_player->lights_still_needed > 0)
8837   {
8838     int element = Feld[x][y];
8839     int graphic = el2img(element);
8840
8841     if (IS_ANIMATED(graphic))
8842       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8843
8844     return;
8845   }
8846
8847   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8848     return;
8849
8850   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8851
8852   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8853 }
8854
8855 void CheckExitSteelEM(int x, int y)
8856 {
8857   if (local_player->gems_still_needed > 0 ||
8858       local_player->sokobanfields_still_needed > 0 ||
8859       local_player->lights_still_needed > 0)
8860   {
8861     int element = Feld[x][y];
8862     int graphic = el2img(element);
8863
8864     if (IS_ANIMATED(graphic))
8865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8866
8867     return;
8868   }
8869
8870   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8871     return;
8872
8873   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8874
8875   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8876 }
8877
8878 void CheckExitSP(int x, int y)
8879 {
8880   if (local_player->gems_still_needed > 0)
8881   {
8882     int element = Feld[x][y];
8883     int graphic = el2img(element);
8884
8885     if (IS_ANIMATED(graphic))
8886       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8887
8888     return;
8889   }
8890
8891   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8892     return;
8893
8894   Feld[x][y] = EL_SP_EXIT_OPENING;
8895
8896   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8897 }
8898
8899 static void CloseAllOpenTimegates()
8900 {
8901   int x, y;
8902
8903   SCAN_PLAYFIELD(x, y)
8904   {
8905     int element = Feld[x][y];
8906
8907     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8908     {
8909       Feld[x][y] = EL_TIMEGATE_CLOSING;
8910
8911       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8912     }
8913   }
8914 }
8915
8916 void DrawTwinkleOnField(int x, int y)
8917 {
8918   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8919     return;
8920
8921   if (Feld[x][y] == EL_BD_DIAMOND)
8922     return;
8923
8924   if (MovDelay[x][y] == 0)      /* next animation frame */
8925     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8926
8927   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8928   {
8929     MovDelay[x][y]--;
8930
8931     DrawLevelElementAnimation(x, y, Feld[x][y]);
8932
8933     if (MovDelay[x][y] != 0)
8934     {
8935       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8936                                            10 - MovDelay[x][y]);
8937
8938       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8939     }
8940   }
8941 }
8942
8943 void MauerWaechst(int x, int y)
8944 {
8945   int delay = 6;
8946
8947   if (!MovDelay[x][y])          /* next animation frame */
8948     MovDelay[x][y] = 3 * delay;
8949
8950   if (MovDelay[x][y])           /* wait some time before next frame */
8951   {
8952     MovDelay[x][y]--;
8953
8954     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8955     {
8956       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8957       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8958
8959       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8960     }
8961
8962     if (!MovDelay[x][y])
8963     {
8964       if (MovDir[x][y] == MV_LEFT)
8965       {
8966         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8967           TEST_DrawLevelField(x - 1, y);
8968       }
8969       else if (MovDir[x][y] == MV_RIGHT)
8970       {
8971         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8972           TEST_DrawLevelField(x + 1, y);
8973       }
8974       else if (MovDir[x][y] == MV_UP)
8975       {
8976         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8977           TEST_DrawLevelField(x, y - 1);
8978       }
8979       else
8980       {
8981         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8982           TEST_DrawLevelField(x, y + 1);
8983       }
8984
8985       Feld[x][y] = Store[x][y];
8986       Store[x][y] = 0;
8987       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8988       TEST_DrawLevelField(x, y);
8989     }
8990   }
8991 }
8992
8993 void MauerAbleger(int ax, int ay)
8994 {
8995   int element = Feld[ax][ay];
8996   int graphic = el2img(element);
8997   boolean oben_frei = FALSE, unten_frei = FALSE;
8998   boolean links_frei = FALSE, rechts_frei = FALSE;
8999   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9000   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9001   boolean new_wall = FALSE;
9002
9003   if (IS_ANIMATED(graphic))
9004     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9005
9006   if (!MovDelay[ax][ay])        /* start building new wall */
9007     MovDelay[ax][ay] = 6;
9008
9009   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9010   {
9011     MovDelay[ax][ay]--;
9012     if (MovDelay[ax][ay])
9013       return;
9014   }
9015
9016   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9017     oben_frei = TRUE;
9018   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9019     unten_frei = TRUE;
9020   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9021     links_frei = TRUE;
9022   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9023     rechts_frei = TRUE;
9024
9025   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9026       element == EL_EXPANDABLE_WALL_ANY)
9027   {
9028     if (oben_frei)
9029     {
9030       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9031       Store[ax][ay-1] = element;
9032       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9033       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9034         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9035                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9036       new_wall = TRUE;
9037     }
9038     if (unten_frei)
9039     {
9040       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9041       Store[ax][ay+1] = element;
9042       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9043       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9044         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9045                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9046       new_wall = TRUE;
9047     }
9048   }
9049
9050   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9051       element == EL_EXPANDABLE_WALL_ANY ||
9052       element == EL_EXPANDABLE_WALL ||
9053       element == EL_BD_EXPANDABLE_WALL)
9054   {
9055     if (links_frei)
9056     {
9057       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9058       Store[ax-1][ay] = element;
9059       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9060       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9061         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9062                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9063       new_wall = TRUE;
9064     }
9065
9066     if (rechts_frei)
9067     {
9068       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9069       Store[ax+1][ay] = element;
9070       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9071       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9072         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9073                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9074       new_wall = TRUE;
9075     }
9076   }
9077
9078   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9079     TEST_DrawLevelField(ax, ay);
9080
9081   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9082     oben_massiv = TRUE;
9083   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9084     unten_massiv = TRUE;
9085   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9086     links_massiv = TRUE;
9087   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9088     rechts_massiv = TRUE;
9089
9090   if (((oben_massiv && unten_massiv) ||
9091        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9092        element == EL_EXPANDABLE_WALL) &&
9093       ((links_massiv && rechts_massiv) ||
9094        element == EL_EXPANDABLE_WALL_VERTICAL))
9095     Feld[ax][ay] = EL_WALL;
9096
9097   if (new_wall)
9098     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9099 }
9100
9101 void MauerAblegerStahl(int ax, int ay)
9102 {
9103   int element = Feld[ax][ay];
9104   int graphic = el2img(element);
9105   boolean oben_frei = FALSE, unten_frei = FALSE;
9106   boolean links_frei = FALSE, rechts_frei = FALSE;
9107   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9108   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9109   boolean new_wall = FALSE;
9110
9111   if (IS_ANIMATED(graphic))
9112     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9113
9114   if (!MovDelay[ax][ay])        /* start building new wall */
9115     MovDelay[ax][ay] = 6;
9116
9117   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9118   {
9119     MovDelay[ax][ay]--;
9120     if (MovDelay[ax][ay])
9121       return;
9122   }
9123
9124   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9125     oben_frei = TRUE;
9126   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9127     unten_frei = TRUE;
9128   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9129     links_frei = TRUE;
9130   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9131     rechts_frei = TRUE;
9132
9133   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9134       element == EL_EXPANDABLE_STEELWALL_ANY)
9135   {
9136     if (oben_frei)
9137     {
9138       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9139       Store[ax][ay-1] = element;
9140       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9141       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9142         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9143                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9144       new_wall = TRUE;
9145     }
9146     if (unten_frei)
9147     {
9148       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9149       Store[ax][ay+1] = element;
9150       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9151       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9152         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9153                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9154       new_wall = TRUE;
9155     }
9156   }
9157
9158   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9159       element == EL_EXPANDABLE_STEELWALL_ANY)
9160   {
9161     if (links_frei)
9162     {
9163       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9164       Store[ax-1][ay] = element;
9165       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9166       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9167         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9168                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9169       new_wall = TRUE;
9170     }
9171
9172     if (rechts_frei)
9173     {
9174       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9175       Store[ax+1][ay] = element;
9176       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9177       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9178         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9179                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9180       new_wall = TRUE;
9181     }
9182   }
9183
9184   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9185     oben_massiv = TRUE;
9186   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9187     unten_massiv = TRUE;
9188   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9189     links_massiv = TRUE;
9190   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9191     rechts_massiv = TRUE;
9192
9193   if (((oben_massiv && unten_massiv) ||
9194        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9195       ((links_massiv && rechts_massiv) ||
9196        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9197     Feld[ax][ay] = EL_STEELWALL;
9198
9199   if (new_wall)
9200     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9201 }
9202
9203 void CheckForDragon(int x, int y)
9204 {
9205   int i, j;
9206   boolean dragon_found = FALSE;
9207   static int xy[4][2] =
9208   {
9209     { 0, -1 },
9210     { -1, 0 },
9211     { +1, 0 },
9212     { 0, +1 }
9213   };
9214
9215   for (i = 0; i < NUM_DIRECTIONS; i++)
9216   {
9217     for (j = 0; j < 4; j++)
9218     {
9219       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9220
9221       if (IN_LEV_FIELD(xx, yy) &&
9222           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9223       {
9224         if (Feld[xx][yy] == EL_DRAGON)
9225           dragon_found = TRUE;
9226       }
9227       else
9228         break;
9229     }
9230   }
9231
9232   if (!dragon_found)
9233   {
9234     for (i = 0; i < NUM_DIRECTIONS; i++)
9235     {
9236       for (j = 0; j < 3; j++)
9237       {
9238         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9239   
9240         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9241         {
9242           Feld[xx][yy] = EL_EMPTY;
9243           TEST_DrawLevelField(xx, yy);
9244         }
9245         else
9246           break;
9247       }
9248     }
9249   }
9250 }
9251
9252 static void InitBuggyBase(int x, int y)
9253 {
9254   int element = Feld[x][y];
9255   int activating_delay = FRAMES_PER_SECOND / 4;
9256
9257   ChangeDelay[x][y] =
9258     (element == EL_SP_BUGGY_BASE ?
9259      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9260      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9261      activating_delay :
9262      element == EL_SP_BUGGY_BASE_ACTIVE ?
9263      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9264 }
9265
9266 static void WarnBuggyBase(int x, int y)
9267 {
9268   int i;
9269   static int xy[4][2] =
9270   {
9271     { 0, -1 },
9272     { -1, 0 },
9273     { +1, 0 },
9274     { 0, +1 }
9275   };
9276
9277   for (i = 0; i < NUM_DIRECTIONS; i++)
9278   {
9279     int xx = x + xy[i][0];
9280     int yy = y + xy[i][1];
9281
9282     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9283     {
9284       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9285
9286       break;
9287     }
9288   }
9289 }
9290
9291 static void InitTrap(int x, int y)
9292 {
9293   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9294 }
9295
9296 static void ActivateTrap(int x, int y)
9297 {
9298   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9299 }
9300
9301 static void ChangeActiveTrap(int x, int y)
9302 {
9303   int graphic = IMG_TRAP_ACTIVE;
9304
9305   /* if new animation frame was drawn, correct crumbled sand border */
9306   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9307     TEST_DrawLevelFieldCrumbled(x, y);
9308 }
9309
9310 static int getSpecialActionElement(int element, int number, int base_element)
9311 {
9312   return (element != EL_EMPTY ? element :
9313           number != -1 ? base_element + number - 1 :
9314           EL_EMPTY);
9315 }
9316
9317 static int getModifiedActionNumber(int value_old, int operator, int operand,
9318                                    int value_min, int value_max)
9319 {
9320   int value_new = (operator == CA_MODE_SET      ? operand :
9321                    operator == CA_MODE_ADD      ? value_old + operand :
9322                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9323                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9324                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9325                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9326                    value_old);
9327
9328   return (value_new < value_min ? value_min :
9329           value_new > value_max ? value_max :
9330           value_new);
9331 }
9332
9333 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9334 {
9335   struct ElementInfo *ei = &element_info[element];
9336   struct ElementChangeInfo *change = &ei->change_page[page];
9337   int target_element = change->target_element;
9338   int action_type = change->action_type;
9339   int action_mode = change->action_mode;
9340   int action_arg = change->action_arg;
9341   int action_element = change->action_element;
9342   int i;
9343
9344   if (!change->has_action)
9345     return;
9346
9347   /* ---------- determine action paramater values -------------------------- */
9348
9349   int level_time_value =
9350     (level.time > 0 ? TimeLeft :
9351      TimePlayed);
9352
9353   int action_arg_element_raw =
9354     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9355      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9356      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9357      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9358      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9359      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9360      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9361      EL_EMPTY);
9362   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9363
9364   int action_arg_direction =
9365     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9366      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9367      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9368      change->actual_trigger_side :
9369      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9370      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9371      MV_NONE);
9372
9373   int action_arg_number_min =
9374     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9375      CA_ARG_MIN);
9376
9377   int action_arg_number_max =
9378     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9379      action_type == CA_SET_LEVEL_GEMS ? 999 :
9380      action_type == CA_SET_LEVEL_TIME ? 9999 :
9381      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9382      action_type == CA_SET_CE_VALUE ? 9999 :
9383      action_type == CA_SET_CE_SCORE ? 9999 :
9384      CA_ARG_MAX);
9385
9386   int action_arg_number_reset =
9387     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9388      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9389      action_type == CA_SET_LEVEL_TIME ? level.time :
9390      action_type == CA_SET_LEVEL_SCORE ? 0 :
9391      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9392      action_type == CA_SET_CE_SCORE ? 0 :
9393      0);
9394
9395   int action_arg_number =
9396     (action_arg <= CA_ARG_MAX ? action_arg :
9397      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9398      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9399      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9400      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9401      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9402      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9403      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9404      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9405      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9406      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9407      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9408      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9409      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9410      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9411      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9412      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9413      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9414      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9415      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9416      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9417      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9418      -1);
9419
9420   int action_arg_number_old =
9421     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9422      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9423      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9424      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9425      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9426      0);
9427
9428   int action_arg_number_new =
9429     getModifiedActionNumber(action_arg_number_old,
9430                             action_mode, action_arg_number,
9431                             action_arg_number_min, action_arg_number_max);
9432
9433   int trigger_player_bits =
9434     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9435      change->actual_trigger_player_bits : change->trigger_player);
9436
9437   int action_arg_player_bits =
9438     (action_arg >= CA_ARG_PLAYER_1 &&
9439      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9440      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9441      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9442      PLAYER_BITS_ANY);
9443
9444   /* ---------- execute action  -------------------------------------------- */
9445
9446   switch (action_type)
9447   {
9448     case CA_NO_ACTION:
9449     {
9450       return;
9451     }
9452
9453     /* ---------- level actions  ------------------------------------------- */
9454
9455     case CA_RESTART_LEVEL:
9456     {
9457       game.restart_level = TRUE;
9458
9459       break;
9460     }
9461
9462     case CA_SHOW_ENVELOPE:
9463     {
9464       int element = getSpecialActionElement(action_arg_element,
9465                                             action_arg_number, EL_ENVELOPE_1);
9466
9467       if (IS_ENVELOPE(element))
9468         local_player->show_envelope = element;
9469
9470       break;
9471     }
9472
9473     case CA_SET_LEVEL_TIME:
9474     {
9475       if (level.time > 0)       /* only modify limited time value */
9476       {
9477         TimeLeft = action_arg_number_new;
9478
9479         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9480
9481         DisplayGameControlValues();
9482
9483         if (!TimeLeft && setup.time_limit)
9484           for (i = 0; i < MAX_PLAYERS; i++)
9485             KillPlayer(&stored_player[i]);
9486       }
9487
9488       break;
9489     }
9490
9491     case CA_SET_LEVEL_SCORE:
9492     {
9493       local_player->score = action_arg_number_new;
9494
9495       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9496
9497       DisplayGameControlValues();
9498
9499       break;
9500     }
9501
9502     case CA_SET_LEVEL_GEMS:
9503     {
9504       local_player->gems_still_needed = action_arg_number_new;
9505
9506       game.snapshot.collected_item = TRUE;
9507
9508       game_panel_controls[GAME_PANEL_GEMS].value =
9509         local_player->gems_still_needed;
9510
9511       DisplayGameControlValues();
9512
9513       break;
9514     }
9515
9516     case CA_SET_LEVEL_WIND:
9517     {
9518       game.wind_direction = action_arg_direction;
9519
9520       break;
9521     }
9522
9523     case CA_SET_LEVEL_RANDOM_SEED:
9524     {
9525       /* ensure that setting a new random seed while playing is predictable */
9526       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9527
9528       break;
9529     }
9530
9531     /* ---------- player actions  ------------------------------------------ */
9532
9533     case CA_MOVE_PLAYER:
9534     {
9535       /* automatically move to the next field in specified direction */
9536       for (i = 0; i < MAX_PLAYERS; i++)
9537         if (trigger_player_bits & (1 << i))
9538           stored_player[i].programmed_action = action_arg_direction;
9539
9540       break;
9541     }
9542
9543     case CA_EXIT_PLAYER:
9544     {
9545       for (i = 0; i < MAX_PLAYERS; i++)
9546         if (action_arg_player_bits & (1 << i))
9547           PlayerWins(&stored_player[i]);
9548
9549       break;
9550     }
9551
9552     case CA_KILL_PLAYER:
9553     {
9554       for (i = 0; i < MAX_PLAYERS; i++)
9555         if (action_arg_player_bits & (1 << i))
9556           KillPlayer(&stored_player[i]);
9557
9558       break;
9559     }
9560
9561     case CA_SET_PLAYER_KEYS:
9562     {
9563       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9564       int element = getSpecialActionElement(action_arg_element,
9565                                             action_arg_number, EL_KEY_1);
9566
9567       if (IS_KEY(element))
9568       {
9569         for (i = 0; i < MAX_PLAYERS; i++)
9570         {
9571           if (trigger_player_bits & (1 << i))
9572           {
9573             stored_player[i].key[KEY_NR(element)] = key_state;
9574
9575             DrawGameDoorValues();
9576           }
9577         }
9578       }
9579
9580       break;
9581     }
9582
9583     case CA_SET_PLAYER_SPEED:
9584     {
9585       for (i = 0; i < MAX_PLAYERS; i++)
9586       {
9587         if (trigger_player_bits & (1 << i))
9588         {
9589           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9590
9591           if (action_arg == CA_ARG_SPEED_FASTER &&
9592               stored_player[i].cannot_move)
9593           {
9594             action_arg_number = STEPSIZE_VERY_SLOW;
9595           }
9596           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9597                    action_arg == CA_ARG_SPEED_FASTER)
9598           {
9599             action_arg_number = 2;
9600             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9601                            CA_MODE_MULTIPLY);
9602           }
9603           else if (action_arg == CA_ARG_NUMBER_RESET)
9604           {
9605             action_arg_number = level.initial_player_stepsize[i];
9606           }
9607
9608           move_stepsize =
9609             getModifiedActionNumber(move_stepsize,
9610                                     action_mode,
9611                                     action_arg_number,
9612                                     action_arg_number_min,
9613                                     action_arg_number_max);
9614
9615           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9616         }
9617       }
9618
9619       break;
9620     }
9621
9622     case CA_SET_PLAYER_SHIELD:
9623     {
9624       for (i = 0; i < MAX_PLAYERS; i++)
9625       {
9626         if (trigger_player_bits & (1 << i))
9627         {
9628           if (action_arg == CA_ARG_SHIELD_OFF)
9629           {
9630             stored_player[i].shield_normal_time_left = 0;
9631             stored_player[i].shield_deadly_time_left = 0;
9632           }
9633           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9634           {
9635             stored_player[i].shield_normal_time_left = 999999;
9636           }
9637           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9638           {
9639             stored_player[i].shield_normal_time_left = 999999;
9640             stored_player[i].shield_deadly_time_left = 999999;
9641           }
9642         }
9643       }
9644
9645       break;
9646     }
9647
9648     case CA_SET_PLAYER_GRAVITY:
9649     {
9650       for (i = 0; i < MAX_PLAYERS; i++)
9651       {
9652         if (trigger_player_bits & (1 << i))
9653         {
9654           stored_player[i].gravity =
9655             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9656              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9657              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9658              stored_player[i].gravity);
9659         }
9660       }
9661
9662       break;
9663     }
9664
9665     case CA_SET_PLAYER_ARTWORK:
9666     {
9667       for (i = 0; i < MAX_PLAYERS; i++)
9668       {
9669         if (trigger_player_bits & (1 << i))
9670         {
9671           int artwork_element = action_arg_element;
9672
9673           if (action_arg == CA_ARG_ELEMENT_RESET)
9674             artwork_element =
9675               (level.use_artwork_element[i] ? level.artwork_element[i] :
9676                stored_player[i].element_nr);
9677
9678           if (stored_player[i].artwork_element != artwork_element)
9679             stored_player[i].Frame = 0;
9680
9681           stored_player[i].artwork_element = artwork_element;
9682
9683           SetPlayerWaiting(&stored_player[i], FALSE);
9684
9685           /* set number of special actions for bored and sleeping animation */
9686           stored_player[i].num_special_action_bored =
9687             get_num_special_action(artwork_element,
9688                                    ACTION_BORING_1, ACTION_BORING_LAST);
9689           stored_player[i].num_special_action_sleeping =
9690             get_num_special_action(artwork_element,
9691                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9692         }
9693       }
9694
9695       break;
9696     }
9697
9698     case CA_SET_PLAYER_INVENTORY:
9699     {
9700       for (i = 0; i < MAX_PLAYERS; i++)
9701       {
9702         struct PlayerInfo *player = &stored_player[i];
9703         int j, k;
9704
9705         if (trigger_player_bits & (1 << i))
9706         {
9707           int inventory_element = action_arg_element;
9708
9709           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9710               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9711               action_arg == CA_ARG_ELEMENT_ACTION)
9712           {
9713             int element = inventory_element;
9714             int collect_count = element_info[element].collect_count_initial;
9715
9716             if (!IS_CUSTOM_ELEMENT(element))
9717               collect_count = 1;
9718
9719             if (collect_count == 0)
9720               player->inventory_infinite_element = element;
9721             else
9722               for (k = 0; k < collect_count; k++)
9723                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9724                   player->inventory_element[player->inventory_size++] =
9725                     element;
9726           }
9727           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9728                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9729                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9730           {
9731             if (player->inventory_infinite_element != EL_UNDEFINED &&
9732                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9733                                      action_arg_element_raw))
9734               player->inventory_infinite_element = EL_UNDEFINED;
9735
9736             for (k = 0, j = 0; j < player->inventory_size; j++)
9737             {
9738               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9739                                         action_arg_element_raw))
9740                 player->inventory_element[k++] = player->inventory_element[j];
9741             }
9742
9743             player->inventory_size = k;
9744           }
9745           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9746           {
9747             if (player->inventory_size > 0)
9748             {
9749               for (j = 0; j < player->inventory_size - 1; j++)
9750                 player->inventory_element[j] = player->inventory_element[j + 1];
9751
9752               player->inventory_size--;
9753             }
9754           }
9755           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9756           {
9757             if (player->inventory_size > 0)
9758               player->inventory_size--;
9759           }
9760           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9761           {
9762             player->inventory_infinite_element = EL_UNDEFINED;
9763             player->inventory_size = 0;
9764           }
9765           else if (action_arg == CA_ARG_INVENTORY_RESET)
9766           {
9767             player->inventory_infinite_element = EL_UNDEFINED;
9768             player->inventory_size = 0;
9769
9770             if (level.use_initial_inventory[i])
9771             {
9772               for (j = 0; j < level.initial_inventory_size[i]; j++)
9773               {
9774                 int element = level.initial_inventory_content[i][j];
9775                 int collect_count = element_info[element].collect_count_initial;
9776
9777                 if (!IS_CUSTOM_ELEMENT(element))
9778                   collect_count = 1;
9779
9780                 if (collect_count == 0)
9781                   player->inventory_infinite_element = element;
9782                 else
9783                   for (k = 0; k < collect_count; k++)
9784                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9785                       player->inventory_element[player->inventory_size++] =
9786                         element;
9787               }
9788             }
9789           }
9790         }
9791       }
9792
9793       break;
9794     }
9795
9796     /* ---------- CE actions  ---------------------------------------------- */
9797
9798     case CA_SET_CE_VALUE:
9799     {
9800       int last_ce_value = CustomValue[x][y];
9801
9802       CustomValue[x][y] = action_arg_number_new;
9803
9804       if (CustomValue[x][y] != last_ce_value)
9805       {
9806         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9807         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9808
9809         if (CustomValue[x][y] == 0)
9810         {
9811           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9812           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9813         }
9814       }
9815
9816       break;
9817     }
9818
9819     case CA_SET_CE_SCORE:
9820     {
9821       int last_ce_score = ei->collect_score;
9822
9823       ei->collect_score = action_arg_number_new;
9824
9825       if (ei->collect_score != last_ce_score)
9826       {
9827         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9828         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9829
9830         if (ei->collect_score == 0)
9831         {
9832           int xx, yy;
9833
9834           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9835           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9836
9837           /*
9838             This is a very special case that seems to be a mixture between
9839             CheckElementChange() and CheckTriggeredElementChange(): while
9840             the first one only affects single elements that are triggered
9841             directly, the second one affects multiple elements in the playfield
9842             that are triggered indirectly by another element. This is a third
9843             case: Changing the CE score always affects multiple identical CEs,
9844             so every affected CE must be checked, not only the single CE for
9845             which the CE score was changed in the first place (as every instance
9846             of that CE shares the same CE score, and therefore also can change)!
9847           */
9848           SCAN_PLAYFIELD(xx, yy)
9849           {
9850             if (Feld[xx][yy] == element)
9851               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9852                                  CE_SCORE_GETS_ZERO);
9853           }
9854         }
9855       }
9856
9857       break;
9858     }
9859
9860     case CA_SET_CE_ARTWORK:
9861     {
9862       int artwork_element = action_arg_element;
9863       boolean reset_frame = FALSE;
9864       int xx, yy;
9865
9866       if (action_arg == CA_ARG_ELEMENT_RESET)
9867         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9868                            element);
9869
9870       if (ei->gfx_element != artwork_element)
9871         reset_frame = TRUE;
9872
9873       ei->gfx_element = artwork_element;
9874
9875       SCAN_PLAYFIELD(xx, yy)
9876       {
9877         if (Feld[xx][yy] == element)
9878         {
9879           if (reset_frame)
9880           {
9881             ResetGfxAnimation(xx, yy);
9882             ResetRandomAnimationValue(xx, yy);
9883           }
9884
9885           TEST_DrawLevelField(xx, yy);
9886         }
9887       }
9888
9889       break;
9890     }
9891
9892     /* ---------- engine actions  ------------------------------------------ */
9893
9894     case CA_SET_ENGINE_SCAN_MODE:
9895     {
9896       InitPlayfieldScanMode(action_arg);
9897
9898       break;
9899     }
9900
9901     default:
9902       break;
9903   }
9904 }
9905
9906 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9907 {
9908   int old_element = Feld[x][y];
9909   int new_element = GetElementFromGroupElement(element);
9910   int previous_move_direction = MovDir[x][y];
9911   int last_ce_value = CustomValue[x][y];
9912   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9913   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9914   boolean add_player_onto_element = (new_element_is_player &&
9915                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9916                                      IS_WALKABLE(old_element));
9917
9918   if (!add_player_onto_element)
9919   {
9920     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9921       RemoveMovingField(x, y);
9922     else
9923       RemoveField(x, y);
9924
9925     Feld[x][y] = new_element;
9926
9927     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9928       MovDir[x][y] = previous_move_direction;
9929
9930     if (element_info[new_element].use_last_ce_value)
9931       CustomValue[x][y] = last_ce_value;
9932
9933     InitField_WithBug1(x, y, FALSE);
9934
9935     new_element = Feld[x][y];   /* element may have changed */
9936
9937     ResetGfxAnimation(x, y);
9938     ResetRandomAnimationValue(x, y);
9939
9940     TEST_DrawLevelField(x, y);
9941
9942     if (GFX_CRUMBLED(new_element))
9943       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9944   }
9945
9946   /* check if element under the player changes from accessible to unaccessible
9947      (needed for special case of dropping element which then changes) */
9948   /* (must be checked after creating new element for walkable group elements) */
9949   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9950       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9951   {
9952     Bang(x, y);
9953
9954     return;
9955   }
9956
9957   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9958   if (new_element_is_player)
9959     RelocatePlayer(x, y, new_element);
9960
9961   if (is_change)
9962     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9963
9964   TestIfBadThingTouchesPlayer(x, y);
9965   TestIfPlayerTouchesCustomElement(x, y);
9966   TestIfElementTouchesCustomElement(x, y);
9967 }
9968
9969 static void CreateField(int x, int y, int element)
9970 {
9971   CreateFieldExt(x, y, element, FALSE);
9972 }
9973
9974 static void CreateElementFromChange(int x, int y, int element)
9975 {
9976   element = GET_VALID_RUNTIME_ELEMENT(element);
9977
9978   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9979   {
9980     int old_element = Feld[x][y];
9981
9982     /* prevent changed element from moving in same engine frame
9983        unless both old and new element can either fall or move */
9984     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9985         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9986       Stop[x][y] = TRUE;
9987   }
9988
9989   CreateFieldExt(x, y, element, TRUE);
9990 }
9991
9992 static boolean ChangeElement(int x, int y, int element, int page)
9993 {
9994   struct ElementInfo *ei = &element_info[element];
9995   struct ElementChangeInfo *change = &ei->change_page[page];
9996   int ce_value = CustomValue[x][y];
9997   int ce_score = ei->collect_score;
9998   int target_element;
9999   int old_element = Feld[x][y];
10000
10001   /* always use default change event to prevent running into a loop */
10002   if (ChangeEvent[x][y] == -1)
10003     ChangeEvent[x][y] = CE_DELAY;
10004
10005   if (ChangeEvent[x][y] == CE_DELAY)
10006   {
10007     /* reset actual trigger element, trigger player and action element */
10008     change->actual_trigger_element = EL_EMPTY;
10009     change->actual_trigger_player = EL_EMPTY;
10010     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10011     change->actual_trigger_side = CH_SIDE_NONE;
10012     change->actual_trigger_ce_value = 0;
10013     change->actual_trigger_ce_score = 0;
10014   }
10015
10016   /* do not change elements more than a specified maximum number of changes */
10017   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10018     return FALSE;
10019
10020   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10021
10022   if (change->explode)
10023   {
10024     Bang(x, y);
10025
10026     return TRUE;
10027   }
10028
10029   if (change->use_target_content)
10030   {
10031     boolean complete_replace = TRUE;
10032     boolean can_replace[3][3];
10033     int xx, yy;
10034
10035     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10036     {
10037       boolean is_empty;
10038       boolean is_walkable;
10039       boolean is_diggable;
10040       boolean is_collectible;
10041       boolean is_removable;
10042       boolean is_destructible;
10043       int ex = x + xx - 1;
10044       int ey = y + yy - 1;
10045       int content_element = change->target_content.e[xx][yy];
10046       int e;
10047
10048       can_replace[xx][yy] = TRUE;
10049
10050       if (ex == x && ey == y)   /* do not check changing element itself */
10051         continue;
10052
10053       if (content_element == EL_EMPTY_SPACE)
10054       {
10055         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10056
10057         continue;
10058       }
10059
10060       if (!IN_LEV_FIELD(ex, ey))
10061       {
10062         can_replace[xx][yy] = FALSE;
10063         complete_replace = FALSE;
10064
10065         continue;
10066       }
10067
10068       e = Feld[ex][ey];
10069
10070       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10071         e = MovingOrBlocked2Element(ex, ey);
10072
10073       is_empty = (IS_FREE(ex, ey) ||
10074                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10075
10076       is_walkable     = (is_empty || IS_WALKABLE(e));
10077       is_diggable     = (is_empty || IS_DIGGABLE(e));
10078       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10079       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10080       is_removable    = (is_diggable || is_collectible);
10081
10082       can_replace[xx][yy] =
10083         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10084           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10085           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10086           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10087           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10088           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10089          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10090
10091       if (!can_replace[xx][yy])
10092         complete_replace = FALSE;
10093     }
10094
10095     if (!change->only_if_complete || complete_replace)
10096     {
10097       boolean something_has_changed = FALSE;
10098
10099       if (change->only_if_complete && change->use_random_replace &&
10100           RND(100) < change->random_percentage)
10101         return FALSE;
10102
10103       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10104       {
10105         int ex = x + xx - 1;
10106         int ey = y + yy - 1;
10107         int content_element;
10108
10109         if (can_replace[xx][yy] && (!change->use_random_replace ||
10110                                     RND(100) < change->random_percentage))
10111         {
10112           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10113             RemoveMovingField(ex, ey);
10114
10115           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10116
10117           content_element = change->target_content.e[xx][yy];
10118           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10119                                               ce_value, ce_score);
10120
10121           CreateElementFromChange(ex, ey, target_element);
10122
10123           something_has_changed = TRUE;
10124
10125           /* for symmetry reasons, freeze newly created border elements */
10126           if (ex != x || ey != y)
10127             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10128         }
10129       }
10130
10131       if (something_has_changed)
10132       {
10133         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10134         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10135       }
10136     }
10137   }
10138   else
10139   {
10140     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10141                                         ce_value, ce_score);
10142
10143     if (element == EL_DIAGONAL_GROWING ||
10144         element == EL_DIAGONAL_SHRINKING)
10145     {
10146       target_element = Store[x][y];
10147
10148       Store[x][y] = EL_EMPTY;
10149     }
10150
10151     CreateElementFromChange(x, y, target_element);
10152
10153     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10154     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10155   }
10156
10157   /* this uses direct change before indirect change */
10158   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10159
10160   return TRUE;
10161 }
10162
10163 static void HandleElementChange(int x, int y, int page)
10164 {
10165   int element = MovingOrBlocked2Element(x, y);
10166   struct ElementInfo *ei = &element_info[element];
10167   struct ElementChangeInfo *change = &ei->change_page[page];
10168   boolean handle_action_before_change = FALSE;
10169
10170 #ifdef DEBUG
10171   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10172       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10173   {
10174     printf("\n\n");
10175     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10176            x, y, element, element_info[element].token_name);
10177     printf("HandleElementChange(): This should never happen!\n");
10178     printf("\n\n");
10179   }
10180 #endif
10181
10182   /* this can happen with classic bombs on walkable, changing elements */
10183   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10184   {
10185     return;
10186   }
10187
10188   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10189   {
10190     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10191
10192     if (change->can_change)
10193     {
10194       /* !!! not clear why graphic animation should be reset at all here !!! */
10195       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10196       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10197
10198       /*
10199         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10200
10201         When using an animation frame delay of 1 (this only happens with
10202         "sp_zonk.moving.left/right" in the classic graphics), the default
10203         (non-moving) animation shows wrong animation frames (while the
10204         moving animation, like "sp_zonk.moving.left/right", is correct,
10205         so this graphical bug never shows up with the classic graphics).
10206         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10207         be drawn instead of the correct frames 0,1,2,3. This is caused by
10208         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10209         an element change: First when the change delay ("ChangeDelay[][]")
10210         counter has reached zero after decrementing, then a second time in
10211         the next frame (after "GfxFrame[][]" was already incremented) when
10212         "ChangeDelay[][]" is reset to the initial delay value again.
10213
10214         This causes frame 0 to be drawn twice, while the last frame won't
10215         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10216
10217         As some animations may already be cleverly designed around this bug
10218         (at least the "Snake Bite" snake tail animation does this), it cannot
10219         simply be fixed here without breaking such existing animations.
10220         Unfortunately, it cannot easily be detected if a graphics set was
10221         designed "before" or "after" the bug was fixed. As a workaround,
10222         a new graphics set option "game.graphics_engine_version" was added
10223         to be able to specify the game's major release version for which the
10224         graphics set was designed, which can then be used to decide if the
10225         bugfix should be used (version 4 and above) or not (version 3 or
10226         below, or if no version was specified at all, as with old sets).
10227
10228         (The wrong/fixed animation frames can be tested with the test level set
10229         "test_gfxframe" and level "000", which contains a specially prepared
10230         custom element at level position (x/y) == (11/9) which uses the zonk
10231         animation mentioned above. Using "game.graphics_engine_version: 4"
10232         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10233         This can also be seen from the debug output for this test element.)
10234       */
10235
10236       /* when a custom element is about to change (for example by change delay),
10237          do not reset graphic animation when the custom element is moving */
10238       if (game.graphics_engine_version < 4 &&
10239           !IS_MOVING(x, y))
10240       {
10241         ResetGfxAnimation(x, y);
10242         ResetRandomAnimationValue(x, y);
10243       }
10244
10245       if (change->pre_change_function)
10246         change->pre_change_function(x, y);
10247     }
10248   }
10249
10250   ChangeDelay[x][y]--;
10251
10252   if (ChangeDelay[x][y] != 0)           /* continue element change */
10253   {
10254     if (change->can_change)
10255     {
10256       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10257
10258       if (IS_ANIMATED(graphic))
10259         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10260
10261       if (change->change_function)
10262         change->change_function(x, y);
10263     }
10264   }
10265   else                                  /* finish element change */
10266   {
10267     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10268     {
10269       page = ChangePage[x][y];
10270       ChangePage[x][y] = -1;
10271
10272       change = &ei->change_page[page];
10273     }
10274
10275     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10276     {
10277       ChangeDelay[x][y] = 1;            /* try change after next move step */
10278       ChangePage[x][y] = page;          /* remember page to use for change */
10279
10280       return;
10281     }
10282
10283     /* special case: set new level random seed before changing element */
10284     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10285       handle_action_before_change = TRUE;
10286
10287     if (change->has_action && handle_action_before_change)
10288       ExecuteCustomElementAction(x, y, element, page);
10289
10290     if (change->can_change)
10291     {
10292       if (ChangeElement(x, y, element, page))
10293       {
10294         if (change->post_change_function)
10295           change->post_change_function(x, y);
10296       }
10297     }
10298
10299     if (change->has_action && !handle_action_before_change)
10300       ExecuteCustomElementAction(x, y, element, page);
10301   }
10302 }
10303
10304 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10305                                               int trigger_element,
10306                                               int trigger_event,
10307                                               int trigger_player,
10308                                               int trigger_side,
10309                                               int trigger_page)
10310 {
10311   boolean change_done_any = FALSE;
10312   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10313   int i;
10314
10315   if (!(trigger_events[trigger_element][trigger_event]))
10316     return FALSE;
10317
10318   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10319
10320   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10321   {
10322     int element = EL_CUSTOM_START + i;
10323     boolean change_done = FALSE;
10324     int p;
10325
10326     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10327         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10328       continue;
10329
10330     for (p = 0; p < element_info[element].num_change_pages; p++)
10331     {
10332       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10333
10334       if (change->can_change_or_has_action &&
10335           change->has_event[trigger_event] &&
10336           change->trigger_side & trigger_side &&
10337           change->trigger_player & trigger_player &&
10338           change->trigger_page & trigger_page_bits &&
10339           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10340       {
10341         change->actual_trigger_element = trigger_element;
10342         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10343         change->actual_trigger_player_bits = trigger_player;
10344         change->actual_trigger_side = trigger_side;
10345         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10346         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10347
10348         if ((change->can_change && !change_done) || change->has_action)
10349         {
10350           int x, y;
10351
10352           SCAN_PLAYFIELD(x, y)
10353           {
10354             if (Feld[x][y] == element)
10355             {
10356               if (change->can_change && !change_done)
10357               {
10358                 /* if element already changed in this frame, not only prevent
10359                    another element change (checked in ChangeElement()), but
10360                    also prevent additional element actions for this element */
10361
10362                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10363                     !level.use_action_after_change_bug)
10364                   continue;
10365
10366                 ChangeDelay[x][y] = 1;
10367                 ChangeEvent[x][y] = trigger_event;
10368
10369                 HandleElementChange(x, y, p);
10370               }
10371               else if (change->has_action)
10372               {
10373                 /* if element already changed in this frame, not only prevent
10374                    another element change (checked in ChangeElement()), but
10375                    also prevent additional element actions for this element */
10376
10377                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10378                     !level.use_action_after_change_bug)
10379                   continue;
10380
10381                 ExecuteCustomElementAction(x, y, element, p);
10382                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10383               }
10384             }
10385           }
10386
10387           if (change->can_change)
10388           {
10389             change_done = TRUE;
10390             change_done_any = TRUE;
10391           }
10392         }
10393       }
10394     }
10395   }
10396
10397   RECURSION_LOOP_DETECTION_END();
10398
10399   return change_done_any;
10400 }
10401
10402 static boolean CheckElementChangeExt(int x, int y,
10403                                      int element,
10404                                      int trigger_element,
10405                                      int trigger_event,
10406                                      int trigger_player,
10407                                      int trigger_side)
10408 {
10409   boolean change_done = FALSE;
10410   int p;
10411
10412   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10413       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10414     return FALSE;
10415
10416   if (Feld[x][y] == EL_BLOCKED)
10417   {
10418     Blocked2Moving(x, y, &x, &y);
10419     element = Feld[x][y];
10420   }
10421
10422   /* check if element has already changed or is about to change after moving */
10423   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10424        Feld[x][y] != element) ||
10425
10426       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10427        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10428         ChangePage[x][y] != -1)))
10429     return FALSE;
10430
10431   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10432
10433   for (p = 0; p < element_info[element].num_change_pages; p++)
10434   {
10435     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10436
10437     /* check trigger element for all events where the element that is checked
10438        for changing interacts with a directly adjacent element -- this is
10439        different to element changes that affect other elements to change on the
10440        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10441     boolean check_trigger_element =
10442       (trigger_event == CE_TOUCHING_X ||
10443        trigger_event == CE_HITTING_X ||
10444        trigger_event == CE_HIT_BY_X ||
10445        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10446
10447     if (change->can_change_or_has_action &&
10448         change->has_event[trigger_event] &&
10449         change->trigger_side & trigger_side &&
10450         change->trigger_player & trigger_player &&
10451         (!check_trigger_element ||
10452          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10453     {
10454       change->actual_trigger_element = trigger_element;
10455       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10456       change->actual_trigger_player_bits = trigger_player;
10457       change->actual_trigger_side = trigger_side;
10458       change->actual_trigger_ce_value = CustomValue[x][y];
10459       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10460
10461       /* special case: trigger element not at (x,y) position for some events */
10462       if (check_trigger_element)
10463       {
10464         static struct
10465         {
10466           int dx, dy;
10467         } move_xy[] =
10468           {
10469             {  0,  0 },
10470             { -1,  0 },
10471             { +1,  0 },
10472             {  0,  0 },
10473             {  0, -1 },
10474             {  0,  0 }, { 0, 0 }, { 0, 0 },
10475             {  0, +1 }
10476           };
10477
10478         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10479         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10480
10481         change->actual_trigger_ce_value = CustomValue[xx][yy];
10482         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10483       }
10484
10485       if (change->can_change && !change_done)
10486       {
10487         ChangeDelay[x][y] = 1;
10488         ChangeEvent[x][y] = trigger_event;
10489
10490         HandleElementChange(x, y, p);
10491
10492         change_done = TRUE;
10493       }
10494       else if (change->has_action)
10495       {
10496         ExecuteCustomElementAction(x, y, element, p);
10497         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10498       }
10499     }
10500   }
10501
10502   RECURSION_LOOP_DETECTION_END();
10503
10504   return change_done;
10505 }
10506
10507 static void PlayPlayerSound(struct PlayerInfo *player)
10508 {
10509   int jx = player->jx, jy = player->jy;
10510   int sound_element = player->artwork_element;
10511   int last_action = player->last_action_waiting;
10512   int action = player->action_waiting;
10513
10514   if (player->is_waiting)
10515   {
10516     if (action != last_action)
10517       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10518     else
10519       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10520   }
10521   else
10522   {
10523     if (action != last_action)
10524       StopSound(element_info[sound_element].sound[last_action]);
10525
10526     if (last_action == ACTION_SLEEPING)
10527       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10528   }
10529 }
10530
10531 static void PlayAllPlayersSound()
10532 {
10533   int i;
10534
10535   for (i = 0; i < MAX_PLAYERS; i++)
10536     if (stored_player[i].active)
10537       PlayPlayerSound(&stored_player[i]);
10538 }
10539
10540 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10541 {
10542   boolean last_waiting = player->is_waiting;
10543   int move_dir = player->MovDir;
10544
10545   player->dir_waiting = move_dir;
10546   player->last_action_waiting = player->action_waiting;
10547
10548   if (is_waiting)
10549   {
10550     if (!last_waiting)          /* not waiting -> waiting */
10551     {
10552       player->is_waiting = TRUE;
10553
10554       player->frame_counter_bored =
10555         FrameCounter +
10556         game.player_boring_delay_fixed +
10557         GetSimpleRandom(game.player_boring_delay_random);
10558       player->frame_counter_sleeping =
10559         FrameCounter +
10560         game.player_sleeping_delay_fixed +
10561         GetSimpleRandom(game.player_sleeping_delay_random);
10562
10563       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10564     }
10565
10566     if (game.player_sleeping_delay_fixed +
10567         game.player_sleeping_delay_random > 0 &&
10568         player->anim_delay_counter == 0 &&
10569         player->post_delay_counter == 0 &&
10570         FrameCounter >= player->frame_counter_sleeping)
10571       player->is_sleeping = TRUE;
10572     else if (game.player_boring_delay_fixed +
10573              game.player_boring_delay_random > 0 &&
10574              FrameCounter >= player->frame_counter_bored)
10575       player->is_bored = TRUE;
10576
10577     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10578                               player->is_bored ? ACTION_BORING :
10579                               ACTION_WAITING);
10580
10581     if (player->is_sleeping && player->use_murphy)
10582     {
10583       /* special case for sleeping Murphy when leaning against non-free tile */
10584
10585       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10586           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10587            !IS_MOVING(player->jx - 1, player->jy)))
10588         move_dir = MV_LEFT;
10589       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10590                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10591                 !IS_MOVING(player->jx + 1, player->jy)))
10592         move_dir = MV_RIGHT;
10593       else
10594         player->is_sleeping = FALSE;
10595
10596       player->dir_waiting = move_dir;
10597     }
10598
10599     if (player->is_sleeping)
10600     {
10601       if (player->num_special_action_sleeping > 0)
10602       {
10603         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10604         {
10605           int last_special_action = player->special_action_sleeping;
10606           int num_special_action = player->num_special_action_sleeping;
10607           int special_action =
10608             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10609              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10610              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10611              last_special_action + 1 : ACTION_SLEEPING);
10612           int special_graphic =
10613             el_act_dir2img(player->artwork_element, special_action, move_dir);
10614
10615           player->anim_delay_counter =
10616             graphic_info[special_graphic].anim_delay_fixed +
10617             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10618           player->post_delay_counter =
10619             graphic_info[special_graphic].post_delay_fixed +
10620             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10621
10622           player->special_action_sleeping = special_action;
10623         }
10624
10625         if (player->anim_delay_counter > 0)
10626         {
10627           player->action_waiting = player->special_action_sleeping;
10628           player->anim_delay_counter--;
10629         }
10630         else if (player->post_delay_counter > 0)
10631         {
10632           player->post_delay_counter--;
10633         }
10634       }
10635     }
10636     else if (player->is_bored)
10637     {
10638       if (player->num_special_action_bored > 0)
10639       {
10640         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10641         {
10642           int special_action =
10643             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10644           int special_graphic =
10645             el_act_dir2img(player->artwork_element, special_action, move_dir);
10646
10647           player->anim_delay_counter =
10648             graphic_info[special_graphic].anim_delay_fixed +
10649             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10650           player->post_delay_counter =
10651             graphic_info[special_graphic].post_delay_fixed +
10652             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10653
10654           player->special_action_bored = special_action;
10655         }
10656
10657         if (player->anim_delay_counter > 0)
10658         {
10659           player->action_waiting = player->special_action_bored;
10660           player->anim_delay_counter--;
10661         }
10662         else if (player->post_delay_counter > 0)
10663         {
10664           player->post_delay_counter--;
10665         }
10666       }
10667     }
10668   }
10669   else if (last_waiting)        /* waiting -> not waiting */
10670   {
10671     player->is_waiting = FALSE;
10672     player->is_bored = FALSE;
10673     player->is_sleeping = FALSE;
10674
10675     player->frame_counter_bored = -1;
10676     player->frame_counter_sleeping = -1;
10677
10678     player->anim_delay_counter = 0;
10679     player->post_delay_counter = 0;
10680
10681     player->dir_waiting = player->MovDir;
10682     player->action_waiting = ACTION_DEFAULT;
10683
10684     player->special_action_bored = ACTION_DEFAULT;
10685     player->special_action_sleeping = ACTION_DEFAULT;
10686   }
10687 }
10688
10689 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10690 {
10691   static boolean player_was_moving = FALSE;
10692   static boolean player_was_snapping = FALSE;
10693   static boolean player_was_dropping = FALSE;
10694
10695   if ((!player->is_moving  && player_was_moving) ||
10696       (player->MovPos == 0 && player_was_moving) ||
10697       (player->is_snapping && !player_was_snapping) ||
10698       (player->is_dropping && !player_was_dropping))
10699   {
10700     if (!SaveEngineSnapshotToList())
10701       return;
10702
10703     player_was_moving = FALSE;
10704     player_was_snapping = TRUE;
10705     player_was_dropping = TRUE;
10706   }
10707   else
10708   {
10709     if (player->is_moving)
10710       player_was_moving = TRUE;
10711
10712     if (!player->is_snapping)
10713       player_was_snapping = FALSE;
10714
10715     if (!player->is_dropping)
10716       player_was_dropping = FALSE;
10717   }
10718 }
10719
10720 static void CheckSingleStepMode(struct PlayerInfo *player)
10721 {
10722   if (tape.single_step && tape.recording && !tape.pausing)
10723   {
10724     /* as it is called "single step mode", just return to pause mode when the
10725        player stopped moving after one tile (or never starts moving at all) */
10726     if (!player->is_moving && !player->is_pushing)
10727     {
10728       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10729       SnapField(player, 0, 0);                  /* stop snapping */
10730     }
10731   }
10732
10733   CheckSaveEngineSnapshot(player);
10734 }
10735
10736 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10737 {
10738   int left      = player_action & JOY_LEFT;
10739   int right     = player_action & JOY_RIGHT;
10740   int up        = player_action & JOY_UP;
10741   int down      = player_action & JOY_DOWN;
10742   int button1   = player_action & JOY_BUTTON_1;
10743   int button2   = player_action & JOY_BUTTON_2;
10744   int dx        = (left ? -1 : right ? 1 : 0);
10745   int dy        = (up   ? -1 : down  ? 1 : 0);
10746
10747   if (!player->active || tape.pausing)
10748     return 0;
10749
10750   if (player_action)
10751   {
10752     if (button1)
10753       SnapField(player, dx, dy);
10754     else
10755     {
10756       if (button2)
10757         DropElement(player);
10758
10759       MovePlayer(player, dx, dy);
10760     }
10761
10762     CheckSingleStepMode(player);
10763
10764     SetPlayerWaiting(player, FALSE);
10765
10766     return player_action;
10767   }
10768   else
10769   {
10770     /* no actions for this player (no input at player's configured device) */
10771
10772     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10773     SnapField(player, 0, 0);
10774     CheckGravityMovementWhenNotMoving(player);
10775
10776     if (player->MovPos == 0)
10777       SetPlayerWaiting(player, TRUE);
10778
10779     if (player->MovPos == 0)    /* needed for tape.playing */
10780       player->is_moving = FALSE;
10781
10782     player->is_dropping = FALSE;
10783     player->is_dropping_pressed = FALSE;
10784     player->drop_pressed_delay = 0;
10785
10786     CheckSingleStepMode(player);
10787
10788     return 0;
10789   }
10790 }
10791
10792 static void CheckLevelTime()
10793 {
10794   int i;
10795
10796   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10797   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10798   {
10799     if (level.native_em_level->lev->home == 0)  /* all players at home */
10800     {
10801       PlayerWins(local_player);
10802
10803       AllPlayersGone = TRUE;
10804
10805       level.native_em_level->lev->home = -1;
10806     }
10807
10808     if (level.native_em_level->ply[0]->alive == 0 &&
10809         level.native_em_level->ply[1]->alive == 0 &&
10810         level.native_em_level->ply[2]->alive == 0 &&
10811         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10812       AllPlayersGone = TRUE;
10813   }
10814   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10815   {
10816     if (game_sp.LevelSolved &&
10817         !game_sp.GameOver)                              /* game won */
10818     {
10819       PlayerWins(local_player);
10820
10821       game_sp.GameOver = TRUE;
10822
10823       AllPlayersGone = TRUE;
10824     }
10825
10826     if (game_sp.GameOver)                               /* game lost */
10827       AllPlayersGone = TRUE;
10828   }
10829
10830   if (TimeFrames >= FRAMES_PER_SECOND)
10831   {
10832     TimeFrames = 0;
10833     TapeTime++;
10834
10835     for (i = 0; i < MAX_PLAYERS; i++)
10836     {
10837       struct PlayerInfo *player = &stored_player[i];
10838
10839       if (SHIELD_ON(player))
10840       {
10841         player->shield_normal_time_left--;
10842
10843         if (player->shield_deadly_time_left > 0)
10844           player->shield_deadly_time_left--;
10845       }
10846     }
10847
10848     if (!local_player->LevelSolved && !level.use_step_counter)
10849     {
10850       TimePlayed++;
10851
10852       if (TimeLeft > 0)
10853       {
10854         TimeLeft--;
10855
10856         if (TimeLeft <= 10 && setup.time_limit)
10857           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10858
10859         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10860            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10861
10862         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10863
10864         if (!TimeLeft && setup.time_limit)
10865         {
10866           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10867             level.native_em_level->lev->killed_out_of_time = TRUE;
10868           else
10869             for (i = 0; i < MAX_PLAYERS; i++)
10870               KillPlayer(&stored_player[i]);
10871         }
10872       }
10873       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10874       {
10875         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10876       }
10877
10878       level.native_em_level->lev->time =
10879         (game.no_time_limit ? TimePlayed : TimeLeft);
10880     }
10881
10882     if (tape.recording || tape.playing)
10883       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10884   }
10885
10886   if (tape.recording || tape.playing)
10887     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10888
10889   UpdateAndDisplayGameControlValues();
10890 }
10891
10892 void AdvanceFrameAndPlayerCounters(int player_nr)
10893 {
10894   int i;
10895
10896   /* advance frame counters (global frame counter and time frame counter) */
10897   FrameCounter++;
10898   TimeFrames++;
10899
10900   /* advance player counters (counters for move delay, move animation etc.) */
10901   for (i = 0; i < MAX_PLAYERS; i++)
10902   {
10903     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10904     int move_delay_value = stored_player[i].move_delay_value;
10905     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10906
10907     if (!advance_player_counters)       /* not all players may be affected */
10908       continue;
10909
10910     if (move_frames == 0)       /* less than one move per game frame */
10911     {
10912       int stepsize = TILEX / move_delay_value;
10913       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10914       int count = (stored_player[i].is_moving ?
10915                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10916
10917       if (count % delay == 0)
10918         move_frames = 1;
10919     }
10920
10921     stored_player[i].Frame += move_frames;
10922
10923     if (stored_player[i].MovPos != 0)
10924       stored_player[i].StepFrame += move_frames;
10925
10926     if (stored_player[i].move_delay > 0)
10927       stored_player[i].move_delay--;
10928
10929     /* due to bugs in previous versions, counter must count up, not down */
10930     if (stored_player[i].push_delay != -1)
10931       stored_player[i].push_delay++;
10932
10933     if (stored_player[i].drop_delay > 0)
10934       stored_player[i].drop_delay--;
10935
10936     if (stored_player[i].is_dropping_pressed)
10937       stored_player[i].drop_pressed_delay++;
10938   }
10939 }
10940
10941 void StartGameActions(boolean init_network_game, boolean record_tape,
10942                       int random_seed)
10943 {
10944   unsigned int new_random_seed = InitRND(random_seed);
10945
10946   if (record_tape)
10947     TapeStartRecording(new_random_seed);
10948
10949 #if defined(NETWORK_AVALIABLE)
10950   if (init_network_game)
10951   {
10952     SendToServer_StartPlaying();
10953
10954     return;
10955   }
10956 #endif
10957
10958   InitGame();
10959 }
10960
10961 void GameActions()
10962 {
10963 #if 0
10964   static unsigned int game_frame_delay = 0;
10965 #endif
10966   unsigned int game_frame_delay_value;
10967   byte *recorded_player_action;
10968   byte summarized_player_action = 0;
10969   byte tape_action[MAX_PLAYERS];
10970   int i;
10971
10972   /* detect endless loops, caused by custom element programming */
10973   if (recursion_loop_detected && recursion_loop_depth == 0)
10974   {
10975     char *message = getStringCat3("Internal Error! Element ",
10976                                   EL_NAME(recursion_loop_element),
10977                                   " caused endless loop! Quit the game?");
10978
10979     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10980           EL_NAME(recursion_loop_element));
10981
10982     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10983
10984     recursion_loop_detected = FALSE;    /* if game should be continued */
10985
10986     free(message);
10987
10988     return;
10989   }
10990
10991   if (game.restart_level)
10992     StartGameActions(options.network, setup.autorecord, level.random_seed);
10993
10994   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10995   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10996   {
10997     if (level.native_em_level->lev->home == 0)  /* all players at home */
10998     {
10999       PlayerWins(local_player);
11000
11001       AllPlayersGone = TRUE;
11002
11003       level.native_em_level->lev->home = -1;
11004     }
11005
11006     if (level.native_em_level->ply[0]->alive == 0 &&
11007         level.native_em_level->ply[1]->alive == 0 &&
11008         level.native_em_level->ply[2]->alive == 0 &&
11009         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11010       AllPlayersGone = TRUE;
11011   }
11012   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11013   {
11014     if (game_sp.LevelSolved &&
11015         !game_sp.GameOver)                              /* game won */
11016     {
11017       PlayerWins(local_player);
11018
11019       game_sp.GameOver = TRUE;
11020
11021       AllPlayersGone = TRUE;
11022     }
11023
11024     if (game_sp.GameOver)                               /* game lost */
11025       AllPlayersGone = TRUE;
11026   }
11027
11028   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11029     GameWon();
11030
11031   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11032     TapeStop();
11033
11034   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11035     return;
11036
11037   game_frame_delay_value =
11038     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11039
11040   if (tape.playing && tape.warp_forward && !tape.pausing)
11041     game_frame_delay_value = 0;
11042
11043   SetVideoFrameDelay(game_frame_delay_value);
11044
11045 #if 0
11046 #if 0
11047   /* ---------- main game synchronization point ---------- */
11048
11049   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11050
11051   printf("::: skip == %d\n", skip);
11052
11053 #else
11054   /* ---------- main game synchronization point ---------- */
11055
11056   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11057 #endif
11058 #endif
11059
11060   if (network_playing && !network_player_action_received)
11061   {
11062     /* try to get network player actions in time */
11063
11064 #if defined(NETWORK_AVALIABLE)
11065     /* last chance to get network player actions without main loop delay */
11066     HandleNetworking();
11067 #endif
11068
11069     /* game was quit by network peer */
11070     if (game_status != GAME_MODE_PLAYING)
11071       return;
11072
11073     if (!network_player_action_received)
11074       return;           /* failed to get network player actions in time */
11075
11076     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11077   }
11078
11079   if (tape.pausing)
11080     return;
11081
11082   /* at this point we know that we really continue executing the game */
11083
11084   network_player_action_received = FALSE;
11085
11086   /* when playing tape, read previously recorded player input from tape data */
11087   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11088
11089   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11090   if (tape.pausing)
11091     return;
11092
11093   if (tape.set_centered_player)
11094   {
11095     game.centered_player_nr_next = tape.centered_player_nr_next;
11096     game.set_centered_player = TRUE;
11097   }
11098
11099   for (i = 0; i < MAX_PLAYERS; i++)
11100   {
11101     summarized_player_action |= stored_player[i].action;
11102
11103     if (!network_playing && (game.team_mode || tape.playing))
11104       stored_player[i].effective_action = stored_player[i].action;
11105   }
11106
11107 #if defined(NETWORK_AVALIABLE)
11108   if (network_playing)
11109     SendToServer_MovePlayer(summarized_player_action);
11110 #endif
11111
11112   // summarize all actions at local players mapped input device position
11113   // (this allows using different input devices in single player mode)
11114   if (!options.network && !game.team_mode)
11115     stored_player[map_player_action[local_player->index_nr]].effective_action =
11116       summarized_player_action;
11117
11118   if (tape.recording &&
11119       setup.team_mode &&
11120       setup.input_on_focus &&
11121       game.centered_player_nr != -1)
11122   {
11123     for (i = 0; i < MAX_PLAYERS; i++)
11124       stored_player[i].effective_action =
11125         (i == game.centered_player_nr ? summarized_player_action : 0);
11126   }
11127
11128   if (recorded_player_action != NULL)
11129     for (i = 0; i < MAX_PLAYERS; i++)
11130       stored_player[i].effective_action = recorded_player_action[i];
11131
11132   for (i = 0; i < MAX_PLAYERS; i++)
11133   {
11134     tape_action[i] = stored_player[i].effective_action;
11135
11136     /* (this may happen in the RND game engine if a player was not present on
11137        the playfield on level start, but appeared later from a custom element */
11138     if (setup.team_mode &&
11139         tape.recording &&
11140         tape_action[i] &&
11141         !tape.player_participates[i])
11142       tape.player_participates[i] = TRUE;
11143   }
11144
11145   /* only record actions from input devices, but not programmed actions */
11146   if (tape.recording)
11147     TapeRecordAction(tape_action);
11148
11149 #if USE_NEW_PLAYER_ASSIGNMENTS
11150   // !!! also map player actions in single player mode !!!
11151   // if (game.team_mode)
11152   if (1)
11153   {
11154     byte mapped_action[MAX_PLAYERS];
11155
11156 #if DEBUG_PLAYER_ACTIONS
11157     printf(":::");
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       printf(" %d, ", stored_player[i].effective_action);
11160 #endif
11161
11162     for (i = 0; i < MAX_PLAYERS; i++)
11163       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11164
11165     for (i = 0; i < MAX_PLAYERS; i++)
11166       stored_player[i].effective_action = mapped_action[i];
11167
11168 #if DEBUG_PLAYER_ACTIONS
11169     printf(" =>");
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       printf(" %d, ", stored_player[i].effective_action);
11172     printf("\n");
11173 #endif
11174   }
11175 #if DEBUG_PLAYER_ACTIONS
11176   else
11177   {
11178     printf(":::");
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180       printf(" %d, ", stored_player[i].effective_action);
11181     printf("\n");
11182   }
11183 #endif
11184 #endif
11185
11186   for (i = 0; i < MAX_PLAYERS; i++)
11187   {
11188     // allow engine snapshot in case of changed movement attempt
11189     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11190         (stored_player[i].effective_action & KEY_MOTION))
11191       game.snapshot.changed_action = TRUE;
11192
11193     // allow engine snapshot in case of snapping/dropping attempt
11194     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11195         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11196       game.snapshot.changed_action = TRUE;
11197
11198     game.snapshot.last_action[i] = stored_player[i].effective_action;
11199   }
11200
11201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11202   {
11203     GameActions_EM_Main();
11204   }
11205   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11206   {
11207     GameActions_SP_Main();
11208   }
11209   else
11210   {
11211     GameActions_RND_Main();
11212   }
11213
11214   BlitScreenToBitmap(backbuffer);
11215
11216   CheckLevelTime();
11217
11218   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11219
11220   if (options.debug)                    /* calculate frames per second */
11221   {
11222     static unsigned int fps_counter = 0;
11223     static int fps_frames = 0;
11224     unsigned int fps_delay_ms = Counter() - fps_counter;
11225
11226     fps_frames++;
11227
11228     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11229     {
11230       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11231
11232       fps_frames = 0;
11233       fps_counter = Counter();
11234     }
11235
11236     redraw_mask |= REDRAW_FPS;
11237   }
11238 }
11239
11240 void GameActions_EM_Main()
11241 {
11242   byte effective_action[MAX_PLAYERS];
11243   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11244   int i;
11245
11246   for (i = 0; i < MAX_PLAYERS; i++)
11247     effective_action[i] = stored_player[i].effective_action;
11248
11249   GameActions_EM(effective_action, warp_mode);
11250 }
11251
11252 void GameActions_SP_Main()
11253 {
11254   byte effective_action[MAX_PLAYERS];
11255   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11256   int i;
11257
11258   for (i = 0; i < MAX_PLAYERS; i++)
11259     effective_action[i] = stored_player[i].effective_action;
11260
11261   GameActions_SP(effective_action, warp_mode);
11262 }
11263
11264 void GameActions_RND_Main()
11265 {
11266   GameActions_RND();
11267 }
11268
11269 void GameActions_RND()
11270 {
11271   int magic_wall_x = 0, magic_wall_y = 0;
11272   int i, x, y, element, graphic;
11273
11274   InitPlayfieldScanModeVars();
11275
11276   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11277   {
11278     SCAN_PLAYFIELD(x, y)
11279     {
11280       ChangeCount[x][y] = 0;
11281       ChangeEvent[x][y] = -1;
11282     }
11283   }
11284
11285   if (game.set_centered_player)
11286   {
11287     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11288
11289     /* switching to "all players" only possible if all players fit to screen */
11290     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11291     {
11292       game.centered_player_nr_next = game.centered_player_nr;
11293       game.set_centered_player = FALSE;
11294     }
11295
11296     /* do not switch focus to non-existing (or non-active) player */
11297     if (game.centered_player_nr_next >= 0 &&
11298         !stored_player[game.centered_player_nr_next].active)
11299     {
11300       game.centered_player_nr_next = game.centered_player_nr;
11301       game.set_centered_player = FALSE;
11302     }
11303   }
11304
11305   if (game.set_centered_player &&
11306       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11307   {
11308     int sx, sy;
11309
11310     if (game.centered_player_nr_next == -1)
11311     {
11312       setScreenCenteredToAllPlayers(&sx, &sy);
11313     }
11314     else
11315     {
11316       sx = stored_player[game.centered_player_nr_next].jx;
11317       sy = stored_player[game.centered_player_nr_next].jy;
11318     }
11319
11320     game.centered_player_nr = game.centered_player_nr_next;
11321     game.set_centered_player = FALSE;
11322
11323     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11324     DrawGameDoorValues();
11325   }
11326
11327   for (i = 0; i < MAX_PLAYERS; i++)
11328   {
11329     int actual_player_action = stored_player[i].effective_action;
11330
11331 #if 1
11332     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11333        - rnd_equinox_tetrachloride 048
11334        - rnd_equinox_tetrachloride_ii 096
11335        - rnd_emanuel_schmieg 002
11336        - doctor_sloan_ww 001, 020
11337     */
11338     if (stored_player[i].MovPos == 0)
11339       CheckGravityMovement(&stored_player[i]);
11340 #endif
11341
11342     /* overwrite programmed action with tape action */
11343     if (stored_player[i].programmed_action)
11344       actual_player_action = stored_player[i].programmed_action;
11345
11346     PlayerActions(&stored_player[i], actual_player_action);
11347
11348     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11349   }
11350
11351   ScrollScreen(NULL, SCROLL_GO_ON);
11352
11353   /* for backwards compatibility, the following code emulates a fixed bug that
11354      occured when pushing elements (causing elements that just made their last
11355      pushing step to already (if possible) make their first falling step in the
11356      same game frame, which is bad); this code is also needed to use the famous
11357      "spring push bug" which is used in older levels and might be wanted to be
11358      used also in newer levels, but in this case the buggy pushing code is only
11359      affecting the "spring" element and no other elements */
11360
11361   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11362   {
11363     for (i = 0; i < MAX_PLAYERS; i++)
11364     {
11365       struct PlayerInfo *player = &stored_player[i];
11366       int x = player->jx;
11367       int y = player->jy;
11368
11369       if (player->active && player->is_pushing && player->is_moving &&
11370           IS_MOVING(x, y) &&
11371           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11372            Feld[x][y] == EL_SPRING))
11373       {
11374         ContinueMoving(x, y);
11375
11376         /* continue moving after pushing (this is actually a bug) */
11377         if (!IS_MOVING(x, y))
11378           Stop[x][y] = FALSE;
11379       }
11380     }
11381   }
11382
11383   SCAN_PLAYFIELD(x, y)
11384   {
11385     ChangeCount[x][y] = 0;
11386     ChangeEvent[x][y] = -1;
11387
11388     /* this must be handled before main playfield loop */
11389     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11390     {
11391       MovDelay[x][y]--;
11392       if (MovDelay[x][y] <= 0)
11393         RemoveField(x, y);
11394     }
11395
11396     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11397     {
11398       MovDelay[x][y]--;
11399       if (MovDelay[x][y] <= 0)
11400       {
11401         RemoveField(x, y);
11402         TEST_DrawLevelField(x, y);
11403
11404         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11405       }
11406     }
11407
11408 #if DEBUG
11409     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11410     {
11411       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11412       printf("GameActions(): This should never happen!\n");
11413
11414       ChangePage[x][y] = -1;
11415     }
11416 #endif
11417
11418     Stop[x][y] = FALSE;
11419     if (WasJustMoving[x][y] > 0)
11420       WasJustMoving[x][y]--;
11421     if (WasJustFalling[x][y] > 0)
11422       WasJustFalling[x][y]--;
11423     if (CheckCollision[x][y] > 0)
11424       CheckCollision[x][y]--;
11425     if (CheckImpact[x][y] > 0)
11426       CheckImpact[x][y]--;
11427
11428     GfxFrame[x][y]++;
11429
11430     /* reset finished pushing action (not done in ContinueMoving() to allow
11431        continuous pushing animation for elements with zero push delay) */
11432     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11433     {
11434       ResetGfxAnimation(x, y);
11435       TEST_DrawLevelField(x, y);
11436     }
11437
11438 #if DEBUG
11439     if (IS_BLOCKED(x, y))
11440     {
11441       int oldx, oldy;
11442
11443       Blocked2Moving(x, y, &oldx, &oldy);
11444       if (!IS_MOVING(oldx, oldy))
11445       {
11446         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11447         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11448         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11449         printf("GameActions(): This should never happen!\n");
11450       }
11451     }
11452 #endif
11453   }
11454
11455   SCAN_PLAYFIELD(x, y)
11456   {
11457     element = Feld[x][y];
11458     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11459
11460     ResetGfxFrame(x, y, TRUE);
11461
11462     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11463         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11464       ResetRandomAnimationValue(x, y);
11465
11466     SetRandomAnimationValue(x, y);
11467
11468     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11469
11470     if (IS_INACTIVE(element))
11471     {
11472       if (IS_ANIMATED(graphic))
11473         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474
11475       continue;
11476     }
11477
11478     /* this may take place after moving, so 'element' may have changed */
11479     if (IS_CHANGING(x, y) &&
11480         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11481     {
11482       int page = element_info[element].event_page_nr[CE_DELAY];
11483
11484       HandleElementChange(x, y, page);
11485
11486       element = Feld[x][y];
11487       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11488     }
11489
11490     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11491     {
11492       StartMoving(x, y);
11493
11494       element = Feld[x][y];
11495       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11496
11497       if (IS_ANIMATED(graphic) &&
11498           !IS_MOVING(x, y) &&
11499           !Stop[x][y])
11500         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501
11502       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11503         TEST_DrawTwinkleOnField(x, y);
11504     }
11505     else if ((element == EL_ACID ||
11506               element == EL_EXIT_OPEN ||
11507               element == EL_EM_EXIT_OPEN ||
11508               element == EL_SP_EXIT_OPEN ||
11509               element == EL_STEEL_EXIT_OPEN ||
11510               element == EL_EM_STEEL_EXIT_OPEN ||
11511               element == EL_SP_TERMINAL ||
11512               element == EL_SP_TERMINAL_ACTIVE ||
11513               element == EL_EXTRA_TIME ||
11514               element == EL_SHIELD_NORMAL ||
11515               element == EL_SHIELD_DEADLY) &&
11516              IS_ANIMATED(graphic))
11517       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11518     else if (IS_MOVING(x, y))
11519       ContinueMoving(x, y);
11520     else if (IS_ACTIVE_BOMB(element))
11521       CheckDynamite(x, y);
11522     else if (element == EL_AMOEBA_GROWING)
11523       AmoebeWaechst(x, y);
11524     else if (element == EL_AMOEBA_SHRINKING)
11525       AmoebaDisappearing(x, y);
11526
11527 #if !USE_NEW_AMOEBA_CODE
11528     else if (IS_AMOEBALIVE(element))
11529       AmoebeAbleger(x, y);
11530 #endif
11531
11532     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11533       Life(x, y);
11534     else if (element == EL_EXIT_CLOSED)
11535       CheckExit(x, y);
11536     else if (element == EL_EM_EXIT_CLOSED)
11537       CheckExitEM(x, y);
11538     else if (element == EL_STEEL_EXIT_CLOSED)
11539       CheckExitSteel(x, y);
11540     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11541       CheckExitSteelEM(x, y);
11542     else if (element == EL_SP_EXIT_CLOSED)
11543       CheckExitSP(x, y);
11544     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11545              element == EL_EXPANDABLE_STEELWALL_GROWING)
11546       MauerWaechst(x, y);
11547     else if (element == EL_EXPANDABLE_WALL ||
11548              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11549              element == EL_EXPANDABLE_WALL_VERTICAL ||
11550              element == EL_EXPANDABLE_WALL_ANY ||
11551              element == EL_BD_EXPANDABLE_WALL)
11552       MauerAbleger(x, y);
11553     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11554              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11555              element == EL_EXPANDABLE_STEELWALL_ANY)
11556       MauerAblegerStahl(x, y);
11557     else if (element == EL_FLAMES)
11558       CheckForDragon(x, y);
11559     else if (element == EL_EXPLOSION)
11560       ; /* drawing of correct explosion animation is handled separately */
11561     else if (element == EL_ELEMENT_SNAPPING ||
11562              element == EL_DIAGONAL_SHRINKING ||
11563              element == EL_DIAGONAL_GROWING)
11564     {
11565       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11566
11567       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11568     }
11569     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11570       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11571
11572     if (IS_BELT_ACTIVE(element))
11573       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11574
11575     if (game.magic_wall_active)
11576     {
11577       int jx = local_player->jx, jy = local_player->jy;
11578
11579       /* play the element sound at the position nearest to the player */
11580       if ((element == EL_MAGIC_WALL_FULL ||
11581            element == EL_MAGIC_WALL_ACTIVE ||
11582            element == EL_MAGIC_WALL_EMPTYING ||
11583            element == EL_BD_MAGIC_WALL_FULL ||
11584            element == EL_BD_MAGIC_WALL_ACTIVE ||
11585            element == EL_BD_MAGIC_WALL_EMPTYING ||
11586            element == EL_DC_MAGIC_WALL_FULL ||
11587            element == EL_DC_MAGIC_WALL_ACTIVE ||
11588            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11589           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11590       {
11591         magic_wall_x = x;
11592         magic_wall_y = y;
11593       }
11594     }
11595   }
11596
11597 #if USE_NEW_AMOEBA_CODE
11598   /* new experimental amoeba growth stuff */
11599   if (!(FrameCounter % 8))
11600   {
11601     static unsigned int random = 1684108901;
11602
11603     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11604     {
11605       x = RND(lev_fieldx);
11606       y = RND(lev_fieldy);
11607       element = Feld[x][y];
11608
11609       if (!IS_PLAYER(x,y) &&
11610           (element == EL_EMPTY ||
11611            CAN_GROW_INTO(element) ||
11612            element == EL_QUICKSAND_EMPTY ||
11613            element == EL_QUICKSAND_FAST_EMPTY ||
11614            element == EL_ACID_SPLASH_LEFT ||
11615            element == EL_ACID_SPLASH_RIGHT))
11616       {
11617         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11618             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11619             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11620             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11621           Feld[x][y] = EL_AMOEBA_DROP;
11622       }
11623
11624       random = random * 129 + 1;
11625     }
11626   }
11627 #endif
11628
11629   game.explosions_delayed = FALSE;
11630
11631   SCAN_PLAYFIELD(x, y)
11632   {
11633     element = Feld[x][y];
11634
11635     if (ExplodeField[x][y])
11636       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11637     else if (element == EL_EXPLOSION)
11638       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11639
11640     ExplodeField[x][y] = EX_TYPE_NONE;
11641   }
11642
11643   game.explosions_delayed = TRUE;
11644
11645   if (game.magic_wall_active)
11646   {
11647     if (!(game.magic_wall_time_left % 4))
11648     {
11649       int element = Feld[magic_wall_x][magic_wall_y];
11650
11651       if (element == EL_BD_MAGIC_WALL_FULL ||
11652           element == EL_BD_MAGIC_WALL_ACTIVE ||
11653           element == EL_BD_MAGIC_WALL_EMPTYING)
11654         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11655       else if (element == EL_DC_MAGIC_WALL_FULL ||
11656                element == EL_DC_MAGIC_WALL_ACTIVE ||
11657                element == EL_DC_MAGIC_WALL_EMPTYING)
11658         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11659       else
11660         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11661     }
11662
11663     if (game.magic_wall_time_left > 0)
11664     {
11665       game.magic_wall_time_left--;
11666
11667       if (!game.magic_wall_time_left)
11668       {
11669         SCAN_PLAYFIELD(x, y)
11670         {
11671           element = Feld[x][y];
11672
11673           if (element == EL_MAGIC_WALL_ACTIVE ||
11674               element == EL_MAGIC_WALL_FULL)
11675           {
11676             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11677             TEST_DrawLevelField(x, y);
11678           }
11679           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11680                    element == EL_BD_MAGIC_WALL_FULL)
11681           {
11682             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11683             TEST_DrawLevelField(x, y);
11684           }
11685           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11686                    element == EL_DC_MAGIC_WALL_FULL)
11687           {
11688             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11689             TEST_DrawLevelField(x, y);
11690           }
11691         }
11692
11693         game.magic_wall_active = FALSE;
11694       }
11695     }
11696   }
11697
11698   if (game.light_time_left > 0)
11699   {
11700     game.light_time_left--;
11701
11702     if (game.light_time_left == 0)
11703       RedrawAllLightSwitchesAndInvisibleElements();
11704   }
11705
11706   if (game.timegate_time_left > 0)
11707   {
11708     game.timegate_time_left--;
11709
11710     if (game.timegate_time_left == 0)
11711       CloseAllOpenTimegates();
11712   }
11713
11714   if (game.lenses_time_left > 0)
11715   {
11716     game.lenses_time_left--;
11717
11718     if (game.lenses_time_left == 0)
11719       RedrawAllInvisibleElementsForLenses();
11720   }
11721
11722   if (game.magnify_time_left > 0)
11723   {
11724     game.magnify_time_left--;
11725
11726     if (game.magnify_time_left == 0)
11727       RedrawAllInvisibleElementsForMagnifier();
11728   }
11729
11730   for (i = 0; i < MAX_PLAYERS; i++)
11731   {
11732     struct PlayerInfo *player = &stored_player[i];
11733
11734     if (SHIELD_ON(player))
11735     {
11736       if (player->shield_deadly_time_left)
11737         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11738       else if (player->shield_normal_time_left)
11739         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11740     }
11741   }
11742
11743 #if USE_DELAYED_GFX_REDRAW
11744   SCAN_PLAYFIELD(x, y)
11745   {
11746     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11747     {
11748       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11749          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11750
11751       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11752         DrawLevelField(x, y);
11753
11754       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11755         DrawLevelFieldCrumbled(x, y);
11756
11757       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11758         DrawLevelFieldCrumbledNeighbours(x, y);
11759
11760       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11761         DrawTwinkleOnField(x, y);
11762     }
11763
11764     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11765   }
11766 #endif
11767
11768   DrawAllPlayers();
11769   PlayAllPlayersSound();
11770
11771   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11772   {
11773     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11774
11775     local_player->show_envelope = 0;
11776   }
11777
11778   /* use random number generator in every frame to make it less predictable */
11779   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11780     RND(1);
11781 }
11782
11783 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11784 {
11785   int min_x = x, min_y = y, max_x = x, max_y = y;
11786   int i;
11787
11788   for (i = 0; i < MAX_PLAYERS; i++)
11789   {
11790     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11791
11792     if (!stored_player[i].active || &stored_player[i] == player)
11793       continue;
11794
11795     min_x = MIN(min_x, jx);
11796     min_y = MIN(min_y, jy);
11797     max_x = MAX(max_x, jx);
11798     max_y = MAX(max_y, jy);
11799   }
11800
11801   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11802 }
11803
11804 static boolean AllPlayersInVisibleScreen()
11805 {
11806   int i;
11807
11808   for (i = 0; i < MAX_PLAYERS; i++)
11809   {
11810     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11811
11812     if (!stored_player[i].active)
11813       continue;
11814
11815     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11816       return FALSE;
11817   }
11818
11819   return TRUE;
11820 }
11821
11822 void ScrollLevel(int dx, int dy)
11823 {
11824   int scroll_offset = 2 * TILEX_VAR;
11825   int x, y;
11826
11827   BlitBitmap(drawto_field, drawto_field,
11828              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11829              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11830              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11831              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11832              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11833              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11834
11835   if (dx != 0)
11836   {
11837     x = (dx == 1 ? BX1 : BX2);
11838     for (y = BY1; y <= BY2; y++)
11839       DrawScreenField(x, y);
11840   }
11841
11842   if (dy != 0)
11843   {
11844     y = (dy == 1 ? BY1 : BY2);
11845     for (x = BX1; x <= BX2; x++)
11846       DrawScreenField(x, y);
11847   }
11848
11849   redraw_mask |= REDRAW_FIELD;
11850 }
11851
11852 static boolean canFallDown(struct PlayerInfo *player)
11853 {
11854   int jx = player->jx, jy = player->jy;
11855
11856   return (IN_LEV_FIELD(jx, jy + 1) &&
11857           (IS_FREE(jx, jy + 1) ||
11858            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11859           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11860           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11861 }
11862
11863 static boolean canPassField(int x, int y, int move_dir)
11864 {
11865   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11866   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11867   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11868   int nextx = x + dx;
11869   int nexty = y + dy;
11870   int element = Feld[x][y];
11871
11872   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11873           !CAN_MOVE(element) &&
11874           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11875           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11876           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11877 }
11878
11879 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11880 {
11881   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11882   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11883   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11884   int newx = x + dx;
11885   int newy = y + dy;
11886
11887   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11888           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11889           (IS_DIGGABLE(Feld[newx][newy]) ||
11890            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11891            canPassField(newx, newy, move_dir)));
11892 }
11893
11894 static void CheckGravityMovement(struct PlayerInfo *player)
11895 {
11896   if (player->gravity && !player->programmed_action)
11897   {
11898     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11899     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11900     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11901     int jx = player->jx, jy = player->jy;
11902     boolean player_is_moving_to_valid_field =
11903       (!player_is_snapping &&
11904        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11905         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11906     boolean player_can_fall_down = canFallDown(player);
11907
11908     if (player_can_fall_down &&
11909         !player_is_moving_to_valid_field)
11910       player->programmed_action = MV_DOWN;
11911   }
11912 }
11913
11914 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11915 {
11916   return CheckGravityMovement(player);
11917
11918   if (player->gravity && !player->programmed_action)
11919   {
11920     int jx = player->jx, jy = player->jy;
11921     boolean field_under_player_is_free =
11922       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11923     boolean player_is_standing_on_valid_field =
11924       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11925        (IS_WALKABLE(Feld[jx][jy]) &&
11926         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11927
11928     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11929       player->programmed_action = MV_DOWN;
11930   }
11931 }
11932
11933 /*
11934   MovePlayerOneStep()
11935   -----------------------------------------------------------------------------
11936   dx, dy:               direction (non-diagonal) to try to move the player to
11937   real_dx, real_dy:     direction as read from input device (can be diagonal)
11938 */
11939
11940 boolean MovePlayerOneStep(struct PlayerInfo *player,
11941                           int dx, int dy, int real_dx, int real_dy)
11942 {
11943   int jx = player->jx, jy = player->jy;
11944   int new_jx = jx + dx, new_jy = jy + dy;
11945   int can_move;
11946   boolean player_can_move = !player->cannot_move;
11947
11948   if (!player->active || (!dx && !dy))
11949     return MP_NO_ACTION;
11950
11951   player->MovDir = (dx < 0 ? MV_LEFT :
11952                     dx > 0 ? MV_RIGHT :
11953                     dy < 0 ? MV_UP :
11954                     dy > 0 ? MV_DOWN :  MV_NONE);
11955
11956   if (!IN_LEV_FIELD(new_jx, new_jy))
11957     return MP_NO_ACTION;
11958
11959   if (!player_can_move)
11960   {
11961     if (player->MovPos == 0)
11962     {
11963       player->is_moving = FALSE;
11964       player->is_digging = FALSE;
11965       player->is_collecting = FALSE;
11966       player->is_snapping = FALSE;
11967       player->is_pushing = FALSE;
11968     }
11969   }
11970
11971   if (!options.network && game.centered_player_nr == -1 &&
11972       !AllPlayersInSight(player, new_jx, new_jy))
11973     return MP_NO_ACTION;
11974
11975   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11976   if (can_move != MP_MOVING)
11977     return can_move;
11978
11979   /* check if DigField() has caused relocation of the player */
11980   if (player->jx != jx || player->jy != jy)
11981     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11982
11983   StorePlayer[jx][jy] = 0;
11984   player->last_jx = jx;
11985   player->last_jy = jy;
11986   player->jx = new_jx;
11987   player->jy = new_jy;
11988   StorePlayer[new_jx][new_jy] = player->element_nr;
11989
11990   if (player->move_delay_value_next != -1)
11991   {
11992     player->move_delay_value = player->move_delay_value_next;
11993     player->move_delay_value_next = -1;
11994   }
11995
11996   player->MovPos =
11997     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11998
11999   player->step_counter++;
12000
12001   PlayerVisit[jx][jy] = FrameCounter;
12002
12003   player->is_moving = TRUE;
12004
12005 #if 1
12006   /* should better be called in MovePlayer(), but this breaks some tapes */
12007   ScrollPlayer(player, SCROLL_INIT);
12008 #endif
12009
12010   return MP_MOVING;
12011 }
12012
12013 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12014 {
12015   int jx = player->jx, jy = player->jy;
12016   int old_jx = jx, old_jy = jy;
12017   int moved = MP_NO_ACTION;
12018
12019   if (!player->active)
12020     return FALSE;
12021
12022   if (!dx && !dy)
12023   {
12024     if (player->MovPos == 0)
12025     {
12026       player->is_moving = FALSE;
12027       player->is_digging = FALSE;
12028       player->is_collecting = FALSE;
12029       player->is_snapping = FALSE;
12030       player->is_pushing = FALSE;
12031     }
12032
12033     return FALSE;
12034   }
12035
12036   if (player->move_delay > 0)
12037     return FALSE;
12038
12039   player->move_delay = -1;              /* set to "uninitialized" value */
12040
12041   /* store if player is automatically moved to next field */
12042   player->is_auto_moving = (player->programmed_action != MV_NONE);
12043
12044   /* remove the last programmed player action */
12045   player->programmed_action = 0;
12046
12047   if (player->MovPos)
12048   {
12049     /* should only happen if pre-1.2 tape recordings are played */
12050     /* this is only for backward compatibility */
12051
12052     int original_move_delay_value = player->move_delay_value;
12053
12054 #if DEBUG
12055     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12056            tape.counter);
12057 #endif
12058
12059     /* scroll remaining steps with finest movement resolution */
12060     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12061
12062     while (player->MovPos)
12063     {
12064       ScrollPlayer(player, SCROLL_GO_ON);
12065       ScrollScreen(NULL, SCROLL_GO_ON);
12066
12067       AdvanceFrameAndPlayerCounters(player->index_nr);
12068
12069       DrawAllPlayers();
12070       BackToFront_WithFrameDelay(0);
12071     }
12072
12073     player->move_delay_value = original_move_delay_value;
12074   }
12075
12076   player->is_active = FALSE;
12077
12078   if (player->last_move_dir & MV_HORIZONTAL)
12079   {
12080     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12081       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12082   }
12083   else
12084   {
12085     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12086       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12087   }
12088
12089   if (!moved && !player->is_active)
12090   {
12091     player->is_moving = FALSE;
12092     player->is_digging = FALSE;
12093     player->is_collecting = FALSE;
12094     player->is_snapping = FALSE;
12095     player->is_pushing = FALSE;
12096   }
12097
12098   jx = player->jx;
12099   jy = player->jy;
12100
12101   if (moved & MP_MOVING && !ScreenMovPos &&
12102       (player->index_nr == game.centered_player_nr ||
12103        game.centered_player_nr == -1))
12104   {
12105     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12106     int offset = game.scroll_delay_value;
12107
12108     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12109     {
12110       /* actual player has left the screen -- scroll in that direction */
12111       if (jx != old_jx)         /* player has moved horizontally */
12112         scroll_x += (jx - old_jx);
12113       else                      /* player has moved vertically */
12114         scroll_y += (jy - old_jy);
12115     }
12116     else
12117     {
12118       if (jx != old_jx)         /* player has moved horizontally */
12119       {
12120         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12121             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12122           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12123
12124         /* don't scroll over playfield boundaries */
12125         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12126           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12127
12128         /* don't scroll more than one field at a time */
12129         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12130
12131         /* don't scroll against the player's moving direction */
12132         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12133             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12134           scroll_x = old_scroll_x;
12135       }
12136       else                      /* player has moved vertically */
12137       {
12138         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12139             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12140           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12141
12142         /* don't scroll over playfield boundaries */
12143         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12144           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12145
12146         /* don't scroll more than one field at a time */
12147         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12148
12149         /* don't scroll against the player's moving direction */
12150         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12151             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12152           scroll_y = old_scroll_y;
12153       }
12154     }
12155
12156     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12157     {
12158       if (!options.network && game.centered_player_nr == -1 &&
12159           !AllPlayersInVisibleScreen())
12160       {
12161         scroll_x = old_scroll_x;
12162         scroll_y = old_scroll_y;
12163       }
12164       else
12165       {
12166         ScrollScreen(player, SCROLL_INIT);
12167         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12168       }
12169     }
12170   }
12171
12172   player->StepFrame = 0;
12173
12174   if (moved & MP_MOVING)
12175   {
12176     if (old_jx != jx && old_jy == jy)
12177       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12178     else if (old_jx == jx && old_jy != jy)
12179       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12180
12181     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12182
12183     player->last_move_dir = player->MovDir;
12184     player->is_moving = TRUE;
12185     player->is_snapping = FALSE;
12186     player->is_switching = FALSE;
12187     player->is_dropping = FALSE;
12188     player->is_dropping_pressed = FALSE;
12189     player->drop_pressed_delay = 0;
12190
12191 #if 0
12192     /* should better be called here than above, but this breaks some tapes */
12193     ScrollPlayer(player, SCROLL_INIT);
12194 #endif
12195   }
12196   else
12197   {
12198     CheckGravityMovementWhenNotMoving(player);
12199
12200     player->is_moving = FALSE;
12201
12202     /* at this point, the player is allowed to move, but cannot move right now
12203        (e.g. because of something blocking the way) -- ensure that the player
12204        is also allowed to move in the next frame (in old versions before 3.1.1,
12205        the player was forced to wait again for eight frames before next try) */
12206
12207     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12208       player->move_delay = 0;   /* allow direct movement in the next frame */
12209   }
12210
12211   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12212     player->move_delay = player->move_delay_value;
12213
12214   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12215   {
12216     TestIfPlayerTouchesBadThing(jx, jy);
12217     TestIfPlayerTouchesCustomElement(jx, jy);
12218   }
12219
12220   if (!player->active)
12221     RemovePlayer(player);
12222
12223   return moved;
12224 }
12225
12226 void ScrollPlayer(struct PlayerInfo *player, int mode)
12227 {
12228   int jx = player->jx, jy = player->jy;
12229   int last_jx = player->last_jx, last_jy = player->last_jy;
12230   int move_stepsize = TILEX / player->move_delay_value;
12231
12232   if (!player->active)
12233     return;
12234
12235   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12236     return;
12237
12238   if (mode == SCROLL_INIT)
12239   {
12240     player->actual_frame_counter = FrameCounter;
12241     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12242
12243     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12244         Feld[last_jx][last_jy] == EL_EMPTY)
12245     {
12246       int last_field_block_delay = 0;   /* start with no blocking at all */
12247       int block_delay_adjustment = player->block_delay_adjustment;
12248
12249       /* if player blocks last field, add delay for exactly one move */
12250       if (player->block_last_field)
12251       {
12252         last_field_block_delay += player->move_delay_value;
12253
12254         /* when blocking enabled, prevent moving up despite gravity */
12255         if (player->gravity && player->MovDir == MV_UP)
12256           block_delay_adjustment = -1;
12257       }
12258
12259       /* add block delay adjustment (also possible when not blocking) */
12260       last_field_block_delay += block_delay_adjustment;
12261
12262       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12263       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12264     }
12265
12266     if (player->MovPos != 0)    /* player has not yet reached destination */
12267       return;
12268   }
12269   else if (!FrameReached(&player->actual_frame_counter, 1))
12270     return;
12271
12272   if (player->MovPos != 0)
12273   {
12274     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12275     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12276
12277     /* before DrawPlayer() to draw correct player graphic for this case */
12278     if (player->MovPos == 0)
12279       CheckGravityMovement(player);
12280   }
12281
12282   if (player->MovPos == 0)      /* player reached destination field */
12283   {
12284     if (player->move_delay_reset_counter > 0)
12285     {
12286       player->move_delay_reset_counter--;
12287
12288       if (player->move_delay_reset_counter == 0)
12289       {
12290         /* continue with normal speed after quickly moving through gate */
12291         HALVE_PLAYER_SPEED(player);
12292
12293         /* be able to make the next move without delay */
12294         player->move_delay = 0;
12295       }
12296     }
12297
12298     player->last_jx = jx;
12299     player->last_jy = jy;
12300
12301     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12302         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12303         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12304         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12305         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12306         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12307         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12308         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12309     {
12310       DrawPlayer(player);       /* needed here only to cleanup last field */
12311       RemovePlayer(player);
12312
12313       if (local_player->friends_still_needed == 0 ||
12314           IS_SP_ELEMENT(Feld[jx][jy]))
12315         PlayerWins(player);
12316     }
12317
12318     /* this breaks one level: "machine", level 000 */
12319     {
12320       int move_direction = player->MovDir;
12321       int enter_side = MV_DIR_OPPOSITE(move_direction);
12322       int leave_side = move_direction;
12323       int old_jx = last_jx;
12324       int old_jy = last_jy;
12325       int old_element = Feld[old_jx][old_jy];
12326       int new_element = Feld[jx][jy];
12327
12328       if (IS_CUSTOM_ELEMENT(old_element))
12329         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12330                                    CE_LEFT_BY_PLAYER,
12331                                    player->index_bit, leave_side);
12332
12333       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12334                                           CE_PLAYER_LEAVES_X,
12335                                           player->index_bit, leave_side);
12336
12337       if (IS_CUSTOM_ELEMENT(new_element))
12338         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12339                                    player->index_bit, enter_side);
12340
12341       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12342                                           CE_PLAYER_ENTERS_X,
12343                                           player->index_bit, enter_side);
12344
12345       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12346                                         CE_MOVE_OF_X, move_direction);
12347     }
12348
12349     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12350     {
12351       TestIfPlayerTouchesBadThing(jx, jy);
12352       TestIfPlayerTouchesCustomElement(jx, jy);
12353
12354       /* needed because pushed element has not yet reached its destination,
12355          so it would trigger a change event at its previous field location */
12356       if (!player->is_pushing)
12357         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12358
12359       if (!player->active)
12360         RemovePlayer(player);
12361     }
12362
12363     if (!local_player->LevelSolved && level.use_step_counter)
12364     {
12365       int i;
12366
12367       TimePlayed++;
12368
12369       if (TimeLeft > 0)
12370       {
12371         TimeLeft--;
12372
12373         if (TimeLeft <= 10 && setup.time_limit)
12374           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12375
12376         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12377
12378         DisplayGameControlValues();
12379
12380         if (!TimeLeft && setup.time_limit)
12381           for (i = 0; i < MAX_PLAYERS; i++)
12382             KillPlayer(&stored_player[i]);
12383       }
12384       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12385       {
12386         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12387
12388         DisplayGameControlValues();
12389       }
12390     }
12391
12392     if (tape.single_step && tape.recording && !tape.pausing &&
12393         !player->programmed_action)
12394       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12395
12396     if (!player->programmed_action)
12397       CheckSaveEngineSnapshot(player);
12398   }
12399 }
12400
12401 void ScrollScreen(struct PlayerInfo *player, int mode)
12402 {
12403   static unsigned int screen_frame_counter = 0;
12404
12405   if (mode == SCROLL_INIT)
12406   {
12407     /* set scrolling step size according to actual player's moving speed */
12408     ScrollStepSize = TILEX / player->move_delay_value;
12409
12410     screen_frame_counter = FrameCounter;
12411     ScreenMovDir = player->MovDir;
12412     ScreenMovPos = player->MovPos;
12413     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12414     return;
12415   }
12416   else if (!FrameReached(&screen_frame_counter, 1))
12417     return;
12418
12419   if (ScreenMovPos)
12420   {
12421     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12422     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12423     redraw_mask |= REDRAW_FIELD;
12424   }
12425   else
12426     ScreenMovDir = MV_NONE;
12427 }
12428
12429 void TestIfPlayerTouchesCustomElement(int x, int y)
12430 {
12431   static int xy[4][2] =
12432   {
12433     { 0, -1 },
12434     { -1, 0 },
12435     { +1, 0 },
12436     { 0, +1 }
12437   };
12438   static int trigger_sides[4][2] =
12439   {
12440     /* center side       border side */
12441     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12442     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12443     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12444     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12445   };
12446   static int touch_dir[4] =
12447   {
12448     MV_LEFT | MV_RIGHT,
12449     MV_UP   | MV_DOWN,
12450     MV_UP   | MV_DOWN,
12451     MV_LEFT | MV_RIGHT
12452   };
12453   int center_element = Feld[x][y];      /* should always be non-moving! */
12454   int i;
12455
12456   for (i = 0; i < NUM_DIRECTIONS; i++)
12457   {
12458     int xx = x + xy[i][0];
12459     int yy = y + xy[i][1];
12460     int center_side = trigger_sides[i][0];
12461     int border_side = trigger_sides[i][1];
12462     int border_element;
12463
12464     if (!IN_LEV_FIELD(xx, yy))
12465       continue;
12466
12467     if (IS_PLAYER(x, y))                /* player found at center element */
12468     {
12469       struct PlayerInfo *player = PLAYERINFO(x, y);
12470
12471       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12472         border_element = Feld[xx][yy];          /* may be moving! */
12473       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12474         border_element = Feld[xx][yy];
12475       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12476         border_element = MovingOrBlocked2Element(xx, yy);
12477       else
12478         continue;               /* center and border element do not touch */
12479
12480       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12481                                  player->index_bit, border_side);
12482       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12483                                           CE_PLAYER_TOUCHES_X,
12484                                           player->index_bit, border_side);
12485
12486       {
12487         /* use player element that is initially defined in the level playfield,
12488            not the player element that corresponds to the runtime player number
12489            (example: a level that contains EL_PLAYER_3 as the only player would
12490            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12491         int player_element = PLAYERINFO(x, y)->initial_element;
12492
12493         CheckElementChangeBySide(xx, yy, border_element, player_element,
12494                                  CE_TOUCHING_X, border_side);
12495       }
12496     }
12497     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12498     {
12499       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12500
12501       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12502       {
12503         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12504           continue;             /* center and border element do not touch */
12505       }
12506
12507       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12508                                  player->index_bit, center_side);
12509       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12510                                           CE_PLAYER_TOUCHES_X,
12511                                           player->index_bit, center_side);
12512
12513       {
12514         /* use player element that is initially defined in the level playfield,
12515            not the player element that corresponds to the runtime player number
12516            (example: a level that contains EL_PLAYER_3 as the only player would
12517            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12518         int player_element = PLAYERINFO(xx, yy)->initial_element;
12519
12520         CheckElementChangeBySide(x, y, center_element, player_element,
12521                                  CE_TOUCHING_X, center_side);
12522       }
12523
12524       break;
12525     }
12526   }
12527 }
12528
12529 void TestIfElementTouchesCustomElement(int x, int y)
12530 {
12531   static int xy[4][2] =
12532   {
12533     { 0, -1 },
12534     { -1, 0 },
12535     { +1, 0 },
12536     { 0, +1 }
12537   };
12538   static int trigger_sides[4][2] =
12539   {
12540     /* center side      border side */
12541     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12542     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12543     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12544     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12545   };
12546   static int touch_dir[4] =
12547   {
12548     MV_LEFT | MV_RIGHT,
12549     MV_UP   | MV_DOWN,
12550     MV_UP   | MV_DOWN,
12551     MV_LEFT | MV_RIGHT
12552   };
12553   boolean change_center_element = FALSE;
12554   int center_element = Feld[x][y];      /* should always be non-moving! */
12555   int border_element_old[NUM_DIRECTIONS];
12556   int i;
12557
12558   for (i = 0; i < NUM_DIRECTIONS; i++)
12559   {
12560     int xx = x + xy[i][0];
12561     int yy = y + xy[i][1];
12562     int border_element;
12563
12564     border_element_old[i] = -1;
12565
12566     if (!IN_LEV_FIELD(xx, yy))
12567       continue;
12568
12569     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12570       border_element = Feld[xx][yy];    /* may be moving! */
12571     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12572       border_element = Feld[xx][yy];
12573     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12574       border_element = MovingOrBlocked2Element(xx, yy);
12575     else
12576       continue;                 /* center and border element do not touch */
12577
12578     border_element_old[i] = border_element;
12579   }
12580
12581   for (i = 0; i < NUM_DIRECTIONS; i++)
12582   {
12583     int xx = x + xy[i][0];
12584     int yy = y + xy[i][1];
12585     int center_side = trigger_sides[i][0];
12586     int border_element = border_element_old[i];
12587
12588     if (border_element == -1)
12589       continue;
12590
12591     /* check for change of border element */
12592     CheckElementChangeBySide(xx, yy, border_element, center_element,
12593                              CE_TOUCHING_X, center_side);
12594
12595     /* (center element cannot be player, so we dont have to check this here) */
12596   }
12597
12598   for (i = 0; i < NUM_DIRECTIONS; i++)
12599   {
12600     int xx = x + xy[i][0];
12601     int yy = y + xy[i][1];
12602     int border_side = trigger_sides[i][1];
12603     int border_element = border_element_old[i];
12604
12605     if (border_element == -1)
12606       continue;
12607
12608     /* check for change of center element (but change it only once) */
12609     if (!change_center_element)
12610       change_center_element =
12611         CheckElementChangeBySide(x, y, center_element, border_element,
12612                                  CE_TOUCHING_X, border_side);
12613
12614     if (IS_PLAYER(xx, yy))
12615     {
12616       /* use player element that is initially defined in the level playfield,
12617          not the player element that corresponds to the runtime player number
12618          (example: a level that contains EL_PLAYER_3 as the only player would
12619          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12620       int player_element = PLAYERINFO(xx, yy)->initial_element;
12621
12622       CheckElementChangeBySide(x, y, center_element, player_element,
12623                                CE_TOUCHING_X, border_side);
12624     }
12625   }
12626 }
12627
12628 void TestIfElementHitsCustomElement(int x, int y, int direction)
12629 {
12630   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12631   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12632   int hitx = x + dx, hity = y + dy;
12633   int hitting_element = Feld[x][y];
12634   int touched_element;
12635
12636   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12637     return;
12638
12639   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12640                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12641
12642   if (IN_LEV_FIELD(hitx, hity))
12643   {
12644     int opposite_direction = MV_DIR_OPPOSITE(direction);
12645     int hitting_side = direction;
12646     int touched_side = opposite_direction;
12647     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12648                           MovDir[hitx][hity] != direction ||
12649                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12650
12651     object_hit = TRUE;
12652
12653     if (object_hit)
12654     {
12655       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12656                                CE_HITTING_X, touched_side);
12657
12658       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12659                                CE_HIT_BY_X, hitting_side);
12660
12661       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12662                                CE_HIT_BY_SOMETHING, opposite_direction);
12663
12664       if (IS_PLAYER(hitx, hity))
12665       {
12666         /* use player element that is initially defined in the level playfield,
12667            not the player element that corresponds to the runtime player number
12668            (example: a level that contains EL_PLAYER_3 as the only player would
12669            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12670         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12671
12672         CheckElementChangeBySide(x, y, hitting_element, player_element,
12673                                  CE_HITTING_X, touched_side);
12674       }
12675     }
12676   }
12677
12678   /* "hitting something" is also true when hitting the playfield border */
12679   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12680                            CE_HITTING_SOMETHING, direction);
12681 }
12682
12683 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12684 {
12685   int i, kill_x = -1, kill_y = -1;
12686
12687   int bad_element = -1;
12688   static int test_xy[4][2] =
12689   {
12690     { 0, -1 },
12691     { -1, 0 },
12692     { +1, 0 },
12693     { 0, +1 }
12694   };
12695   static int test_dir[4] =
12696   {
12697     MV_UP,
12698     MV_LEFT,
12699     MV_RIGHT,
12700     MV_DOWN
12701   };
12702
12703   for (i = 0; i < NUM_DIRECTIONS; i++)
12704   {
12705     int test_x, test_y, test_move_dir, test_element;
12706
12707     test_x = good_x + test_xy[i][0];
12708     test_y = good_y + test_xy[i][1];
12709
12710     if (!IN_LEV_FIELD(test_x, test_y))
12711       continue;
12712
12713     test_move_dir =
12714       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12715
12716     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12717
12718     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12719        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12720     */
12721     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12722         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12723     {
12724       kill_x = test_x;
12725       kill_y = test_y;
12726       bad_element = test_element;
12727
12728       break;
12729     }
12730   }
12731
12732   if (kill_x != -1 || kill_y != -1)
12733   {
12734     if (IS_PLAYER(good_x, good_y))
12735     {
12736       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12737
12738       if (player->shield_deadly_time_left > 0 &&
12739           !IS_INDESTRUCTIBLE(bad_element))
12740         Bang(kill_x, kill_y);
12741       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12742         KillPlayer(player);
12743     }
12744     else
12745       Bang(good_x, good_y);
12746   }
12747 }
12748
12749 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12750 {
12751   int i, kill_x = -1, kill_y = -1;
12752   int bad_element = Feld[bad_x][bad_y];
12753   static int test_xy[4][2] =
12754   {
12755     { 0, -1 },
12756     { -1, 0 },
12757     { +1, 0 },
12758     { 0, +1 }
12759   };
12760   static int touch_dir[4] =
12761   {
12762     MV_LEFT | MV_RIGHT,
12763     MV_UP   | MV_DOWN,
12764     MV_UP   | MV_DOWN,
12765     MV_LEFT | MV_RIGHT
12766   };
12767   static int test_dir[4] =
12768   {
12769     MV_UP,
12770     MV_LEFT,
12771     MV_RIGHT,
12772     MV_DOWN
12773   };
12774
12775   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12776     return;
12777
12778   for (i = 0; i < NUM_DIRECTIONS; i++)
12779   {
12780     int test_x, test_y, test_move_dir, test_element;
12781
12782     test_x = bad_x + test_xy[i][0];
12783     test_y = bad_y + test_xy[i][1];
12784
12785     if (!IN_LEV_FIELD(test_x, test_y))
12786       continue;
12787
12788     test_move_dir =
12789       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12790
12791     test_element = Feld[test_x][test_y];
12792
12793     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12794        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12795     */
12796     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12797         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12798     {
12799       /* good thing is player or penguin that does not move away */
12800       if (IS_PLAYER(test_x, test_y))
12801       {
12802         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12803
12804         if (bad_element == EL_ROBOT && player->is_moving)
12805           continue;     /* robot does not kill player if he is moving */
12806
12807         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12808         {
12809           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12810             continue;           /* center and border element do not touch */
12811         }
12812
12813         kill_x = test_x;
12814         kill_y = test_y;
12815
12816         break;
12817       }
12818       else if (test_element == EL_PENGUIN)
12819       {
12820         kill_x = test_x;
12821         kill_y = test_y;
12822
12823         break;
12824       }
12825     }
12826   }
12827
12828   if (kill_x != -1 || kill_y != -1)
12829   {
12830     if (IS_PLAYER(kill_x, kill_y))
12831     {
12832       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12833
12834       if (player->shield_deadly_time_left > 0 &&
12835           !IS_INDESTRUCTIBLE(bad_element))
12836         Bang(bad_x, bad_y);
12837       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12838         KillPlayer(player);
12839     }
12840     else
12841       Bang(kill_x, kill_y);
12842   }
12843 }
12844
12845 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12846 {
12847   int bad_element = Feld[bad_x][bad_y];
12848   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12849   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12850   int test_x = bad_x + dx, test_y = bad_y + dy;
12851   int test_move_dir, test_element;
12852   int kill_x = -1, kill_y = -1;
12853
12854   if (!IN_LEV_FIELD(test_x, test_y))
12855     return;
12856
12857   test_move_dir =
12858     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12859
12860   test_element = Feld[test_x][test_y];
12861
12862   if (test_move_dir != bad_move_dir)
12863   {
12864     /* good thing can be player or penguin that does not move away */
12865     if (IS_PLAYER(test_x, test_y))
12866     {
12867       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12868
12869       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12870          player as being hit when he is moving towards the bad thing, because
12871          the "get hit by" condition would be lost after the player stops) */
12872       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12873         return;         /* player moves away from bad thing */
12874
12875       kill_x = test_x;
12876       kill_y = test_y;
12877     }
12878     else if (test_element == EL_PENGUIN)
12879     {
12880       kill_x = test_x;
12881       kill_y = test_y;
12882     }
12883   }
12884
12885   if (kill_x != -1 || kill_y != -1)
12886   {
12887     if (IS_PLAYER(kill_x, kill_y))
12888     {
12889       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12890
12891       if (player->shield_deadly_time_left > 0 &&
12892           !IS_INDESTRUCTIBLE(bad_element))
12893         Bang(bad_x, bad_y);
12894       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12895         KillPlayer(player);
12896     }
12897     else
12898       Bang(kill_x, kill_y);
12899   }
12900 }
12901
12902 void TestIfPlayerTouchesBadThing(int x, int y)
12903 {
12904   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12905 }
12906
12907 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12908 {
12909   TestIfGoodThingHitsBadThing(x, y, move_dir);
12910 }
12911
12912 void TestIfBadThingTouchesPlayer(int x, int y)
12913 {
12914   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12915 }
12916
12917 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12918 {
12919   TestIfBadThingHitsGoodThing(x, y, move_dir);
12920 }
12921
12922 void TestIfFriendTouchesBadThing(int x, int y)
12923 {
12924   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12925 }
12926
12927 void TestIfBadThingTouchesFriend(int x, int y)
12928 {
12929   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12930 }
12931
12932 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12933 {
12934   int i, kill_x = bad_x, kill_y = bad_y;
12935   static int xy[4][2] =
12936   {
12937     { 0, -1 },
12938     { -1, 0 },
12939     { +1, 0 },
12940     { 0, +1 }
12941   };
12942
12943   for (i = 0; i < NUM_DIRECTIONS; i++)
12944   {
12945     int x, y, element;
12946
12947     x = bad_x + xy[i][0];
12948     y = bad_y + xy[i][1];
12949     if (!IN_LEV_FIELD(x, y))
12950       continue;
12951
12952     element = Feld[x][y];
12953     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12954         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12955     {
12956       kill_x = x;
12957       kill_y = y;
12958       break;
12959     }
12960   }
12961
12962   if (kill_x != bad_x || kill_y != bad_y)
12963     Bang(bad_x, bad_y);
12964 }
12965
12966 void KillPlayer(struct PlayerInfo *player)
12967 {
12968   int jx = player->jx, jy = player->jy;
12969
12970   if (!player->active)
12971     return;
12972
12973 #if 0
12974   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12975          player->killed, player->active, player->reanimated);
12976 #endif
12977
12978   /* the following code was introduced to prevent an infinite loop when calling
12979      -> Bang()
12980      -> CheckTriggeredElementChangeExt()
12981      -> ExecuteCustomElementAction()
12982      -> KillPlayer()
12983      -> (infinitely repeating the above sequence of function calls)
12984      which occurs when killing the player while having a CE with the setting
12985      "kill player X when explosion of <player X>"; the solution using a new
12986      field "player->killed" was chosen for backwards compatibility, although
12987      clever use of the fields "player->active" etc. would probably also work */
12988 #if 1
12989   if (player->killed)
12990     return;
12991 #endif
12992
12993   player->killed = TRUE;
12994
12995   /* remove accessible field at the player's position */
12996   Feld[jx][jy] = EL_EMPTY;
12997
12998   /* deactivate shield (else Bang()/Explode() would not work right) */
12999   player->shield_normal_time_left = 0;
13000   player->shield_deadly_time_left = 0;
13001
13002 #if 0
13003   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13004          player->killed, player->active, player->reanimated);
13005 #endif
13006
13007   Bang(jx, jy);
13008
13009 #if 0
13010   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13011          player->killed, player->active, player->reanimated);
13012 #endif
13013
13014   if (player->reanimated)       /* killed player may have been reanimated */
13015     player->killed = player->reanimated = FALSE;
13016   else
13017     BuryPlayer(player);
13018 }
13019
13020 static void KillPlayerUnlessEnemyProtected(int x, int y)
13021 {
13022   if (!PLAYER_ENEMY_PROTECTED(x, y))
13023     KillPlayer(PLAYERINFO(x, y));
13024 }
13025
13026 static void KillPlayerUnlessExplosionProtected(int x, int y)
13027 {
13028   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13029     KillPlayer(PLAYERINFO(x, y));
13030 }
13031
13032 void BuryPlayer(struct PlayerInfo *player)
13033 {
13034   int jx = player->jx, jy = player->jy;
13035
13036   if (!player->active)
13037     return;
13038
13039   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13040   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13041
13042   player->GameOver = TRUE;
13043   RemovePlayer(player);
13044 }
13045
13046 void RemovePlayer(struct PlayerInfo *player)
13047 {
13048   int jx = player->jx, jy = player->jy;
13049   int i, found = FALSE;
13050
13051   player->present = FALSE;
13052   player->active = FALSE;
13053
13054   if (!ExplodeField[jx][jy])
13055     StorePlayer[jx][jy] = 0;
13056
13057   if (player->is_moving)
13058     TEST_DrawLevelField(player->last_jx, player->last_jy);
13059
13060   for (i = 0; i < MAX_PLAYERS; i++)
13061     if (stored_player[i].active)
13062       found = TRUE;
13063
13064   if (!found)
13065     AllPlayersGone = TRUE;
13066
13067   ExitX = ZX = jx;
13068   ExitY = ZY = jy;
13069 }
13070
13071 static void setFieldForSnapping(int x, int y, int element, int direction)
13072 {
13073   struct ElementInfo *ei = &element_info[element];
13074   int direction_bit = MV_DIR_TO_BIT(direction);
13075   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13076   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13077                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13078
13079   Feld[x][y] = EL_ELEMENT_SNAPPING;
13080   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13081
13082   ResetGfxAnimation(x, y);
13083
13084   GfxElement[x][y] = element;
13085   GfxAction[x][y] = action;
13086   GfxDir[x][y] = direction;
13087   GfxFrame[x][y] = -1;
13088 }
13089
13090 /*
13091   =============================================================================
13092   checkDiagonalPushing()
13093   -----------------------------------------------------------------------------
13094   check if diagonal input device direction results in pushing of object
13095   (by checking if the alternative direction is walkable, diggable, ...)
13096   =============================================================================
13097 */
13098
13099 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13100                                     int x, int y, int real_dx, int real_dy)
13101 {
13102   int jx, jy, dx, dy, xx, yy;
13103
13104   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13105     return TRUE;
13106
13107   /* diagonal direction: check alternative direction */
13108   jx = player->jx;
13109   jy = player->jy;
13110   dx = x - jx;
13111   dy = y - jy;
13112   xx = jx + (dx == 0 ? real_dx : 0);
13113   yy = jy + (dy == 0 ? real_dy : 0);
13114
13115   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13116 }
13117
13118 /*
13119   =============================================================================
13120   DigField()
13121   -----------------------------------------------------------------------------
13122   x, y:                 field next to player (non-diagonal) to try to dig to
13123   real_dx, real_dy:     direction as read from input device (can be diagonal)
13124   =============================================================================
13125 */
13126
13127 static int DigField(struct PlayerInfo *player,
13128                     int oldx, int oldy, int x, int y,
13129                     int real_dx, int real_dy, int mode)
13130 {
13131   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13132   boolean player_was_pushing = player->is_pushing;
13133   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13134   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13135   int jx = oldx, jy = oldy;
13136   int dx = x - jx, dy = y - jy;
13137   int nextx = x + dx, nexty = y + dy;
13138   int move_direction = (dx == -1 ? MV_LEFT  :
13139                         dx == +1 ? MV_RIGHT :
13140                         dy == -1 ? MV_UP    :
13141                         dy == +1 ? MV_DOWN  : MV_NONE);
13142   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13143   int dig_side = MV_DIR_OPPOSITE(move_direction);
13144   int old_element = Feld[jx][jy];
13145   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13146   int collect_count;
13147
13148   if (is_player)                /* function can also be called by EL_PENGUIN */
13149   {
13150     if (player->MovPos == 0)
13151     {
13152       player->is_digging = FALSE;
13153       player->is_collecting = FALSE;
13154     }
13155
13156     if (player->MovPos == 0)    /* last pushing move finished */
13157       player->is_pushing = FALSE;
13158
13159     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13160     {
13161       player->is_switching = FALSE;
13162       player->push_delay = -1;
13163
13164       return MP_NO_ACTION;
13165     }
13166   }
13167
13168   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13169     old_element = Back[jx][jy];
13170
13171   /* in case of element dropped at player position, check background */
13172   else if (Back[jx][jy] != EL_EMPTY &&
13173            game.engine_version >= VERSION_IDENT(2,2,0,0))
13174     old_element = Back[jx][jy];
13175
13176   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13177     return MP_NO_ACTION;        /* field has no opening in this direction */
13178
13179   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13180     return MP_NO_ACTION;        /* field has no opening in this direction */
13181
13182   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13183   {
13184     SplashAcid(x, y);
13185
13186     Feld[jx][jy] = player->artwork_element;
13187     InitMovingField(jx, jy, MV_DOWN);
13188     Store[jx][jy] = EL_ACID;
13189     ContinueMoving(jx, jy);
13190     BuryPlayer(player);
13191
13192     return MP_DONT_RUN_INTO;
13193   }
13194
13195   if (player_can_move && DONT_RUN_INTO(element))
13196   {
13197     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13198
13199     return MP_DONT_RUN_INTO;
13200   }
13201
13202   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13203     return MP_NO_ACTION;
13204
13205   collect_count = element_info[element].collect_count_initial;
13206
13207   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13208     return MP_NO_ACTION;
13209
13210   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13211     player_can_move = player_can_move_or_snap;
13212
13213   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13214       game.engine_version >= VERSION_IDENT(2,2,0,0))
13215   {
13216     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13217                                player->index_bit, dig_side);
13218     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13219                                         player->index_bit, dig_side);
13220
13221     if (element == EL_DC_LANDMINE)
13222       Bang(x, y);
13223
13224     if (Feld[x][y] != element)          /* field changed by snapping */
13225       return MP_ACTION;
13226
13227     return MP_NO_ACTION;
13228   }
13229
13230   if (player->gravity && is_player && !player->is_auto_moving &&
13231       canFallDown(player) && move_direction != MV_DOWN &&
13232       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13233     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13234
13235   if (player_can_move &&
13236       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13237   {
13238     int sound_element = SND_ELEMENT(element);
13239     int sound_action = ACTION_WALKING;
13240
13241     if (IS_RND_GATE(element))
13242     {
13243       if (!player->key[RND_GATE_NR(element)])
13244         return MP_NO_ACTION;
13245     }
13246     else if (IS_RND_GATE_GRAY(element))
13247     {
13248       if (!player->key[RND_GATE_GRAY_NR(element)])
13249         return MP_NO_ACTION;
13250     }
13251     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13252     {
13253       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13254         return MP_NO_ACTION;
13255     }
13256     else if (element == EL_EXIT_OPEN ||
13257              element == EL_EM_EXIT_OPEN ||
13258              element == EL_EM_EXIT_OPENING ||
13259              element == EL_STEEL_EXIT_OPEN ||
13260              element == EL_EM_STEEL_EXIT_OPEN ||
13261              element == EL_EM_STEEL_EXIT_OPENING ||
13262              element == EL_SP_EXIT_OPEN ||
13263              element == EL_SP_EXIT_OPENING)
13264     {
13265       sound_action = ACTION_PASSING;    /* player is passing exit */
13266     }
13267     else if (element == EL_EMPTY)
13268     {
13269       sound_action = ACTION_MOVING;             /* nothing to walk on */
13270     }
13271
13272     /* play sound from background or player, whatever is available */
13273     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13274       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13275     else
13276       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13277   }
13278   else if (player_can_move &&
13279            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13280   {
13281     if (!ACCESS_FROM(element, opposite_direction))
13282       return MP_NO_ACTION;      /* field not accessible from this direction */
13283
13284     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13285       return MP_NO_ACTION;
13286
13287     if (IS_EM_GATE(element))
13288     {
13289       if (!player->key[EM_GATE_NR(element)])
13290         return MP_NO_ACTION;
13291     }
13292     else if (IS_EM_GATE_GRAY(element))
13293     {
13294       if (!player->key[EM_GATE_GRAY_NR(element)])
13295         return MP_NO_ACTION;
13296     }
13297     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13298     {
13299       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13300         return MP_NO_ACTION;
13301     }
13302     else if (IS_EMC_GATE(element))
13303     {
13304       if (!player->key[EMC_GATE_NR(element)])
13305         return MP_NO_ACTION;
13306     }
13307     else if (IS_EMC_GATE_GRAY(element))
13308     {
13309       if (!player->key[EMC_GATE_GRAY_NR(element)])
13310         return MP_NO_ACTION;
13311     }
13312     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13313     {
13314       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13315         return MP_NO_ACTION;
13316     }
13317     else if (element == EL_DC_GATE_WHITE ||
13318              element == EL_DC_GATE_WHITE_GRAY ||
13319              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13320     {
13321       if (player->num_white_keys == 0)
13322         return MP_NO_ACTION;
13323
13324       player->num_white_keys--;
13325     }
13326     else if (IS_SP_PORT(element))
13327     {
13328       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13329           element == EL_SP_GRAVITY_PORT_RIGHT ||
13330           element == EL_SP_GRAVITY_PORT_UP ||
13331           element == EL_SP_GRAVITY_PORT_DOWN)
13332         player->gravity = !player->gravity;
13333       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13334                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13335                element == EL_SP_GRAVITY_ON_PORT_UP ||
13336                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13337         player->gravity = TRUE;
13338       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13339                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13340                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13341                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13342         player->gravity = FALSE;
13343     }
13344
13345     /* automatically move to the next field with double speed */
13346     player->programmed_action = move_direction;
13347
13348     if (player->move_delay_reset_counter == 0)
13349     {
13350       player->move_delay_reset_counter = 2;     /* two double speed steps */
13351
13352       DOUBLE_PLAYER_SPEED(player);
13353     }
13354
13355     PlayLevelSoundAction(x, y, ACTION_PASSING);
13356   }
13357   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13358   {
13359     RemoveField(x, y);
13360
13361     if (mode != DF_SNAP)
13362     {
13363       GfxElement[x][y] = GFX_ELEMENT(element);
13364       player->is_digging = TRUE;
13365     }
13366
13367     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13368
13369     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13370                                         player->index_bit, dig_side);
13371
13372     if (mode == DF_SNAP)
13373     {
13374       if (level.block_snap_field)
13375         setFieldForSnapping(x, y, element, move_direction);
13376       else
13377         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13378
13379       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13380                                           player->index_bit, dig_side);
13381     }
13382   }
13383   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13384   {
13385     RemoveField(x, y);
13386
13387     if (is_player && mode != DF_SNAP)
13388     {
13389       GfxElement[x][y] = element;
13390       player->is_collecting = TRUE;
13391     }
13392
13393     if (element == EL_SPEED_PILL)
13394     {
13395       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13396     }
13397     else if (element == EL_EXTRA_TIME && level.time > 0)
13398     {
13399       TimeLeft += level.extra_time;
13400
13401       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13402
13403       DisplayGameControlValues();
13404     }
13405     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13406     {
13407       player->shield_normal_time_left += level.shield_normal_time;
13408       if (element == EL_SHIELD_DEADLY)
13409         player->shield_deadly_time_left += level.shield_deadly_time;
13410     }
13411     else if (element == EL_DYNAMITE ||
13412              element == EL_EM_DYNAMITE ||
13413              element == EL_SP_DISK_RED)
13414     {
13415       if (player->inventory_size < MAX_INVENTORY_SIZE)
13416         player->inventory_element[player->inventory_size++] = element;
13417
13418       DrawGameDoorValues();
13419     }
13420     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13421     {
13422       player->dynabomb_count++;
13423       player->dynabombs_left++;
13424     }
13425     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13426     {
13427       player->dynabomb_size++;
13428     }
13429     else if (element == EL_DYNABOMB_INCREASE_POWER)
13430     {
13431       player->dynabomb_xl = TRUE;
13432     }
13433     else if (IS_KEY(element))
13434     {
13435       player->key[KEY_NR(element)] = TRUE;
13436
13437       DrawGameDoorValues();
13438     }
13439     else if (element == EL_DC_KEY_WHITE)
13440     {
13441       player->num_white_keys++;
13442
13443       /* display white keys? */
13444       /* DrawGameDoorValues(); */
13445     }
13446     else if (IS_ENVELOPE(element))
13447     {
13448       player->show_envelope = element;
13449     }
13450     else if (element == EL_EMC_LENSES)
13451     {
13452       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13453
13454       RedrawAllInvisibleElementsForLenses();
13455     }
13456     else if (element == EL_EMC_MAGNIFIER)
13457     {
13458       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13459
13460       RedrawAllInvisibleElementsForMagnifier();
13461     }
13462     else if (IS_DROPPABLE(element) ||
13463              IS_THROWABLE(element))     /* can be collected and dropped */
13464     {
13465       int i;
13466
13467       if (collect_count == 0)
13468         player->inventory_infinite_element = element;
13469       else
13470         for (i = 0; i < collect_count; i++)
13471           if (player->inventory_size < MAX_INVENTORY_SIZE)
13472             player->inventory_element[player->inventory_size++] = element;
13473
13474       DrawGameDoorValues();
13475     }
13476     else if (collect_count > 0)
13477     {
13478       local_player->gems_still_needed -= collect_count;
13479       if (local_player->gems_still_needed < 0)
13480         local_player->gems_still_needed = 0;
13481
13482       game.snapshot.collected_item = TRUE;
13483
13484       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13485
13486       DisplayGameControlValues();
13487     }
13488
13489     RaiseScoreElement(element);
13490     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13491
13492     if (is_player)
13493       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13494                                           player->index_bit, dig_side);
13495
13496     if (mode == DF_SNAP)
13497     {
13498       if (level.block_snap_field)
13499         setFieldForSnapping(x, y, element, move_direction);
13500       else
13501         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13502
13503       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13504                                           player->index_bit, dig_side);
13505     }
13506   }
13507   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13508   {
13509     if (mode == DF_SNAP && element != EL_BD_ROCK)
13510       return MP_NO_ACTION;
13511
13512     if (CAN_FALL(element) && dy)
13513       return MP_NO_ACTION;
13514
13515     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13516         !(element == EL_SPRING && level.use_spring_bug))
13517       return MP_NO_ACTION;
13518
13519     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13520         ((move_direction & MV_VERTICAL &&
13521           ((element_info[element].move_pattern & MV_LEFT &&
13522             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13523            (element_info[element].move_pattern & MV_RIGHT &&
13524             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13525          (move_direction & MV_HORIZONTAL &&
13526           ((element_info[element].move_pattern & MV_UP &&
13527             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13528            (element_info[element].move_pattern & MV_DOWN &&
13529             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13530       return MP_NO_ACTION;
13531
13532     /* do not push elements already moving away faster than player */
13533     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13534         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13535       return MP_NO_ACTION;
13536
13537     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13538     {
13539       if (player->push_delay_value == -1 || !player_was_pushing)
13540         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13541     }
13542     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13543     {
13544       if (player->push_delay_value == -1)
13545         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13546     }
13547     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13548     {
13549       if (!player->is_pushing)
13550         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13551     }
13552
13553     player->is_pushing = TRUE;
13554     player->is_active = TRUE;
13555
13556     if (!(IN_LEV_FIELD(nextx, nexty) &&
13557           (IS_FREE(nextx, nexty) ||
13558            (IS_SB_ELEMENT(element) &&
13559             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13560            (IS_CUSTOM_ELEMENT(element) &&
13561             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13562       return MP_NO_ACTION;
13563
13564     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13565       return MP_NO_ACTION;
13566
13567     if (player->push_delay == -1)       /* new pushing; restart delay */
13568       player->push_delay = 0;
13569
13570     if (player->push_delay < player->push_delay_value &&
13571         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13572         element != EL_SPRING && element != EL_BALLOON)
13573     {
13574       /* make sure that there is no move delay before next try to push */
13575       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13576         player->move_delay = 0;
13577
13578       return MP_NO_ACTION;
13579     }
13580
13581     if (IS_CUSTOM_ELEMENT(element) &&
13582         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13583     {
13584       if (!DigFieldByCE(nextx, nexty, element))
13585         return MP_NO_ACTION;
13586     }
13587
13588     if (IS_SB_ELEMENT(element))
13589     {
13590       if (element == EL_SOKOBAN_FIELD_FULL)
13591       {
13592         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13593         local_player->sokobanfields_still_needed++;
13594       }
13595
13596       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13597       {
13598         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13599         local_player->sokobanfields_still_needed--;
13600       }
13601
13602       Feld[x][y] = EL_SOKOBAN_OBJECT;
13603
13604       if (Back[x][y] == Back[nextx][nexty])
13605         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13606       else if (Back[x][y] != 0)
13607         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13608                                     ACTION_EMPTYING);
13609       else
13610         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13611                                     ACTION_FILLING);
13612
13613       if (local_player->sokobanfields_still_needed == 0 &&
13614           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13615       {
13616         PlayerWins(player);
13617
13618         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13619       }
13620     }
13621     else
13622       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13623
13624     InitMovingField(x, y, move_direction);
13625     GfxAction[x][y] = ACTION_PUSHING;
13626
13627     if (mode == DF_SNAP)
13628       ContinueMoving(x, y);
13629     else
13630       MovPos[x][y] = (dx != 0 ? dx : dy);
13631
13632     Pushed[x][y] = TRUE;
13633     Pushed[nextx][nexty] = TRUE;
13634
13635     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13636       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13637     else
13638       player->push_delay_value = -1;    /* get new value later */
13639
13640     /* check for element change _after_ element has been pushed */
13641     if (game.use_change_when_pushing_bug)
13642     {
13643       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13644                                  player->index_bit, dig_side);
13645       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13646                                           player->index_bit, dig_side);
13647     }
13648   }
13649   else if (IS_SWITCHABLE(element))
13650   {
13651     if (PLAYER_SWITCHING(player, x, y))
13652     {
13653       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13654                                           player->index_bit, dig_side);
13655
13656       return MP_ACTION;
13657     }
13658
13659     player->is_switching = TRUE;
13660     player->switch_x = x;
13661     player->switch_y = y;
13662
13663     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13664
13665     if (element == EL_ROBOT_WHEEL)
13666     {
13667       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13668       ZX = x;
13669       ZY = y;
13670
13671       game.robot_wheel_active = TRUE;
13672
13673       TEST_DrawLevelField(x, y);
13674     }
13675     else if (element == EL_SP_TERMINAL)
13676     {
13677       int xx, yy;
13678
13679       SCAN_PLAYFIELD(xx, yy)
13680       {
13681         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13682         {
13683           Bang(xx, yy);
13684         }
13685         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13686         {
13687           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13688
13689           ResetGfxAnimation(xx, yy);
13690           TEST_DrawLevelField(xx, yy);
13691         }
13692       }
13693     }
13694     else if (IS_BELT_SWITCH(element))
13695     {
13696       ToggleBeltSwitch(x, y);
13697     }
13698     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13699              element == EL_SWITCHGATE_SWITCH_DOWN ||
13700              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13701              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13702     {
13703       ToggleSwitchgateSwitch(x, y);
13704     }
13705     else if (element == EL_LIGHT_SWITCH ||
13706              element == EL_LIGHT_SWITCH_ACTIVE)
13707     {
13708       ToggleLightSwitch(x, y);
13709     }
13710     else if (element == EL_TIMEGATE_SWITCH ||
13711              element == EL_DC_TIMEGATE_SWITCH)
13712     {
13713       ActivateTimegateSwitch(x, y);
13714     }
13715     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13716              element == EL_BALLOON_SWITCH_RIGHT ||
13717              element == EL_BALLOON_SWITCH_UP    ||
13718              element == EL_BALLOON_SWITCH_DOWN  ||
13719              element == EL_BALLOON_SWITCH_NONE  ||
13720              element == EL_BALLOON_SWITCH_ANY)
13721     {
13722       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13723                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13724                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13725                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13726                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13727                              move_direction);
13728     }
13729     else if (element == EL_LAMP)
13730     {
13731       Feld[x][y] = EL_LAMP_ACTIVE;
13732       local_player->lights_still_needed--;
13733
13734       ResetGfxAnimation(x, y);
13735       TEST_DrawLevelField(x, y);
13736     }
13737     else if (element == EL_TIME_ORB_FULL)
13738     {
13739       Feld[x][y] = EL_TIME_ORB_EMPTY;
13740
13741       if (level.time > 0 || level.use_time_orb_bug)
13742       {
13743         TimeLeft += level.time_orb_time;
13744         game.no_time_limit = FALSE;
13745
13746         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13747
13748         DisplayGameControlValues();
13749       }
13750
13751       ResetGfxAnimation(x, y);
13752       TEST_DrawLevelField(x, y);
13753     }
13754     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13755              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13756     {
13757       int xx, yy;
13758
13759       game.ball_state = !game.ball_state;
13760
13761       SCAN_PLAYFIELD(xx, yy)
13762       {
13763         int e = Feld[xx][yy];
13764
13765         if (game.ball_state)
13766         {
13767           if (e == EL_EMC_MAGIC_BALL)
13768             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13769           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13770             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13771         }
13772         else
13773         {
13774           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13775             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13776           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13777             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13778         }
13779       }
13780     }
13781
13782     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13783                                         player->index_bit, dig_side);
13784
13785     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13786                                         player->index_bit, dig_side);
13787
13788     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13789                                         player->index_bit, dig_side);
13790
13791     return MP_ACTION;
13792   }
13793   else
13794   {
13795     if (!PLAYER_SWITCHING(player, x, y))
13796     {
13797       player->is_switching = TRUE;
13798       player->switch_x = x;
13799       player->switch_y = y;
13800
13801       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13802                                  player->index_bit, dig_side);
13803       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13804                                           player->index_bit, dig_side);
13805
13806       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13807                                  player->index_bit, dig_side);
13808       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13809                                           player->index_bit, dig_side);
13810     }
13811
13812     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13813                                player->index_bit, dig_side);
13814     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13815                                         player->index_bit, dig_side);
13816
13817     return MP_NO_ACTION;
13818   }
13819
13820   player->push_delay = -1;
13821
13822   if (is_player)                /* function can also be called by EL_PENGUIN */
13823   {
13824     if (Feld[x][y] != element)          /* really digged/collected something */
13825     {
13826       player->is_collecting = !player->is_digging;
13827       player->is_active = TRUE;
13828     }
13829   }
13830
13831   return MP_MOVING;
13832 }
13833
13834 static boolean DigFieldByCE(int x, int y, int digging_element)
13835 {
13836   int element = Feld[x][y];
13837
13838   if (!IS_FREE(x, y))
13839   {
13840     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13841                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13842                   ACTION_BREAKING);
13843
13844     /* no element can dig solid indestructible elements */
13845     if (IS_INDESTRUCTIBLE(element) &&
13846         !IS_DIGGABLE(element) &&
13847         !IS_COLLECTIBLE(element))
13848       return FALSE;
13849
13850     if (AmoebaNr[x][y] &&
13851         (element == EL_AMOEBA_FULL ||
13852          element == EL_BD_AMOEBA ||
13853          element == EL_AMOEBA_GROWING))
13854     {
13855       AmoebaCnt[AmoebaNr[x][y]]--;
13856       AmoebaCnt2[AmoebaNr[x][y]]--;
13857     }
13858
13859     if (IS_MOVING(x, y))
13860       RemoveMovingField(x, y);
13861     else
13862     {
13863       RemoveField(x, y);
13864       TEST_DrawLevelField(x, y);
13865     }
13866
13867     /* if digged element was about to explode, prevent the explosion */
13868     ExplodeField[x][y] = EX_TYPE_NONE;
13869
13870     PlayLevelSoundAction(x, y, action);
13871   }
13872
13873   Store[x][y] = EL_EMPTY;
13874
13875   /* this makes it possible to leave the removed element again */
13876   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13877     Store[x][y] = element;
13878
13879   return TRUE;
13880 }
13881
13882 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13883 {
13884   int jx = player->jx, jy = player->jy;
13885   int x = jx + dx, y = jy + dy;
13886   int snap_direction = (dx == -1 ? MV_LEFT  :
13887                         dx == +1 ? MV_RIGHT :
13888                         dy == -1 ? MV_UP    :
13889                         dy == +1 ? MV_DOWN  : MV_NONE);
13890   boolean can_continue_snapping = (level.continuous_snapping &&
13891                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13892
13893   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13894     return FALSE;
13895
13896   if (!player->active || !IN_LEV_FIELD(x, y))
13897     return FALSE;
13898
13899   if (dx && dy)
13900     return FALSE;
13901
13902   if (!dx && !dy)
13903   {
13904     if (player->MovPos == 0)
13905       player->is_pushing = FALSE;
13906
13907     player->is_snapping = FALSE;
13908
13909     if (player->MovPos == 0)
13910     {
13911       player->is_moving = FALSE;
13912       player->is_digging = FALSE;
13913       player->is_collecting = FALSE;
13914     }
13915
13916     return FALSE;
13917   }
13918
13919   /* prevent snapping with already pressed snap key when not allowed */
13920   if (player->is_snapping && !can_continue_snapping)
13921     return FALSE;
13922
13923   player->MovDir = snap_direction;
13924
13925   if (player->MovPos == 0)
13926   {
13927     player->is_moving = FALSE;
13928     player->is_digging = FALSE;
13929     player->is_collecting = FALSE;
13930   }
13931
13932   player->is_dropping = FALSE;
13933   player->is_dropping_pressed = FALSE;
13934   player->drop_pressed_delay = 0;
13935
13936   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13937     return FALSE;
13938
13939   player->is_snapping = TRUE;
13940   player->is_active = TRUE;
13941
13942   if (player->MovPos == 0)
13943   {
13944     player->is_moving = FALSE;
13945     player->is_digging = FALSE;
13946     player->is_collecting = FALSE;
13947   }
13948
13949   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13950     TEST_DrawLevelField(player->last_jx, player->last_jy);
13951
13952   TEST_DrawLevelField(x, y);
13953
13954   return TRUE;
13955 }
13956
13957 static boolean DropElement(struct PlayerInfo *player)
13958 {
13959   int old_element, new_element;
13960   int dropx = player->jx, dropy = player->jy;
13961   int drop_direction = player->MovDir;
13962   int drop_side = drop_direction;
13963   int drop_element = get_next_dropped_element(player);
13964
13965   player->is_dropping_pressed = TRUE;
13966
13967   /* do not drop an element on top of another element; when holding drop key
13968      pressed without moving, dropped element must move away before the next
13969      element can be dropped (this is especially important if the next element
13970      is dynamite, which can be placed on background for historical reasons) */
13971   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13972     return MP_ACTION;
13973
13974   if (IS_THROWABLE(drop_element))
13975   {
13976     dropx += GET_DX_FROM_DIR(drop_direction);
13977     dropy += GET_DY_FROM_DIR(drop_direction);
13978
13979     if (!IN_LEV_FIELD(dropx, dropy))
13980       return FALSE;
13981   }
13982
13983   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13984   new_element = drop_element;           /* default: no change when dropping */
13985
13986   /* check if player is active, not moving and ready to drop */
13987   if (!player->active || player->MovPos || player->drop_delay > 0)
13988     return FALSE;
13989
13990   /* check if player has anything that can be dropped */
13991   if (new_element == EL_UNDEFINED)
13992     return FALSE;
13993
13994   /* check if drop key was pressed long enough for EM style dynamite */
13995   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13996     return FALSE;
13997
13998   /* check if anything can be dropped at the current position */
13999   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14000     return FALSE;
14001
14002   /* collected custom elements can only be dropped on empty fields */
14003   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14004     return FALSE;
14005
14006   if (old_element != EL_EMPTY)
14007     Back[dropx][dropy] = old_element;   /* store old element on this field */
14008
14009   ResetGfxAnimation(dropx, dropy);
14010   ResetRandomAnimationValue(dropx, dropy);
14011
14012   if (player->inventory_size > 0 ||
14013       player->inventory_infinite_element != EL_UNDEFINED)
14014   {
14015     if (player->inventory_size > 0)
14016     {
14017       player->inventory_size--;
14018
14019       DrawGameDoorValues();
14020
14021       if (new_element == EL_DYNAMITE)
14022         new_element = EL_DYNAMITE_ACTIVE;
14023       else if (new_element == EL_EM_DYNAMITE)
14024         new_element = EL_EM_DYNAMITE_ACTIVE;
14025       else if (new_element == EL_SP_DISK_RED)
14026         new_element = EL_SP_DISK_RED_ACTIVE;
14027     }
14028
14029     Feld[dropx][dropy] = new_element;
14030
14031     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14032       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14033                           el2img(Feld[dropx][dropy]), 0);
14034
14035     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14036
14037     /* needed if previous element just changed to "empty" in the last frame */
14038     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14039
14040     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14041                                player->index_bit, drop_side);
14042     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14043                                         CE_PLAYER_DROPS_X,
14044                                         player->index_bit, drop_side);
14045
14046     TestIfElementTouchesCustomElement(dropx, dropy);
14047   }
14048   else          /* player is dropping a dyna bomb */
14049   {
14050     player->dynabombs_left--;
14051
14052     Feld[dropx][dropy] = new_element;
14053
14054     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14055       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14056                           el2img(Feld[dropx][dropy]), 0);
14057
14058     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14059   }
14060
14061   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14062     InitField_WithBug1(dropx, dropy, FALSE);
14063
14064   new_element = Feld[dropx][dropy];     /* element might have changed */
14065
14066   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14067       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14068   {
14069     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14070       MovDir[dropx][dropy] = drop_direction;
14071
14072     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14073
14074     /* do not cause impact style collision by dropping elements that can fall */
14075     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14076   }
14077
14078   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14079   player->is_dropping = TRUE;
14080
14081   player->drop_pressed_delay = 0;
14082   player->is_dropping_pressed = FALSE;
14083
14084   player->drop_x = dropx;
14085   player->drop_y = dropy;
14086
14087   return TRUE;
14088 }
14089
14090 /* ------------------------------------------------------------------------- */
14091 /* game sound playing functions                                              */
14092 /* ------------------------------------------------------------------------- */
14093
14094 static int *loop_sound_frame = NULL;
14095 static int *loop_sound_volume = NULL;
14096
14097 void InitPlayLevelSound()
14098 {
14099   int num_sounds = getSoundListSize();
14100
14101   checked_free(loop_sound_frame);
14102   checked_free(loop_sound_volume);
14103
14104   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14105   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14106 }
14107
14108 static void PlayLevelSound(int x, int y, int nr)
14109 {
14110   int sx = SCREENX(x), sy = SCREENY(y);
14111   int volume, stereo_position;
14112   int max_distance = 8;
14113   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14114
14115   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14116       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14117     return;
14118
14119   if (!IN_LEV_FIELD(x, y) ||
14120       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14121       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14122     return;
14123
14124   volume = SOUND_MAX_VOLUME;
14125
14126   if (!IN_SCR_FIELD(sx, sy))
14127   {
14128     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14129     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14130
14131     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14132   }
14133
14134   stereo_position = (SOUND_MAX_LEFT +
14135                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14136                      (SCR_FIELDX + 2 * max_distance));
14137
14138   if (IS_LOOP_SOUND(nr))
14139   {
14140     /* This assures that quieter loop sounds do not overwrite louder ones,
14141        while restarting sound volume comparison with each new game frame. */
14142
14143     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14144       return;
14145
14146     loop_sound_volume[nr] = volume;
14147     loop_sound_frame[nr] = FrameCounter;
14148   }
14149
14150   PlaySoundExt(nr, volume, stereo_position, type);
14151 }
14152
14153 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14154 {
14155   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14156                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14157                  y < LEVELY(BY1) ? LEVELY(BY1) :
14158                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14159                  sound_action);
14160 }
14161
14162 static void PlayLevelSoundAction(int x, int y, int action)
14163 {
14164   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14165 }
14166
14167 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14168 {
14169   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14170
14171   if (sound_effect != SND_UNDEFINED)
14172     PlayLevelSound(x, y, sound_effect);
14173 }
14174
14175 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14176                                               int action)
14177 {
14178   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14179
14180   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14181     PlayLevelSound(x, y, sound_effect);
14182 }
14183
14184 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14185 {
14186   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14187
14188   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14189     PlayLevelSound(x, y, sound_effect);
14190 }
14191
14192 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14193 {
14194   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14195
14196   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14197     StopSound(sound_effect);
14198 }
14199
14200 static void PlayLevelMusic()
14201 {
14202   if (levelset.music[level_nr] != MUS_UNDEFINED)
14203     PlayMusic(levelset.music[level_nr]);        /* from config file */
14204   else
14205     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14206 }
14207
14208 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14209 {
14210   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14211   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14212   int x = xx - 1 - offset;
14213   int y = yy - 1 - offset;
14214
14215   switch (sample)
14216   {
14217     case SAMPLE_blank:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14219       break;
14220
14221     case SAMPLE_roll:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14223       break;
14224
14225     case SAMPLE_stone:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14227       break;
14228
14229     case SAMPLE_nut:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14231       break;
14232
14233     case SAMPLE_crack:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14235       break;
14236
14237     case SAMPLE_bug:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14239       break;
14240
14241     case SAMPLE_tank:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14243       break;
14244
14245     case SAMPLE_android_clone:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14247       break;
14248
14249     case SAMPLE_android_move:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14251       break;
14252
14253     case SAMPLE_spring:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14255       break;
14256
14257     case SAMPLE_slurp:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14259       break;
14260
14261     case SAMPLE_eater:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14263       break;
14264
14265     case SAMPLE_eater_eat:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14267       break;
14268
14269     case SAMPLE_alien:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14271       break;
14272
14273     case SAMPLE_collect:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14275       break;
14276
14277     case SAMPLE_diamond:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14279       break;
14280
14281     case SAMPLE_squash:
14282       /* !!! CHECK THIS !!! */
14283 #if 1
14284       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14285 #else
14286       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14287 #endif
14288       break;
14289
14290     case SAMPLE_wonderfall:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14292       break;
14293
14294     case SAMPLE_drip:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14296       break;
14297
14298     case SAMPLE_push:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14300       break;
14301
14302     case SAMPLE_dirt:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14304       break;
14305
14306     case SAMPLE_acid:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14308       break;
14309
14310     case SAMPLE_ball:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14312       break;
14313
14314     case SAMPLE_grow:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14316       break;
14317
14318     case SAMPLE_wonder:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14320       break;
14321
14322     case SAMPLE_door:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14324       break;
14325
14326     case SAMPLE_exit_open:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14328       break;
14329
14330     case SAMPLE_exit_leave:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14332       break;
14333
14334     case SAMPLE_dynamite:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14336       break;
14337
14338     case SAMPLE_tick:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14340       break;
14341
14342     case SAMPLE_press:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14344       break;
14345
14346     case SAMPLE_wheel:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14348       break;
14349
14350     case SAMPLE_boom:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14352       break;
14353
14354     case SAMPLE_die:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14356       break;
14357
14358     case SAMPLE_time:
14359       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14360       break;
14361
14362     default:
14363       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14364       break;
14365   }
14366 }
14367
14368 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14369 {
14370   int element = map_element_SP_to_RND(element_sp);
14371   int action = map_action_SP_to_RND(action_sp);
14372   int offset = (setup.sp_show_border_elements ? 0 : 1);
14373   int x = xx - offset;
14374   int y = yy - offset;
14375
14376   PlayLevelSoundElementAction(x, y, element, action);
14377 }
14378
14379 void RaiseScore(int value)
14380 {
14381   local_player->score += value;
14382
14383   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14384
14385   DisplayGameControlValues();
14386 }
14387
14388 void RaiseScoreElement(int element)
14389 {
14390   switch (element)
14391   {
14392     case EL_EMERALD:
14393     case EL_BD_DIAMOND:
14394     case EL_EMERALD_YELLOW:
14395     case EL_EMERALD_RED:
14396     case EL_EMERALD_PURPLE:
14397     case EL_SP_INFOTRON:
14398       RaiseScore(level.score[SC_EMERALD]);
14399       break;
14400     case EL_DIAMOND:
14401       RaiseScore(level.score[SC_DIAMOND]);
14402       break;
14403     case EL_CRYSTAL:
14404       RaiseScore(level.score[SC_CRYSTAL]);
14405       break;
14406     case EL_PEARL:
14407       RaiseScore(level.score[SC_PEARL]);
14408       break;
14409     case EL_BUG:
14410     case EL_BD_BUTTERFLY:
14411     case EL_SP_ELECTRON:
14412       RaiseScore(level.score[SC_BUG]);
14413       break;
14414     case EL_SPACESHIP:
14415     case EL_BD_FIREFLY:
14416     case EL_SP_SNIKSNAK:
14417       RaiseScore(level.score[SC_SPACESHIP]);
14418       break;
14419     case EL_YAMYAM:
14420     case EL_DARK_YAMYAM:
14421       RaiseScore(level.score[SC_YAMYAM]);
14422       break;
14423     case EL_ROBOT:
14424       RaiseScore(level.score[SC_ROBOT]);
14425       break;
14426     case EL_PACMAN:
14427       RaiseScore(level.score[SC_PACMAN]);
14428       break;
14429     case EL_NUT:
14430       RaiseScore(level.score[SC_NUT]);
14431       break;
14432     case EL_DYNAMITE:
14433     case EL_EM_DYNAMITE:
14434     case EL_SP_DISK_RED:
14435     case EL_DYNABOMB_INCREASE_NUMBER:
14436     case EL_DYNABOMB_INCREASE_SIZE:
14437     case EL_DYNABOMB_INCREASE_POWER:
14438       RaiseScore(level.score[SC_DYNAMITE]);
14439       break;
14440     case EL_SHIELD_NORMAL:
14441     case EL_SHIELD_DEADLY:
14442       RaiseScore(level.score[SC_SHIELD]);
14443       break;
14444     case EL_EXTRA_TIME:
14445       RaiseScore(level.extra_time_score);
14446       break;
14447     case EL_KEY_1:
14448     case EL_KEY_2:
14449     case EL_KEY_3:
14450     case EL_KEY_4:
14451     case EL_EM_KEY_1:
14452     case EL_EM_KEY_2:
14453     case EL_EM_KEY_3:
14454     case EL_EM_KEY_4:
14455     case EL_EMC_KEY_5:
14456     case EL_EMC_KEY_6:
14457     case EL_EMC_KEY_7:
14458     case EL_EMC_KEY_8:
14459     case EL_DC_KEY_WHITE:
14460       RaiseScore(level.score[SC_KEY]);
14461       break;
14462     default:
14463       RaiseScore(element_info[element].collect_score);
14464       break;
14465   }
14466 }
14467
14468 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14469 {
14470   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14471   {
14472     /* closing door required in case of envelope style request dialogs */
14473     if (!skip_request)
14474       CloseDoor(DOOR_CLOSE_1);
14475
14476 #if defined(NETWORK_AVALIABLE)
14477     if (options.network)
14478       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14479     else
14480 #endif
14481     {
14482       if (quick_quit)
14483         FadeSkipNextFadeIn();
14484
14485       SetGameStatus(GAME_MODE_MAIN);
14486
14487       DrawMainMenu();
14488     }
14489   }
14490   else          /* continue playing the game */
14491   {
14492     if (tape.playing && tape.deactivate_display)
14493       TapeDeactivateDisplayOff(TRUE);
14494
14495     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14496
14497     if (tape.playing && tape.deactivate_display)
14498       TapeDeactivateDisplayOn();
14499   }
14500 }
14501
14502 void RequestQuitGame(boolean ask_if_really_quit)
14503 {
14504   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14505   boolean skip_request = AllPlayersGone || quick_quit;
14506
14507   RequestQuitGameExt(skip_request, quick_quit,
14508                      "Do you really want to quit the game?");
14509 }
14510
14511
14512 /* ------------------------------------------------------------------------- */
14513 /* random generator functions                                                */
14514 /* ------------------------------------------------------------------------- */
14515
14516 unsigned int InitEngineRandom_RND(int seed)
14517 {
14518   game.num_random_calls = 0;
14519
14520   return InitEngineRandom(seed);
14521 }
14522
14523 unsigned int RND(int max)
14524 {
14525   if (max > 0)
14526   {
14527     game.num_random_calls++;
14528
14529     return GetEngineRandom(max);
14530   }
14531
14532   return 0;
14533 }
14534
14535
14536 /* ------------------------------------------------------------------------- */
14537 /* game engine snapshot handling functions                                   */
14538 /* ------------------------------------------------------------------------- */
14539
14540 struct EngineSnapshotInfo
14541 {
14542   /* runtime values for custom element collect score */
14543   int collect_score[NUM_CUSTOM_ELEMENTS];
14544
14545   /* runtime values for group element choice position */
14546   int choice_pos[NUM_GROUP_ELEMENTS];
14547
14548   /* runtime values for belt position animations */
14549   int belt_graphic[4][NUM_BELT_PARTS];
14550   int belt_anim_mode[4][NUM_BELT_PARTS];
14551 };
14552
14553 static struct EngineSnapshotInfo engine_snapshot_rnd;
14554 static char *snapshot_level_identifier = NULL;
14555 static int snapshot_level_nr = -1;
14556
14557 static void SaveEngineSnapshotValues_RND()
14558 {
14559   static int belt_base_active_element[4] =
14560   {
14561     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14562     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14563     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14564     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14565   };
14566   int i, j;
14567
14568   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14569   {
14570     int element = EL_CUSTOM_START + i;
14571
14572     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14573   }
14574
14575   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14576   {
14577     int element = EL_GROUP_START + i;
14578
14579     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14580   }
14581
14582   for (i = 0; i < 4; i++)
14583   {
14584     for (j = 0; j < NUM_BELT_PARTS; j++)
14585     {
14586       int element = belt_base_active_element[i] + j;
14587       int graphic = el2img(element);
14588       int anim_mode = graphic_info[graphic].anim_mode;
14589
14590       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14591       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14592     }
14593   }
14594 }
14595
14596 static void LoadEngineSnapshotValues_RND()
14597 {
14598   unsigned int num_random_calls = game.num_random_calls;
14599   int i, j;
14600
14601   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14602   {
14603     int element = EL_CUSTOM_START + i;
14604
14605     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14606   }
14607
14608   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14609   {
14610     int element = EL_GROUP_START + i;
14611
14612     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14613   }
14614
14615   for (i = 0; i < 4; i++)
14616   {
14617     for (j = 0; j < NUM_BELT_PARTS; j++)
14618     {
14619       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14620       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14621
14622       graphic_info[graphic].anim_mode = anim_mode;
14623     }
14624   }
14625
14626   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14627   {
14628     InitRND(tape.random_seed);
14629     for (i = 0; i < num_random_calls; i++)
14630       RND(1);
14631   }
14632
14633   if (game.num_random_calls != num_random_calls)
14634   {
14635     Error(ERR_INFO, "number of random calls out of sync");
14636     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14637     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14638     Error(ERR_EXIT, "this should not happen -- please debug");
14639   }
14640 }
14641
14642 void FreeEngineSnapshotSingle()
14643 {
14644   FreeSnapshotSingle();
14645
14646   setString(&snapshot_level_identifier, NULL);
14647   snapshot_level_nr = -1;
14648 }
14649
14650 void FreeEngineSnapshotList()
14651 {
14652   FreeSnapshotList();
14653 }
14654
14655 ListNode *SaveEngineSnapshotBuffers()
14656 {
14657   ListNode *buffers = NULL;
14658
14659   /* copy some special values to a structure better suited for the snapshot */
14660
14661   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14662     SaveEngineSnapshotValues_RND();
14663   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14664     SaveEngineSnapshotValues_EM();
14665   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14666     SaveEngineSnapshotValues_SP(&buffers);
14667
14668   /* save values stored in special snapshot structure */
14669
14670   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14671     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14672   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14673     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14674   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14675     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14676
14677   /* save further RND engine values */
14678
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14682
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14687
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14693
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14697
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14699
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14701
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14704
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14723
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14730
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14733
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14739
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14742
14743 #if 0
14744   ListNode *node = engine_snapshot_list_rnd;
14745   int num_bytes = 0;
14746
14747   while (node != NULL)
14748   {
14749     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14750
14751     node = node->next;
14752   }
14753
14754   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14755 #endif
14756
14757   return buffers;
14758 }
14759
14760 void SaveEngineSnapshotSingle()
14761 {
14762   ListNode *buffers = SaveEngineSnapshotBuffers();
14763
14764   /* finally save all snapshot buffers to single snapshot */
14765   SaveSnapshotSingle(buffers);
14766
14767   /* save level identification information */
14768   setString(&snapshot_level_identifier, leveldir_current->identifier);
14769   snapshot_level_nr = level_nr;
14770 }
14771
14772 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14773 {
14774   boolean save_snapshot =
14775     (initial_snapshot ||
14776      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14777      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14778       game.snapshot.changed_action) ||
14779      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14780       game.snapshot.collected_item));
14781
14782   game.snapshot.changed_action = FALSE;
14783   game.snapshot.collected_item = FALSE;
14784
14785   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14786       tape.quick_resume ||
14787       !save_snapshot)
14788     return FALSE;
14789
14790   ListNode *buffers = SaveEngineSnapshotBuffers();
14791
14792   /* finally save all snapshot buffers to snapshot list */
14793   SaveSnapshotToList(buffers);
14794
14795   return TRUE;
14796 }
14797
14798 boolean SaveEngineSnapshotToList()
14799 {
14800   return SaveEngineSnapshotToListExt(FALSE);
14801 }
14802
14803 void SaveEngineSnapshotToListInitial()
14804 {
14805   FreeEngineSnapshotList();
14806
14807   SaveEngineSnapshotToListExt(TRUE);
14808 }
14809
14810 void LoadEngineSnapshotValues()
14811 {
14812   /* restore special values from snapshot structure */
14813
14814   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14815     LoadEngineSnapshotValues_RND();
14816   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14817     LoadEngineSnapshotValues_EM();
14818   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14819     LoadEngineSnapshotValues_SP();
14820 }
14821
14822 void LoadEngineSnapshotSingle()
14823 {
14824   LoadSnapshotSingle();
14825
14826   LoadEngineSnapshotValues();
14827 }
14828
14829 void LoadEngineSnapshot_Undo(int steps)
14830 {
14831   LoadSnapshotFromList_Older(steps);
14832
14833   LoadEngineSnapshotValues();
14834 }
14835
14836 void LoadEngineSnapshot_Redo(int steps)
14837 {
14838   LoadSnapshotFromList_Newer(steps);
14839
14840   LoadEngineSnapshotValues();
14841 }
14842
14843 boolean CheckEngineSnapshotSingle()
14844 {
14845   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14846           snapshot_level_nr == level_nr);
14847 }
14848
14849 boolean CheckEngineSnapshotList()
14850 {
14851   return CheckSnapshotList();
14852 }
14853
14854
14855 /* ---------- new game button stuff ---------------------------------------- */
14856
14857 static struct
14858 {
14859   int graphic;
14860   struct XY *pos;
14861   int gadget_id;
14862   char *infotext;
14863 } gamebutton_info[NUM_GAME_BUTTONS] =
14864 {
14865   {
14866     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14867     GAME_CTRL_ID_STOP,                  "stop game"
14868   },
14869   {
14870     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14871     GAME_CTRL_ID_PAUSE,                 "pause game"
14872   },
14873   {
14874     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14875     GAME_CTRL_ID_PLAY,                  "play game"
14876   },
14877   {
14878     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14879     GAME_CTRL_ID_UNDO,                  "undo step"
14880   },
14881   {
14882     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14883     GAME_CTRL_ID_REDO,                  "redo step"
14884   },
14885   {
14886     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14887     GAME_CTRL_ID_SAVE,                  "save game"
14888   },
14889   {
14890     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14891     GAME_CTRL_ID_PAUSE2,                "pause game"
14892   },
14893   {
14894     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14895     GAME_CTRL_ID_LOAD,                  "load game"
14896   },
14897   {
14898     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14899     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14900   },
14901   {
14902     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14903     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14904   },
14905   {
14906     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14907     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14908   }
14909 };
14910
14911 void CreateGameButtons()
14912 {
14913   int i;
14914
14915   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14916   {
14917     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14918     struct XY *pos = gamebutton_info[i].pos;
14919     struct GadgetInfo *gi;
14920     int button_type;
14921     boolean checked;
14922     unsigned int event_mask;
14923     int base_x = (tape.show_game_buttons ? VX : DX);
14924     int base_y = (tape.show_game_buttons ? VY : DY);
14925     int gd_x   = gfx->src_x;
14926     int gd_y   = gfx->src_y;
14927     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14928     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14929     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14930     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14931     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14932     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14933     int id = i;
14934
14935     if (gfx->bitmap == NULL)
14936     {
14937       game_gadget[id] = NULL;
14938
14939       continue;
14940     }
14941
14942     if (id == GAME_CTRL_ID_STOP ||
14943         id == GAME_CTRL_ID_PLAY ||
14944         id == GAME_CTRL_ID_SAVE ||
14945         id == GAME_CTRL_ID_LOAD)
14946     {
14947       button_type = GD_TYPE_NORMAL_BUTTON;
14948       checked = FALSE;
14949       event_mask = GD_EVENT_RELEASED;
14950     }
14951     else if (id == GAME_CTRL_ID_UNDO ||
14952              id == GAME_CTRL_ID_REDO)
14953     {
14954       button_type = GD_TYPE_NORMAL_BUTTON;
14955       checked = FALSE;
14956       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14957     }
14958     else
14959     {
14960       button_type = GD_TYPE_CHECK_BUTTON;
14961       checked =
14962         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14963          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14964          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14965       event_mask = GD_EVENT_PRESSED;
14966     }
14967
14968     gi = CreateGadget(GDI_CUSTOM_ID, id,
14969                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14970                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14971                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14972                       GDI_WIDTH, gfx->width,
14973                       GDI_HEIGHT, gfx->height,
14974                       GDI_TYPE, button_type,
14975                       GDI_STATE, GD_BUTTON_UNPRESSED,
14976                       GDI_CHECKED, checked,
14977                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14978                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14979                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14980                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14981                       GDI_DIRECT_DRAW, FALSE,
14982                       GDI_EVENT_MASK, event_mask,
14983                       GDI_CALLBACK_ACTION, HandleGameButtons,
14984                       GDI_END);
14985
14986     if (gi == NULL)
14987       Error(ERR_EXIT, "cannot create gadget");
14988
14989     game_gadget[id] = gi;
14990   }
14991 }
14992
14993 void FreeGameButtons()
14994 {
14995   int i;
14996
14997   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14998     FreeGadget(game_gadget[i]);
14999 }
15000
15001 static void UnmapGameButtonsAtSamePosition(int id)
15002 {
15003   int i;
15004
15005   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15006     if (i != id &&
15007         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15008         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15009       UnmapGadget(game_gadget[i]);
15010 }
15011
15012 static void UnmapGameButtonsAtSamePosition_All()
15013 {
15014   if (setup.show_snapshot_buttons)
15015   {
15016     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15017     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15018     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15019   }
15020   else
15021   {
15022     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15023     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15024     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15025   }
15026 }
15027
15028 static void MapGameButtonsAtSamePosition(int id)
15029 {
15030   int i;
15031
15032   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15033     if (i != id &&
15034         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15035         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15036       MapGadget(game_gadget[i]);
15037
15038   UnmapGameButtonsAtSamePosition_All();
15039 }
15040
15041 void MapUndoRedoButtons()
15042 {
15043   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15044   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15045
15046   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15047   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15048
15049   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15050 }
15051
15052 void UnmapUndoRedoButtons()
15053 {
15054   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15055   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15056
15057   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15058   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15059
15060   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15061 }
15062
15063 void MapGameButtons()
15064 {
15065   int i;
15066
15067   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15068     if (i != GAME_CTRL_ID_UNDO &&
15069         i != GAME_CTRL_ID_REDO)
15070       MapGadget(game_gadget[i]);
15071
15072   UnmapGameButtonsAtSamePosition_All();
15073
15074   RedrawGameButtons();
15075 }
15076
15077 void UnmapGameButtons()
15078 {
15079   int i;
15080
15081   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15082     UnmapGadget(game_gadget[i]);
15083 }
15084
15085 void RedrawGameButtons()
15086 {
15087   int i;
15088
15089   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15090     RedrawGadget(game_gadget[i]);
15091
15092   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15093   redraw_mask &= ~REDRAW_ALL;
15094 }
15095
15096 void GameUndoRedoExt()
15097 {
15098   ClearPlayerAction();
15099
15100   tape.pausing = TRUE;
15101
15102   RedrawPlayfield();
15103   UpdateAndDisplayGameControlValues();
15104
15105   DrawCompleteVideoDisplay();
15106   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15107   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15108   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15109
15110   BackToFront();
15111 }
15112
15113 void GameUndo(int steps)
15114 {
15115   if (!CheckEngineSnapshotList())
15116     return;
15117
15118   LoadEngineSnapshot_Undo(steps);
15119
15120   GameUndoRedoExt();
15121 }
15122
15123 void GameRedo(int steps)
15124 {
15125   if (!CheckEngineSnapshotList())
15126     return;
15127
15128   LoadEngineSnapshot_Redo(steps);
15129
15130   GameUndoRedoExt();
15131 }
15132
15133 static void HandleGameButtonsExt(int id, int button)
15134 {
15135   static boolean game_undo_executed = FALSE;
15136   int steps = BUTTON_STEPSIZE(button);
15137   boolean handle_game_buttons =
15138     (game_status == GAME_MODE_PLAYING ||
15139      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15140
15141   if (!handle_game_buttons)
15142     return;
15143
15144   switch (id)
15145   {
15146     case GAME_CTRL_ID_STOP:
15147       if (game_status == GAME_MODE_MAIN)
15148         break;
15149
15150       if (tape.playing)
15151         TapeStop();
15152       else
15153         RequestQuitGame(TRUE);
15154
15155       break;
15156
15157     case GAME_CTRL_ID_PAUSE:
15158     case GAME_CTRL_ID_PAUSE2:
15159       if (options.network && game_status == GAME_MODE_PLAYING)
15160       {
15161 #if defined(NETWORK_AVALIABLE)
15162         if (tape.pausing)
15163           SendToServer_ContinuePlaying();
15164         else
15165           SendToServer_PausePlaying();
15166 #endif
15167       }
15168       else
15169         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15170
15171       game_undo_executed = FALSE;
15172
15173       break;
15174
15175     case GAME_CTRL_ID_PLAY:
15176       if (game_status == GAME_MODE_MAIN)
15177       {
15178         StartGameActions(options.network, setup.autorecord, level.random_seed);
15179       }
15180       else if (tape.pausing)
15181       {
15182 #if defined(NETWORK_AVALIABLE)
15183         if (options.network)
15184           SendToServer_ContinuePlaying();
15185         else
15186 #endif
15187           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15188       }
15189       break;
15190
15191     case GAME_CTRL_ID_UNDO:
15192       // Important: When using "save snapshot when collecting an item" mode,
15193       // load last (current) snapshot for first "undo" after pressing "pause"
15194       // (else the last-but-one snapshot would be loaded, because the snapshot
15195       // pointer already points to the last snapshot when pressing "pause",
15196       // which is fine for "every step/move" mode, but not for "every collect")
15197       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15198           !game_undo_executed)
15199         steps--;
15200
15201       game_undo_executed = TRUE;
15202
15203       GameUndo(steps);
15204       break;
15205
15206     case GAME_CTRL_ID_REDO:
15207       GameRedo(steps);
15208       break;
15209
15210     case GAME_CTRL_ID_SAVE:
15211       TapeQuickSave();
15212       break;
15213
15214     case GAME_CTRL_ID_LOAD:
15215       TapeQuickLoad();
15216       break;
15217
15218     case SOUND_CTRL_ID_MUSIC:
15219       if (setup.sound_music)
15220       { 
15221         setup.sound_music = FALSE;
15222
15223         FadeMusic();
15224       }
15225       else if (audio.music_available)
15226       { 
15227         setup.sound = setup.sound_music = TRUE;
15228
15229         SetAudioMode(setup.sound);
15230
15231         PlayLevelMusic();
15232       }
15233       break;
15234
15235     case SOUND_CTRL_ID_LOOPS:
15236       if (setup.sound_loops)
15237         setup.sound_loops = FALSE;
15238       else if (audio.loops_available)
15239       {
15240         setup.sound = setup.sound_loops = TRUE;
15241
15242         SetAudioMode(setup.sound);
15243       }
15244       break;
15245
15246     case SOUND_CTRL_ID_SIMPLE:
15247       if (setup.sound_simple)
15248         setup.sound_simple = FALSE;
15249       else if (audio.sound_available)
15250       {
15251         setup.sound = setup.sound_simple = TRUE;
15252
15253         SetAudioMode(setup.sound);
15254       }
15255       break;
15256
15257     default:
15258       break;
15259   }
15260 }
15261
15262 static void HandleGameButtons(struct GadgetInfo *gi)
15263 {
15264   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15265 }
15266
15267 void HandleSoundButtonKeys(Key key)
15268 {
15269
15270   if (key == setup.shortcut.sound_simple)
15271     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15272   else if (key == setup.shortcut.sound_loops)
15273     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15274   else if (key == setup.shortcut.sound_music)
15275     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15276 }