fixed bug with marking level as changed for certain non-level-changing gadgets
[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 scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055
1056 static void HandleGameButtons(struct GadgetInfo *);
1057
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1065
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1076
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1080
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1083
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1085
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1089
1090 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1091 {                                                                       \
1092   if (recursion_loop_detected)                                          \
1093     return (rc);                                                        \
1094                                                                         \
1095   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1096   {                                                                     \
1097     recursion_loop_detected = TRUE;                                     \
1098     recursion_loop_element = (e);                                       \
1099   }                                                                     \
1100                                                                         \
1101   recursion_loop_depth++;                                               \
1102 }
1103
1104 #define RECURSION_LOOP_DETECTION_END()                                  \
1105 {                                                                       \
1106   recursion_loop_depth--;                                               \
1107 }
1108
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1112
1113 static int map_player_action[MAX_PLAYERS];
1114
1115
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after  */
1118 /* a specified time, eventually calling a function when changing             */
1119 /* ------------------------------------------------------------------------- */
1120
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1124
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1128
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1132
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1135
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1138
1139 struct ChangingElementInfo
1140 {
1141   int element;
1142   int target_element;
1143   int change_delay;
1144   void (*pre_change_function)(int x, int y);
1145   void (*change_function)(int x, int y);
1146   void (*post_change_function)(int x, int y);
1147 };
1148
1149 static struct ChangingElementInfo change_delay_list[] =
1150 {
1151   {
1152     EL_NUT_BREAKING,
1153     EL_EMERALD,
1154     6,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_PEARL_BREAKING,
1161     EL_EMPTY,
1162     8,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_OPENING,
1169     EL_EXIT_OPEN,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_EXIT_CLOSING,
1177     EL_EXIT_CLOSED,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_OPENING,
1185     EL_STEEL_EXIT_OPEN,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_STEEL_EXIT_CLOSING,
1193     EL_STEEL_EXIT_CLOSED,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_OPENING,
1201     EL_EM_EXIT_OPEN,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_EXIT_CLOSING,
1209     EL_EMPTY,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_OPENING,
1217     EL_EM_STEEL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EM_STEEL_EXIT_CLOSING,
1225     EL_STEELWALL,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_OPENING,
1233     EL_SP_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SP_EXIT_CLOSING,
1241     EL_SP_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_OPENING,
1249     EL_SWITCHGATE_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_SWITCHGATE_CLOSING,
1257     EL_SWITCHGATE_CLOSED,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_OPENING,
1265     EL_TIMEGATE_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_TIMEGATE_CLOSING,
1273     EL_TIMEGATE_CLOSED,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279
1280   {
1281     EL_ACID_SPLASH_LEFT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_ACID_SPLASH_RIGHT,
1290     EL_EMPTY,
1291     8,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE,
1298     EL_SP_BUGGY_BASE_ACTIVATING,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVATING,
1306     EL_SP_BUGGY_BASE_ACTIVE,
1307     0,
1308     InitBuggyBase,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_BUGGY_BASE_ACTIVE,
1314     EL_SP_BUGGY_BASE,
1315     0,
1316     InitBuggyBase,
1317     WarnBuggyBase,
1318     NULL
1319   },
1320   {
1321     EL_TRAP,
1322     EL_TRAP_ACTIVE,
1323     0,
1324     InitTrap,
1325     NULL,
1326     ActivateTrap
1327   },
1328   {
1329     EL_TRAP_ACTIVE,
1330     EL_TRAP,
1331     31,
1332     NULL,
1333     ChangeActiveTrap,
1334     NULL
1335   },
1336   {
1337     EL_ROBOT_WHEEL_ACTIVE,
1338     EL_ROBOT_WHEEL,
1339     0,
1340     InitRobotWheel,
1341     RunRobotWheel,
1342     StopRobotWheel
1343   },
1344   {
1345     EL_TIMEGATE_SWITCH_ACTIVE,
1346     EL_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354     EL_DC_TIMEGATE_SWITCH,
1355     0,
1356     InitTimegateWheel,
1357     RunTimegateWheel,
1358     NULL
1359   },
1360   {
1361     EL_EMC_MAGIC_BALL_ACTIVE,
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     0,
1364     InitMagicBallDelay,
1365     NULL,
1366     ActivateMagicBall
1367   },
1368   {
1369     EL_EMC_SPRING_BUMPER_ACTIVE,
1370     EL_EMC_SPRING_BUMPER,
1371     8,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_SHRINKING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_DIAGONAL_GROWING,
1386     EL_UNDEFINED,
1387     0,
1388     NULL,
1389     NULL,
1390     NULL,
1391   },
1392
1393   {
1394     EL_UNDEFINED,
1395     EL_UNDEFINED,
1396     -1,
1397     NULL,
1398     NULL,
1399     NULL
1400   }
1401 };
1402
1403 struct
1404 {
1405   int element;
1406   int push_delay_fixed, push_delay_random;
1407 }
1408 push_delay_list[] =
1409 {
1410   { EL_SPRING,                  0, 0 },
1411   { EL_BALLOON,                 0, 0 },
1412
1413   { EL_SOKOBAN_OBJECT,          2, 0 },
1414   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1415   { EL_SATELLITE,               2, 0 },
1416   { EL_SP_DISK_YELLOW,          2, 0 },
1417
1418   { EL_UNDEFINED,               0, 0 },
1419 };
1420
1421 struct
1422 {
1423   int element;
1424   int move_stepsize;
1425 }
1426 move_stepsize_list[] =
1427 {
1428   { EL_AMOEBA_DROP,             2 },
1429   { EL_AMOEBA_DROPPING,         2 },
1430   { EL_QUICKSAND_FILLING,       1 },
1431   { EL_QUICKSAND_EMPTYING,      1 },
1432   { EL_QUICKSAND_FAST_FILLING,  2 },
1433   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434   { EL_MAGIC_WALL_FILLING,      2 },
1435   { EL_MAGIC_WALL_EMPTYING,     2 },
1436   { EL_BD_MAGIC_WALL_FILLING,   2 },
1437   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1438   { EL_DC_MAGIC_WALL_FILLING,   2 },
1439   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1440
1441   { EL_UNDEFINED,               0 },
1442 };
1443
1444 struct
1445 {
1446   int element;
1447   int count;
1448 }
1449 collect_count_list[] =
1450 {
1451   { EL_EMERALD,                 1 },
1452   { EL_BD_DIAMOND,              1 },
1453   { EL_EMERALD_YELLOW,          1 },
1454   { EL_EMERALD_RED,             1 },
1455   { EL_EMERALD_PURPLE,          1 },
1456   { EL_DIAMOND,                 3 },
1457   { EL_SP_INFOTRON,             1 },
1458   { EL_PEARL,                   5 },
1459   { EL_CRYSTAL,                 8 },
1460
1461   { EL_UNDEFINED,               0 },
1462 };
1463
1464 struct
1465 {
1466   int element;
1467   int direction;
1468 }
1469 access_direction_list[] =
1470 {
1471   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1473   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1474   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1475   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1476   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1477   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1478   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1479   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1480   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1481   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1482
1483   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1484   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1485   { EL_SP_PORT_UP,                                                   MV_DOWN },
1486   { EL_SP_PORT_DOWN,                                         MV_UP           },
1487   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1488   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1489   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1491   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1492   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1493   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1494   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1495   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1496   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1497   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1498   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1499   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1500   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1501   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1502
1503   { EL_UNDEFINED,                       MV_NONE                              }
1504 };
1505
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1507
1508 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1511                                  IS_JUST_CHANGING(x, y))
1512
1513 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1514
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1520
1521 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1522                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1523                                      (y) += playfield_scan_delta_y)     \
1524                                 for ((x) = playfield_scan_start_x;      \
1525                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1526                                      (x) += playfield_scan_delta_x)
1527
1528 #ifdef DEBUG
1529 void DEBUG_SetMaximumDynamite()
1530 {
1531   int i;
1532
1533   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535       local_player->inventory_element[local_player->inventory_size++] =
1536         EL_DYNAMITE;
1537 }
1538 #endif
1539
1540 static void InitPlayfieldScanModeVars()
1541 {
1542   if (game.use_reverse_scan_direction)
1543   {
1544     playfield_scan_start_x = lev_fieldx - 1;
1545     playfield_scan_start_y = lev_fieldy - 1;
1546
1547     playfield_scan_delta_x = -1;
1548     playfield_scan_delta_y = -1;
1549   }
1550   else
1551   {
1552     playfield_scan_start_x = 0;
1553     playfield_scan_start_y = 0;
1554
1555     playfield_scan_delta_x = 1;
1556     playfield_scan_delta_y = 1;
1557   }
1558 }
1559
1560 static void InitPlayfieldScanMode(int mode)
1561 {
1562   game.use_reverse_scan_direction =
1563     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1564
1565   InitPlayfieldScanModeVars();
1566 }
1567
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1569 {
1570   move_stepsize =
1571     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1572
1573   /* make sure that stepsize value is always a power of 2 */
1574   move_stepsize = (1 << log_2(move_stepsize));
1575
1576   return TILEX / move_stepsize;
1577 }
1578
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1580                                boolean init_game)
1581 {
1582   int player_nr = player->index_nr;
1583   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1585
1586   /* do no immediately change move delay -- the player might just be moving */
1587   player->move_delay_value_next = move_delay;
1588
1589   /* information if player can move must be set separately */
1590   player->cannot_move = cannot_move;
1591
1592   if (init_game)
1593   {
1594     player->move_delay       = game.initial_move_delay[player_nr];
1595     player->move_delay_value = game.initial_move_delay_value[player_nr];
1596
1597     player->move_delay_value_next = -1;
1598
1599     player->move_delay_reset_counter = 0;
1600   }
1601 }
1602
1603 void GetPlayerConfig()
1604 {
1605   GameFrameDelay = setup.game_frame_delay;
1606
1607   if (!audio.sound_available)
1608     setup.sound_simple = FALSE;
1609
1610   if (!audio.loops_available)
1611     setup.sound_loops = FALSE;
1612
1613   if (!audio.music_available)
1614     setup.sound_music = FALSE;
1615
1616   if (!video.fullscreen_available)
1617     setup.fullscreen = FALSE;
1618
1619   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1620
1621   SetAudioMode(setup.sound);
1622   InitJoysticks();
1623 }
1624
1625 int GetElementFromGroupElement(int element)
1626 {
1627   if (IS_GROUP_ELEMENT(element))
1628   {
1629     struct ElementGroupInfo *group = element_info[element].group;
1630     int last_anim_random_frame = gfx.anim_random_frame;
1631     int element_pos;
1632
1633     if (group->choice_mode == ANIM_RANDOM)
1634       gfx.anim_random_frame = RND(group->num_elements_resolved);
1635
1636     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1637                                     group->choice_mode, 0,
1638                                     group->choice_pos);
1639
1640     if (group->choice_mode == ANIM_RANDOM)
1641       gfx.anim_random_frame = last_anim_random_frame;
1642
1643     group->choice_pos++;
1644
1645     element = group->element_resolved[element_pos];
1646   }
1647
1648   return element;
1649 }
1650
1651 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 {
1653   if (element == EL_SP_MURPHY)
1654   {
1655     if (init_game)
1656     {
1657       if (stored_player[0].present)
1658       {
1659         Feld[x][y] = EL_SP_MURPHY_CLONE;
1660
1661         return;
1662       }
1663       else
1664       {
1665         stored_player[0].initial_element = element;
1666         stored_player[0].use_murphy = TRUE;
1667
1668         if (!level.use_artwork_element[0])
1669           stored_player[0].artwork_element = EL_SP_MURPHY;
1670       }
1671
1672       Feld[x][y] = EL_PLAYER_1;
1673     }
1674   }
1675
1676   if (init_game)
1677   {
1678     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1679     int jx = player->jx, jy = player->jy;
1680
1681     player->present = TRUE;
1682
1683     player->block_last_field = (element == EL_SP_MURPHY ?
1684                                 level.sp_block_last_field :
1685                                 level.block_last_field);
1686
1687     /* ---------- initialize player's last field block delay --------------- */
1688
1689     /* always start with reliable default value (no adjustment needed) */
1690     player->block_delay_adjustment = 0;
1691
1692     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1693     if (player->block_last_field && element == EL_SP_MURPHY)
1694       player->block_delay_adjustment = 1;
1695
1696     /* special case 2: in game engines before 3.1.1, blocking was different */
1697     if (game.use_block_last_field_bug)
1698       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699
1700     if (!options.network || player->connected)
1701     {
1702       player->active = TRUE;
1703
1704       /* remove potentially duplicate players */
1705       if (StorePlayer[jx][jy] == Feld[x][y])
1706         StorePlayer[jx][jy] = 0;
1707
1708       StorePlayer[x][y] = Feld[x][y];
1709
1710 #if DEBUG_INIT_PLAYER
1711       if (options.debug)
1712       {
1713         printf("- player element %d activated", player->element_nr);
1714         printf(" (local player is %d and currently %s)\n",
1715                local_player->element_nr,
1716                local_player->active ? "active" : "not active");
1717       }
1718     }
1719 #endif
1720
1721     Feld[x][y] = EL_EMPTY;
1722
1723     player->jx = player->last_jx = x;
1724     player->jy = player->last_jy = y;
1725   }
1726
1727   if (!init_game)
1728   {
1729     int player_nr = GET_PLAYER_NR(element);
1730     struct PlayerInfo *player = &stored_player[player_nr];
1731
1732     if (player->active && player->killed)
1733       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1734   }
1735 }
1736
1737 static void InitField(int x, int y, boolean init_game)
1738 {
1739   int element = Feld[x][y];
1740
1741   switch (element)
1742   {
1743     case EL_SP_MURPHY:
1744     case EL_PLAYER_1:
1745     case EL_PLAYER_2:
1746     case EL_PLAYER_3:
1747     case EL_PLAYER_4:
1748       InitPlayerField(x, y, element, init_game);
1749       break;
1750
1751     case EL_SOKOBAN_FIELD_PLAYER:
1752       element = Feld[x][y] = EL_PLAYER_1;
1753       InitField(x, y, init_game);
1754
1755       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1756       InitField(x, y, init_game);
1757       break;
1758
1759     case EL_SOKOBAN_FIELD_EMPTY:
1760       local_player->sokobanfields_still_needed++;
1761       break;
1762
1763     case EL_STONEBLOCK:
1764       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1765         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1766       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1767         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1768       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1769         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1770       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1771         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1772       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1773         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774       break;
1775
1776     case EL_BUG:
1777     case EL_BUG_RIGHT:
1778     case EL_BUG_UP:
1779     case EL_BUG_LEFT:
1780     case EL_BUG_DOWN:
1781     case EL_SPACESHIP:
1782     case EL_SPACESHIP_RIGHT:
1783     case EL_SPACESHIP_UP:
1784     case EL_SPACESHIP_LEFT:
1785     case EL_SPACESHIP_DOWN:
1786     case EL_BD_BUTTERFLY:
1787     case EL_BD_BUTTERFLY_RIGHT:
1788     case EL_BD_BUTTERFLY_UP:
1789     case EL_BD_BUTTERFLY_LEFT:
1790     case EL_BD_BUTTERFLY_DOWN:
1791     case EL_BD_FIREFLY:
1792     case EL_BD_FIREFLY_RIGHT:
1793     case EL_BD_FIREFLY_UP:
1794     case EL_BD_FIREFLY_LEFT:
1795     case EL_BD_FIREFLY_DOWN:
1796     case EL_PACMAN_RIGHT:
1797     case EL_PACMAN_UP:
1798     case EL_PACMAN_LEFT:
1799     case EL_PACMAN_DOWN:
1800     case EL_YAMYAM:
1801     case EL_YAMYAM_LEFT:
1802     case EL_YAMYAM_RIGHT:
1803     case EL_YAMYAM_UP:
1804     case EL_YAMYAM_DOWN:
1805     case EL_DARK_YAMYAM:
1806     case EL_ROBOT:
1807     case EL_PACMAN:
1808     case EL_SP_SNIKSNAK:
1809     case EL_SP_ELECTRON:
1810     case EL_MOLE:
1811     case EL_MOLE_LEFT:
1812     case EL_MOLE_RIGHT:
1813     case EL_MOLE_UP:
1814     case EL_MOLE_DOWN:
1815       InitMovDir(x, y);
1816       break;
1817
1818     case EL_AMOEBA_FULL:
1819     case EL_BD_AMOEBA:
1820       InitAmoebaNr(x, y);
1821       break;
1822
1823     case EL_AMOEBA_DROP:
1824       if (y == lev_fieldy - 1)
1825       {
1826         Feld[x][y] = EL_AMOEBA_GROWING;
1827         Store[x][y] = EL_AMOEBA_WET;
1828       }
1829       break;
1830
1831     case EL_DYNAMITE_ACTIVE:
1832     case EL_SP_DISK_RED_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1836     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1837       MovDelay[x][y] = 96;
1838       break;
1839
1840     case EL_EM_DYNAMITE_ACTIVE:
1841       MovDelay[x][y] = 32;
1842       break;
1843
1844     case EL_LAMP:
1845       local_player->lights_still_needed++;
1846       break;
1847
1848     case EL_PENGUIN:
1849       local_player->friends_still_needed++;
1850       break;
1851
1852     case EL_PIG:
1853     case EL_DRAGON:
1854       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1855       break;
1856
1857     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1861     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1862     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1864     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1865     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1867     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1868     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1869       if (init_game)
1870       {
1871         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1873         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874
1875         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1876         {
1877           game.belt_dir[belt_nr] = belt_dir;
1878           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879         }
1880         else    /* more than one switch -- set it like the first switch */
1881         {
1882           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1883         }
1884       }
1885       break;
1886
1887     case EL_LIGHT_SWITCH_ACTIVE:
1888       if (init_game)
1889         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1890       break;
1891
1892     case EL_INVISIBLE_STEELWALL:
1893     case EL_INVISIBLE_WALL:
1894     case EL_INVISIBLE_SAND:
1895       if (game.light_time_left > 0 ||
1896           game.lenses_time_left > 0)
1897         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1898       break;
1899
1900     case EL_EMC_MAGIC_BALL:
1901       if (game.ball_state)
1902         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1903       break;
1904
1905     case EL_EMC_MAGIC_BALL_SWITCH:
1906       if (game.ball_state)
1907         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1908       break;
1909
1910     case EL_TRIGGER_PLAYER:
1911     case EL_TRIGGER_ELEMENT:
1912     case EL_TRIGGER_CE_VALUE:
1913     case EL_TRIGGER_CE_SCORE:
1914     case EL_SELF:
1915     case EL_ANY_ELEMENT:
1916     case EL_CURRENT_CE_VALUE:
1917     case EL_CURRENT_CE_SCORE:
1918     case EL_PREV_CE_1:
1919     case EL_PREV_CE_2:
1920     case EL_PREV_CE_3:
1921     case EL_PREV_CE_4:
1922     case EL_PREV_CE_5:
1923     case EL_PREV_CE_6:
1924     case EL_PREV_CE_7:
1925     case EL_PREV_CE_8:
1926     case EL_NEXT_CE_1:
1927     case EL_NEXT_CE_2:
1928     case EL_NEXT_CE_3:
1929     case EL_NEXT_CE_4:
1930     case EL_NEXT_CE_5:
1931     case EL_NEXT_CE_6:
1932     case EL_NEXT_CE_7:
1933     case EL_NEXT_CE_8:
1934       /* reference elements should not be used on the playfield */
1935       Feld[x][y] = EL_EMPTY;
1936       break;
1937
1938     default:
1939       if (IS_CUSTOM_ELEMENT(element))
1940       {
1941         if (CAN_MOVE(element))
1942           InitMovDir(x, y);
1943
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946       }
1947       else if (IS_GROUP_ELEMENT(element))
1948       {
1949         Feld[x][y] = GetElementFromGroupElement(element);
1950
1951         InitField(x, y, init_game);
1952       }
1953
1954       break;
1955   }
1956
1957   if (!init_game)
1958     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1959 }
1960
1961 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 {
1963   InitField(x, y, init_game);
1964
1965   /* not needed to call InitMovDir() -- already done by InitField()! */
1966   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1967       CAN_MOVE(Feld[x][y]))
1968     InitMovDir(x, y);
1969 }
1970
1971 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 {
1973   int old_element = Feld[x][y];
1974
1975   InitField(x, y, init_game);
1976
1977   /* not needed to call InitMovDir() -- already done by InitField()! */
1978   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1979       CAN_MOVE(old_element) &&
1980       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1981     InitMovDir(x, y);
1982
1983   /* this case is in fact a combination of not less than three bugs:
1984      first, it calls InitMovDir() for elements that can move, although this is
1985      already done by InitField(); then, it checks the element that was at this
1986      field _before_ the call to InitField() (which can change it); lastly, it
1987      was not called for "mole with direction" elements, which were treated as
1988      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1989   */
1990 }
1991
1992 static int get_key_element_from_nr(int key_nr)
1993 {
1994   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1995                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1996                           EL_EM_KEY_1 : EL_KEY_1);
1997
1998   return key_base_element + key_nr;
1999 }
2000
2001 static int get_next_dropped_element(struct PlayerInfo *player)
2002 {
2003   return (player->inventory_size > 0 ?
2004           player->inventory_element[player->inventory_size - 1] :
2005           player->inventory_infinite_element != EL_UNDEFINED ?
2006           player->inventory_infinite_element :
2007           player->dynabombs_left > 0 ?
2008           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2009           EL_UNDEFINED);
2010 }
2011
2012 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 {
2014   /* pos >= 0: get element from bottom of the stack;
2015      pos <  0: get element from top of the stack */
2016
2017   if (pos < 0)
2018   {
2019     int min_inventory_size = -pos;
2020     int inventory_pos = player->inventory_size - min_inventory_size;
2021     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022
2023     return (player->inventory_size >= min_inventory_size ?
2024             player->inventory_element[inventory_pos] :
2025             player->inventory_infinite_element != EL_UNDEFINED ?
2026             player->inventory_infinite_element :
2027             player->dynabombs_left >= min_dynabombs_left ?
2028             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2029             EL_UNDEFINED);
2030   }
2031   else
2032   {
2033     int min_dynabombs_left = pos + 1;
2034     int min_inventory_size = pos + 1 - player->dynabombs_left;
2035     int inventory_pos = pos - player->dynabombs_left;
2036
2037     return (player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             player->inventory_size >= min_inventory_size ?
2042             player->inventory_element[inventory_pos] :
2043             EL_UNDEFINED);
2044   }
2045 }
2046
2047 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 {
2049   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2050   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2051   int compare_result;
2052
2053   if (gpo1->sort_priority != gpo2->sort_priority)
2054     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055   else
2056     compare_result = gpo1->nr - gpo2->nr;
2057
2058   return compare_result;
2059 }
2060
2061 int getPlayerInventorySize(int player_nr)
2062 {
2063   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2064     return level.native_em_level->ply[player_nr]->dynamite;
2065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2066     return level.native_sp_level->game_sp->red_disk_count;
2067   else
2068     return stored_player[player_nr].inventory_size;
2069 }
2070
2071 void InitGameControlValues()
2072 {
2073   int i;
2074
2075   for (i = 0; game_panel_controls[i].nr != -1; i++)
2076   {
2077     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2078     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2079     struct TextPosInfo *pos = gpc->pos;
2080     int nr = gpc->nr;
2081     int type = gpc->type;
2082
2083     if (nr != i)
2084     {
2085       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2086       Error(ERR_EXIT, "this should not happen -- please debug");
2087     }
2088
2089     /* force update of game controls after initialization */
2090     gpc->value = gpc->last_value = -1;
2091     gpc->frame = gpc->last_frame = -1;
2092     gpc->gfx_frame = -1;
2093
2094     /* determine panel value width for later calculation of alignment */
2095     if (type == TYPE_INTEGER || type == TYPE_STRING)
2096     {
2097       pos->width = pos->size * getFontWidth(pos->font);
2098       pos->height = getFontHeight(pos->font);
2099     }
2100     else if (type == TYPE_ELEMENT)
2101     {
2102       pos->width = pos->size;
2103       pos->height = pos->size;
2104     }
2105
2106     /* fill structure for game panel draw order */
2107     gpo->nr = gpc->nr;
2108     gpo->sort_priority = pos->sort_priority;
2109   }
2110
2111   /* sort game panel controls according to sort_priority and control number */
2112   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2113         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2114 }
2115
2116 void UpdatePlayfieldElementCount()
2117 {
2118   boolean use_element_count = FALSE;
2119   int i, j, x, y;
2120
2121   /* first check if it is needed at all to calculate playfield element count */
2122   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2123     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2124       use_element_count = TRUE;
2125
2126   if (!use_element_count)
2127     return;
2128
2129   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130     element_info[i].element_count = 0;
2131
2132   SCAN_PLAYFIELD(x, y)
2133   {
2134     element_info[Feld[x][y]].element_count++;
2135   }
2136
2137   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2138     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2139       if (IS_IN_GROUP(j, i))
2140         element_info[EL_GROUP_START + i].element_count +=
2141           element_info[j].element_count;
2142 }
2143
2144 void UpdateGameControlValues()
2145 {
2146   int i, k;
2147   int time = (local_player->LevelSolved ?
2148               local_player->LevelSolved_CountingTime :
2149               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2150               level.native_em_level->lev->time :
2151               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2152               level.native_sp_level->game_sp->time_played :
2153               game.no_time_limit ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2159                level.native_sp_level->game_sp->score :
2160                local_player->score);
2161   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2162               level.native_em_level->lev->required :
2163               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2164               level.native_sp_level->game_sp->infotrons_still_needed :
2165               local_player->gems_still_needed);
2166   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2167                      level.native_em_level->lev->required > 0 :
2168                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2169                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2170                      local_player->gems_still_needed > 0 ||
2171                      local_player->sokobanfields_still_needed > 0 ||
2172                      local_player->lights_still_needed > 0);
2173
2174   UpdatePlayfieldElementCount();
2175
2176   /* update game panel control values */
2177
2178   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2179   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180
2181   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2182   for (i = 0; i < MAX_NUM_KEYS; i++)
2183     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2185   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186
2187   if (game.centered_player_nr == -1)
2188   {
2189     for (i = 0; i < MAX_PLAYERS; i++)
2190     {
2191       /* only one player in Supaplex game engine */
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2193         break;
2194
2195       for (k = 0; k < MAX_NUM_KEYS; k++)
2196       {
2197         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198         {
2199           if (level.native_em_level->ply[i]->keys & (1 << k))
2200             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2201               get_key_element_from_nr(k);
2202         }
2203         else if (stored_player[i].key[k])
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207
2208       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2209         getPlayerInventorySize(i);
2210
2211       if (stored_player[i].num_white_keys > 0)
2212         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2213           EL_DC_KEY_WHITE;
2214
2215       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2216         stored_player[i].num_white_keys;
2217     }
2218   }
2219   else
2220   {
2221     int player_nr = game.centered_player_nr;
2222
2223     for (k = 0; k < MAX_NUM_KEYS; k++)
2224     {
2225       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226       {
2227         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2228           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2229             get_key_element_from_nr(k);
2230       }
2231       else if (stored_player[player_nr].key[k])
2232         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2233           get_key_element_from_nr(k);
2234     }
2235
2236     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2237       getPlayerInventorySize(player_nr);
2238
2239     if (stored_player[player_nr].num_white_keys > 0)
2240       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241
2242     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2243       stored_player[player_nr].num_white_keys;
2244   }
2245
2246   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247   {
2248     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, i);
2250     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2251       get_inventory_element_from_pos(local_player, -i - 1);
2252   }
2253
2254   game_panel_controls[GAME_PANEL_SCORE].value = score;
2255   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256
2257   game_panel_controls[GAME_PANEL_TIME].value = time;
2258
2259   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2260   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2261   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262
2263   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264
2265   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2266     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2269     local_player->shield_normal_time_left;
2270   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2271     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272      EL_EMPTY);
2273   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2274     local_player->shield_deadly_time_left;
2275
2276   game_panel_controls[GAME_PANEL_EXIT].value =
2277     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278
2279   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2280     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2281   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2282     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2283      EL_EMC_MAGIC_BALL_SWITCH);
2284
2285   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2286     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2287   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2288     game.light_time_left;
2289
2290   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2291     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2292   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2293     game.timegate_time_left;
2294
2295   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2296     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297
2298   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2299     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2301     game.lenses_time_left;
2302
2303   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2304     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2305   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2306     game.magnify_time_left;
2307
2308   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2309     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2310      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2311      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2312      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2313      EL_BALLOON_SWITCH_NONE);
2314
2315   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2316     local_player->dynabomb_count;
2317   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2318     local_player->dynabomb_size;
2319   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2320     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321
2322   game_panel_controls[GAME_PANEL_PENGUINS].value =
2323     local_player->friends_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2326     local_player->sokobanfields_still_needed;
2327   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2328     local_player->sokobanfields_still_needed;
2329
2330   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2331     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332
2333   for (i = 0; i < NUM_BELTS; i++)
2334   {
2335     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2336       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2337        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2338     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2339       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2340   }
2341
2342   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2343     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2344   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2345     game.magic_wall_time_left;
2346
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2348     local_player->gravity;
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387       {
2388         int last_anim_random_frame = gfx.anim_random_frame;
2389         int element = gpc->value;
2390         int graphic = el2panelimg(element);
2391
2392         if (gpc->value != gpc->last_value)
2393         {
2394           gpc->gfx_frame = 0;
2395           gpc->gfx_random = INIT_GFX_RANDOM();
2396         }
2397         else
2398         {
2399           gpc->gfx_frame++;
2400
2401           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2402               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2403             gpc->gfx_random = INIT_GFX_RANDOM();
2404         }
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = gpc->gfx_random;
2408
2409         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2410           gpc->gfx_frame = element_info[element].collect_score;
2411
2412         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2413                                               gpc->gfx_frame);
2414
2415         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2416           gfx.anim_random_frame = last_anim_random_frame;
2417       }
2418     }
2419   }
2420 }
2421
2422 void DisplayGameControlValues()
2423 {
2424   boolean redraw_panel = FALSE;
2425   int i;
2426
2427   for (i = 0; game_panel_controls[i].nr != -1; i++)
2428   {
2429     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430
2431     if (PANEL_DEACTIVATED(gpc->pos))
2432       continue;
2433
2434     if (gpc->value == gpc->last_value &&
2435         gpc->frame == gpc->last_frame)
2436       continue;
2437
2438     redraw_panel = TRUE;
2439   }
2440
2441   if (!redraw_panel)
2442     return;
2443
2444   /* copy default game door content to main double buffer */
2445
2446   /* !!! CHECK AGAIN !!! */
2447   SetPanelBackground();
2448   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2449   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450
2451   /* redraw game control buttons */
2452   RedrawGameButtons();
2453
2454   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457   {
2458     int nr = game_panel_order[i].nr;
2459     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2460     struct TextPosInfo *pos = gpc->pos;
2461     int type = gpc->type;
2462     int value = gpc->value;
2463     int frame = gpc->frame;
2464     int size = pos->size;
2465     int font = pos->font;
2466     boolean draw_masked = pos->draw_masked;
2467     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468
2469     if (PANEL_DEACTIVATED(pos))
2470       continue;
2471
2472     gpc->last_value = value;
2473     gpc->last_frame = frame;
2474
2475     if (type == TYPE_INTEGER)
2476     {
2477       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2478           nr == GAME_PANEL_TIME)
2479       {
2480         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481
2482         if (use_dynamic_size)           /* use dynamic number of digits */
2483         {
2484           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2485           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2486           int size2 = size1 + 1;
2487           int font1 = pos->font;
2488           int font2 = pos->font_alt;
2489
2490           size = (value < value_change ? size1 : size2);
2491           font = (value < value_change ? font1 : font2);
2492         }
2493       }
2494
2495       /* correct text size if "digits" is zero or less */
2496       if (size <= 0)
2497         size = strlen(int2str(value, size));
2498
2499       /* dynamically correct text alignment */
2500       pos->width = size * getFontWidth(font);
2501
2502       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2503                   int2str(value, size), font, mask_mode);
2504     }
2505     else if (type == TYPE_ELEMENT)
2506     {
2507       int element, graphic;
2508       Bitmap *src_bitmap;
2509       int src_x, src_y;
2510       int width, height;
2511       int dst_x = PANEL_XPOS(pos);
2512       int dst_y = PANEL_YPOS(pos);
2513
2514       if (value != EL_UNDEFINED && value != EL_EMPTY)
2515       {
2516         element = value;
2517         graphic = el2panelimg(value);
2518
2519         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520
2521         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2522           size = TILESIZE;
2523
2524         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2525                               &src_x, &src_y);
2526
2527         width  = graphic_info[graphic].width  * size / TILESIZE;
2528         height = graphic_info[graphic].height * size / TILESIZE;
2529
2530         if (draw_masked)
2531           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2532                            dst_x, dst_y);
2533         else
2534           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2535                      dst_x, dst_y);
2536       }
2537     }
2538     else if (type == TYPE_STRING)
2539     {
2540       boolean active = (value != 0);
2541       char *state_normal = "off";
2542       char *state_active = "on";
2543       char *state = (active ? state_active : state_normal);
2544       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2545                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2546                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2547                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2548
2549       if (nr == GAME_PANEL_GRAVITY_STATE)
2550       {
2551         int font1 = pos->font;          /* (used for normal state) */
2552         int font2 = pos->font_alt;      /* (used for active state) */
2553
2554         font = (active ? font2 : font1);
2555       }
2556
2557       if (s != NULL)
2558       {
2559         char *s_cut;
2560
2561         if (size <= 0)
2562         {
2563           /* don't truncate output if "chars" is zero or less */
2564           size = strlen(s);
2565
2566           /* dynamically correct text alignment */
2567           pos->width = size * getFontWidth(font);
2568         }
2569
2570         s_cut = getStringCopyN(s, size);
2571
2572         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2573                     s_cut, font, mask_mode);
2574
2575         free(s_cut);
2576       }
2577     }
2578
2579     redraw_mask |= REDRAW_DOOR_1;
2580   }
2581
2582   SetGameStatus(GAME_MODE_PLAYING);
2583 }
2584
2585 void UpdateAndDisplayGameControlValues()
2586 {
2587   if (tape.deactivate_display)
2588     return;
2589
2590   UpdateGameControlValues();
2591   DisplayGameControlValues();
2592 }
2593
2594 void UpdateGameDoorValues()
2595 {
2596   UpdateGameControlValues();
2597 }
2598
2599 void DrawGameDoorValues()
2600 {
2601   DisplayGameControlValues();
2602 }
2603
2604
2605 /*
2606   =============================================================================
2607   InitGameEngine()
2608   -----------------------------------------------------------------------------
2609   initialize game engine due to level / tape version number
2610   =============================================================================
2611 */
2612
2613 static void InitGameEngine()
2614 {
2615   int i, j, k, l, x, y;
2616
2617   /* set game engine from tape file when re-playing, else from level file */
2618   game.engine_version = (tape.playing ? tape.engine_version :
2619                          level.game_version);
2620
2621   /* set single or multi-player game mode (needed for re-playing tapes) */
2622   game.team_mode = setup.team_mode;
2623
2624   if (tape.playing)
2625   {
2626     int num_players = 0;
2627
2628     for (i = 0; i < MAX_PLAYERS; i++)
2629       if (tape.player_participates[i])
2630         num_players++;
2631
2632     /* multi-player tapes contain input data for more than one player */
2633     game.team_mode = (num_players > 1);
2634   }
2635
2636   /* ---------------------------------------------------------------------- */
2637   /* set flags for bugs and changes according to active game engine version */
2638   /* ---------------------------------------------------------------------- */
2639
2640   /*
2641     Summary of bugfix/change:
2642     Fixed handling for custom elements that change when pushed by the player.
2643
2644     Fixed/changed in version:
2645     3.1.0
2646
2647     Description:
2648     Before 3.1.0, custom elements that "change when pushing" changed directly
2649     after the player started pushing them (until then handled in "DigField()").
2650     Since 3.1.0, these custom elements are not changed until the "pushing"
2651     move of the element is finished (now handled in "ContinueMoving()").
2652
2653     Affected levels/tapes:
2654     The first condition is generally needed for all levels/tapes before version
2655     3.1.0, which might use the old behaviour before it was changed; known tapes
2656     that are affected are some tapes from the level set "Walpurgis Gardens" by
2657     Jamie Cullen.
2658     The second condition is an exception from the above case and is needed for
2659     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2660     above (including some development versions of 3.1.0), but before it was
2661     known that this change would break tapes like the above and was fixed in
2662     3.1.1, so that the changed behaviour was active although the engine version
2663     while recording maybe was before 3.1.0. There is at least one tape that is
2664     affected by this exception, which is the tape for the one-level set "Bug
2665     Machine" by Juergen Bonhagen.
2666   */
2667
2668   game.use_change_when_pushing_bug =
2669     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670      !(tape.playing &&
2671        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2672        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2673
2674   /*
2675     Summary of bugfix/change:
2676     Fixed handling for blocking the field the player leaves when moving.
2677
2678     Fixed/changed in version:
2679     3.1.1
2680
2681     Description:
2682     Before 3.1.1, when "block last field when moving" was enabled, the field
2683     the player is leaving when moving was blocked for the time of the move,
2684     and was directly unblocked afterwards. This resulted in the last field
2685     being blocked for exactly one less than the number of frames of one player
2686     move. Additionally, even when blocking was disabled, the last field was
2687     blocked for exactly one frame.
2688     Since 3.1.1, due to changes in player movement handling, the last field
2689     is not blocked at all when blocking is disabled. When blocking is enabled,
2690     the last field is blocked for exactly the number of frames of one player
2691     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2692     last field is blocked for exactly one more than the number of frames of
2693     one player move.
2694
2695     Affected levels/tapes:
2696     (!!! yet to be determined -- probably many !!!)
2697   */
2698
2699   game.use_block_last_field_bug =
2700     (game.engine_version < VERSION_IDENT(3,1,1,0));
2701
2702   /* ---------------------------------------------------------------------- */
2703
2704   /* set maximal allowed number of custom element changes per game frame */
2705   game.max_num_changes_per_frame = 1;
2706
2707   /* default scan direction: scan playfield from top/left to bottom/right */
2708   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2709
2710   /* dynamically adjust element properties according to game engine version */
2711   InitElementPropertiesEngine(game.engine_version);
2712
2713 #if 0
2714   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2715   printf("          tape version == %06d [%s] [file: %06d]\n",
2716          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2717          tape.file_version);
2718   printf("       => game.engine_version == %06d\n", game.engine_version);
2719 #endif
2720
2721   /* ---------- initialize player's initial move delay --------------------- */
2722
2723   /* dynamically adjust player properties according to level information */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay_value[i] =
2726       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2727
2728   /* dynamically adjust player properties according to game engine version */
2729   for (i = 0; i < MAX_PLAYERS; i++)
2730     game.initial_move_delay[i] =
2731       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2732        game.initial_move_delay_value[i] : 0);
2733
2734   /* ---------- initialize player's initial push delay --------------------- */
2735
2736   /* dynamically adjust player properties according to game engine version */
2737   game.initial_push_delay_value =
2738     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2739
2740   /* ---------- initialize changing elements ------------------------------- */
2741
2742   /* initialize changing elements information */
2743   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2744   {
2745     struct ElementInfo *ei = &element_info[i];
2746
2747     /* this pointer might have been changed in the level editor */
2748     ei->change = &ei->change_page[0];
2749
2750     if (!IS_CUSTOM_ELEMENT(i))
2751     {
2752       ei->change->target_element = EL_EMPTY_SPACE;
2753       ei->change->delay_fixed = 0;
2754       ei->change->delay_random = 0;
2755       ei->change->delay_frames = 1;
2756     }
2757
2758     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2759     {
2760       ei->has_change_event[j] = FALSE;
2761
2762       ei->event_page_nr[j] = 0;
2763       ei->event_page[j] = &ei->change_page[0];
2764     }
2765   }
2766
2767   /* add changing elements from pre-defined list */
2768   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2769   {
2770     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2771     struct ElementInfo *ei = &element_info[ch_delay->element];
2772
2773     ei->change->target_element       = ch_delay->target_element;
2774     ei->change->delay_fixed          = ch_delay->change_delay;
2775
2776     ei->change->pre_change_function  = ch_delay->pre_change_function;
2777     ei->change->change_function      = ch_delay->change_function;
2778     ei->change->post_change_function = ch_delay->post_change_function;
2779
2780     ei->change->can_change = TRUE;
2781     ei->change->can_change_or_has_action = TRUE;
2782
2783     ei->has_change_event[CE_DELAY] = TRUE;
2784
2785     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2786     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2787   }
2788
2789   /* ---------- initialize internal run-time variables --------------------- */
2790
2791   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2792   {
2793     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2794
2795     for (j = 0; j < ei->num_change_pages; j++)
2796     {
2797       ei->change_page[j].can_change_or_has_action =
2798         (ei->change_page[j].can_change |
2799          ei->change_page[j].has_action);
2800     }
2801   }
2802
2803   /* add change events from custom element configuration */
2804   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2805   {
2806     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2807
2808     for (j = 0; j < ei->num_change_pages; j++)
2809     {
2810       if (!ei->change_page[j].can_change_or_has_action)
2811         continue;
2812
2813       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2814       {
2815         /* only add event page for the first page found with this event */
2816         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2817         {
2818           ei->has_change_event[k] = TRUE;
2819
2820           ei->event_page_nr[k] = j;
2821           ei->event_page[k] = &ei->change_page[j];
2822         }
2823       }
2824     }
2825   }
2826
2827   /* ---------- initialize reference elements in change conditions --------- */
2828
2829   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2830   {
2831     int element = EL_CUSTOM_START + i;
2832     struct ElementInfo *ei = &element_info[element];
2833
2834     for (j = 0; j < ei->num_change_pages; j++)
2835     {
2836       int trigger_element = ei->change_page[j].initial_trigger_element;
2837
2838       if (trigger_element >= EL_PREV_CE_8 &&
2839           trigger_element <= EL_NEXT_CE_8)
2840         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2841
2842       ei->change_page[j].trigger_element = trigger_element;
2843     }
2844   }
2845
2846   /* ---------- initialize run-time trigger player and element ------------- */
2847
2848   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2849   {
2850     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2851
2852     for (j = 0; j < ei->num_change_pages; j++)
2853     {
2854       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2855       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2856       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2857       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2858       ei->change_page[j].actual_trigger_ce_value = 0;
2859       ei->change_page[j].actual_trigger_ce_score = 0;
2860     }
2861   }
2862
2863   /* ---------- initialize trigger events ---------------------------------- */
2864
2865   /* initialize trigger events information */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2868       trigger_events[i][j] = FALSE;
2869
2870   /* add trigger events from element change event properties */
2871   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2872   {
2873     struct ElementInfo *ei = &element_info[i];
2874
2875     for (j = 0; j < ei->num_change_pages; j++)
2876     {
2877       if (!ei->change_page[j].can_change_or_has_action)
2878         continue;
2879
2880       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2881       {
2882         int trigger_element = ei->change_page[j].trigger_element;
2883
2884         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2885         {
2886           if (ei->change_page[j].has_event[k])
2887           {
2888             if (IS_GROUP_ELEMENT(trigger_element))
2889             {
2890               struct ElementGroupInfo *group =
2891                 element_info[trigger_element].group;
2892
2893               for (l = 0; l < group->num_elements_resolved; l++)
2894                 trigger_events[group->element_resolved[l]][k] = TRUE;
2895             }
2896             else if (trigger_element == EL_ANY_ELEMENT)
2897               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2898                 trigger_events[l][k] = TRUE;
2899             else
2900               trigger_events[trigger_element][k] = TRUE;
2901           }
2902         }
2903       }
2904     }
2905   }
2906
2907   /* ---------- initialize push delay -------------------------------------- */
2908
2909   /* initialize push delay values to default */
2910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2911   {
2912     if (!IS_CUSTOM_ELEMENT(i))
2913     {
2914       /* set default push delay values (corrected since version 3.0.7-1) */
2915       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2916       {
2917         element_info[i].push_delay_fixed = 2;
2918         element_info[i].push_delay_random = 8;
2919       }
2920       else
2921       {
2922         element_info[i].push_delay_fixed = 8;
2923         element_info[i].push_delay_random = 8;
2924       }
2925     }
2926   }
2927
2928   /* set push delay value for certain elements from pre-defined list */
2929   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2930   {
2931     int e = push_delay_list[i].element;
2932
2933     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2934     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2935   }
2936
2937   /* set push delay value for Supaplex elements for newer engine versions */
2938   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2939   {
2940     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2941     {
2942       if (IS_SP_ELEMENT(i))
2943       {
2944         /* set SP push delay to just enough to push under a falling zonk */
2945         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2946
2947         element_info[i].push_delay_fixed  = delay;
2948         element_info[i].push_delay_random = 0;
2949       }
2950     }
2951   }
2952
2953   /* ---------- initialize move stepsize ----------------------------------- */
2954
2955   /* initialize move stepsize values to default */
2956   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2957     if (!IS_CUSTOM_ELEMENT(i))
2958       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2959
2960   /* set move stepsize value for certain elements from pre-defined list */
2961   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2962   {
2963     int e = move_stepsize_list[i].element;
2964
2965     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2966   }
2967
2968   /* ---------- initialize collect score ----------------------------------- */
2969
2970   /* initialize collect score values for custom elements from initial value */
2971   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2972     if (IS_CUSTOM_ELEMENT(i))
2973       element_info[i].collect_score = element_info[i].collect_score_initial;
2974
2975   /* ---------- initialize collect count ----------------------------------- */
2976
2977   /* initialize collect count values for non-custom elements */
2978   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2979     if (!IS_CUSTOM_ELEMENT(i))
2980       element_info[i].collect_count_initial = 0;
2981
2982   /* add collect count values for all elements from pre-defined list */
2983   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2984     element_info[collect_count_list[i].element].collect_count_initial =
2985       collect_count_list[i].count;
2986
2987   /* ---------- initialize access direction -------------------------------- */
2988
2989   /* initialize access direction values to default (access from every side) */
2990   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2991     if (!IS_CUSTOM_ELEMENT(i))
2992       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2993
2994   /* set access direction value for certain elements from pre-defined list */
2995   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2996     element_info[access_direction_list[i].element].access_direction =
2997       access_direction_list[i].direction;
2998
2999   /* ---------- initialize explosion content ------------------------------- */
3000   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3001   {
3002     if (IS_CUSTOM_ELEMENT(i))
3003       continue;
3004
3005     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3006     {
3007       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3008
3009       element_info[i].content.e[x][y] =
3010         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3011          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3012          i == EL_PLAYER_3 ? EL_EMERALD :
3013          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3014          i == EL_MOLE ? EL_EMERALD_RED :
3015          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3016          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3017          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3018          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3019          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3020          i == EL_WALL_EMERALD ? EL_EMERALD :
3021          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3022          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3023          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3024          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3025          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3026          i == EL_WALL_PEARL ? EL_PEARL :
3027          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3028          EL_EMPTY);
3029     }
3030   }
3031
3032   /* ---------- initialize recursion detection ------------------------------ */
3033   recursion_loop_depth = 0;
3034   recursion_loop_detected = FALSE;
3035   recursion_loop_element = EL_UNDEFINED;
3036
3037   /* ---------- initialize graphics engine ---------------------------------- */
3038   game.scroll_delay_value =
3039     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3040      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3041   game.scroll_delay_value =
3042     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3043
3044   /* ---------- initialize game engine snapshots ---------------------------- */
3045   for (i = 0; i < MAX_PLAYERS; i++)
3046     game.snapshot.last_action[i] = 0;
3047   game.snapshot.changed_action = FALSE;
3048   game.snapshot.collected_item = FALSE;
3049   game.snapshot.mode =
3050     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3051      SNAPSHOT_MODE_EVERY_STEP :
3052      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3053      SNAPSHOT_MODE_EVERY_MOVE :
3054      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3055      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3056   game.snapshot.save_snapshot = FALSE;
3057 }
3058
3059 int get_num_special_action(int element, int action_first, int action_last)
3060 {
3061   int num_special_action = 0;
3062   int i, j;
3063
3064   for (i = action_first; i <= action_last; i++)
3065   {
3066     boolean found = FALSE;
3067
3068     for (j = 0; j < NUM_DIRECTIONS; j++)
3069       if (el_act_dir2img(element, i, j) !=
3070           el_act_dir2img(element, ACTION_DEFAULT, j))
3071         found = TRUE;
3072
3073     if (found)
3074       num_special_action++;
3075     else
3076       break;
3077   }
3078
3079   return num_special_action;
3080 }
3081
3082
3083 /*
3084   =============================================================================
3085   InitGame()
3086   -----------------------------------------------------------------------------
3087   initialize and start new game
3088   =============================================================================
3089 */
3090
3091 void InitGame()
3092 {
3093   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3094   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3095   int fade_mask = REDRAW_FIELD;
3096
3097   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3098   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3099   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3100   int initial_move_dir = MV_DOWN;
3101   int i, j, x, y;
3102
3103   // required here to update video display before fading (FIX THIS)
3104   DrawMaskedBorder(REDRAW_DOOR_2);
3105
3106   if (!game.restart_level)
3107     CloseDoor(DOOR_CLOSE_1);
3108
3109   SetGameStatus(GAME_MODE_PLAYING);
3110
3111   if (level_editor_test_game)
3112     FadeSkipNextFadeIn();
3113   else
3114     FadeSetEnterScreen();
3115
3116   if (CheckIfGlobalBorderHasChanged())
3117     fade_mask = REDRAW_ALL;
3118
3119   FadeSoundsAndMusic();
3120
3121   ExpireSoundLoops(TRUE);
3122
3123   FadeOut(fade_mask);
3124
3125   /* needed if different viewport properties defined for playing */
3126   ChangeViewportPropertiesIfNeeded();
3127
3128   ClearField();
3129
3130   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3131
3132   DrawCompleteVideoDisplay();
3133
3134   InitGameEngine();
3135   InitGameControlValues();
3136
3137   /* don't play tapes over network */
3138   network_playing = (options.network && !tape.playing);
3139
3140   for (i = 0; i < MAX_PLAYERS; i++)
3141   {
3142     struct PlayerInfo *player = &stored_player[i];
3143
3144     player->index_nr = i;
3145     player->index_bit = (1 << i);
3146     player->element_nr = EL_PLAYER_1 + i;
3147
3148     player->present = FALSE;
3149     player->active = FALSE;
3150     player->mapped = FALSE;
3151
3152     player->killed = FALSE;
3153     player->reanimated = FALSE;
3154
3155     player->action = 0;
3156     player->effective_action = 0;
3157     player->programmed_action = 0;
3158
3159     player->score = 0;
3160     player->score_final = 0;
3161
3162     player->gems_still_needed = level.gems_needed;
3163     player->sokobanfields_still_needed = 0;
3164     player->lights_still_needed = 0;
3165     player->friends_still_needed = 0;
3166
3167     for (j = 0; j < MAX_NUM_KEYS; j++)
3168       player->key[j] = FALSE;
3169
3170     player->num_white_keys = 0;
3171
3172     player->dynabomb_count = 0;
3173     player->dynabomb_size = 1;
3174     player->dynabombs_left = 0;
3175     player->dynabomb_xl = FALSE;
3176
3177     player->MovDir = initial_move_dir;
3178     player->MovPos = 0;
3179     player->GfxPos = 0;
3180     player->GfxDir = initial_move_dir;
3181     player->GfxAction = ACTION_DEFAULT;
3182     player->Frame = 0;
3183     player->StepFrame = 0;
3184
3185     player->initial_element = player->element_nr;
3186     player->artwork_element =
3187       (level.use_artwork_element[i] ? level.artwork_element[i] :
3188        player->element_nr);
3189     player->use_murphy = FALSE;
3190
3191     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3192     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3193
3194     player->gravity = level.initial_player_gravity[i];
3195
3196     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3197
3198     player->actual_frame_counter = 0;
3199
3200     player->step_counter = 0;
3201
3202     player->last_move_dir = initial_move_dir;
3203
3204     player->is_active = FALSE;
3205
3206     player->is_waiting = FALSE;
3207     player->is_moving = FALSE;
3208     player->is_auto_moving = FALSE;
3209     player->is_digging = FALSE;
3210     player->is_snapping = FALSE;
3211     player->is_collecting = FALSE;
3212     player->is_pushing = FALSE;
3213     player->is_switching = FALSE;
3214     player->is_dropping = FALSE;
3215     player->is_dropping_pressed = FALSE;
3216
3217     player->is_bored = FALSE;
3218     player->is_sleeping = FALSE;
3219
3220     player->was_waiting = TRUE;
3221     player->was_moving = FALSE;
3222     player->was_snapping = FALSE;
3223     player->was_dropping = FALSE;
3224
3225     player->frame_counter_bored = -1;
3226     player->frame_counter_sleeping = -1;
3227
3228     player->anim_delay_counter = 0;
3229     player->post_delay_counter = 0;
3230
3231     player->dir_waiting = initial_move_dir;
3232     player->action_waiting = ACTION_DEFAULT;
3233     player->last_action_waiting = ACTION_DEFAULT;
3234     player->special_action_bored = ACTION_DEFAULT;
3235     player->special_action_sleeping = ACTION_DEFAULT;
3236
3237     player->switch_x = -1;
3238     player->switch_y = -1;
3239
3240     player->drop_x = -1;
3241     player->drop_y = -1;
3242
3243     player->show_envelope = 0;
3244
3245     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3246
3247     player->push_delay       = -1;      /* initialized when pushing starts */
3248     player->push_delay_value = game.initial_push_delay_value;
3249
3250     player->drop_delay = 0;
3251     player->drop_pressed_delay = 0;
3252
3253     player->last_jx = -1;
3254     player->last_jy = -1;
3255     player->jx = -1;
3256     player->jy = -1;
3257
3258     player->shield_normal_time_left = 0;
3259     player->shield_deadly_time_left = 0;
3260
3261     player->inventory_infinite_element = EL_UNDEFINED;
3262     player->inventory_size = 0;
3263
3264     if (level.use_initial_inventory[i])
3265     {
3266       for (j = 0; j < level.initial_inventory_size[i]; j++)
3267       {
3268         int element = level.initial_inventory_content[i][j];
3269         int collect_count = element_info[element].collect_count_initial;
3270         int k;
3271
3272         if (!IS_CUSTOM_ELEMENT(element))
3273           collect_count = 1;
3274
3275         if (collect_count == 0)
3276           player->inventory_infinite_element = element;
3277         else
3278           for (k = 0; k < collect_count; k++)
3279             if (player->inventory_size < MAX_INVENTORY_SIZE)
3280               player->inventory_element[player->inventory_size++] = element;
3281       }
3282     }
3283
3284     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3285     SnapField(player, 0, 0);
3286
3287     player->LevelSolved = FALSE;
3288     player->GameOver = FALSE;
3289
3290     player->LevelSolved_GameWon = FALSE;
3291     player->LevelSolved_GameEnd = FALSE;
3292     player->LevelSolved_PanelOff = FALSE;
3293     player->LevelSolved_SaveTape = FALSE;
3294     player->LevelSolved_SaveScore = FALSE;
3295     player->LevelSolved_CountingTime = 0;
3296     player->LevelSolved_CountingScore = 0;
3297
3298     map_player_action[i] = i;
3299   }
3300
3301   network_player_action_received = FALSE;
3302
3303 #if defined(NETWORK_AVALIABLE)
3304   /* initial null action */
3305   if (network_playing)
3306     SendToServer_MovePlayer(MV_NONE);
3307 #endif
3308
3309   ZX = ZY = -1;
3310   ExitX = ExitY = -1;
3311
3312   FrameCounter = 0;
3313   TimeFrames = 0;
3314   TimePlayed = 0;
3315   TimeLeft = level.time;
3316   TapeTime = 0;
3317
3318   ScreenMovDir = MV_NONE;
3319   ScreenMovPos = 0;
3320   ScreenGfxPos = 0;
3321
3322   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3323
3324   AllPlayersGone = FALSE;
3325
3326   game.no_time_limit = (level.time == 0);
3327
3328   game.yamyam_content_nr = 0;
3329   game.robot_wheel_active = FALSE;
3330   game.magic_wall_active = FALSE;
3331   game.magic_wall_time_left = 0;
3332   game.light_time_left = 0;
3333   game.timegate_time_left = 0;
3334   game.switchgate_pos = 0;
3335   game.wind_direction = level.wind_direction_initial;
3336
3337   game.lenses_time_left = 0;
3338   game.magnify_time_left = 0;
3339
3340   game.ball_state = level.ball_state_initial;
3341   game.ball_content_nr = 0;
3342
3343   game.envelope_active = FALSE;
3344
3345   /* set focus to local player for network games, else to all players */
3346   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3347   game.centered_player_nr_next = game.centered_player_nr;
3348   game.set_centered_player = FALSE;
3349
3350   if (network_playing && tape.recording)
3351   {
3352     /* store client dependent player focus when recording network games */
3353     tape.centered_player_nr_next = game.centered_player_nr_next;
3354     tape.set_centered_player = TRUE;
3355   }
3356
3357   for (i = 0; i < NUM_BELTS; i++)
3358   {
3359     game.belt_dir[i] = MV_NONE;
3360     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3361   }
3362
3363   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3364     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3365
3366 #if DEBUG_INIT_PLAYER
3367   if (options.debug)
3368   {
3369     printf("Player status at level initialization:\n");
3370   }
3371 #endif
3372
3373   SCAN_PLAYFIELD(x, y)
3374   {
3375     Feld[x][y] = level.field[x][y];
3376     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3377     ChangeDelay[x][y] = 0;
3378     ChangePage[x][y] = -1;
3379     CustomValue[x][y] = 0;              /* initialized in InitField() */
3380     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3381     AmoebaNr[x][y] = 0;
3382     WasJustMoving[x][y] = 0;
3383     WasJustFalling[x][y] = 0;
3384     CheckCollision[x][y] = 0;
3385     CheckImpact[x][y] = 0;
3386     Stop[x][y] = FALSE;
3387     Pushed[x][y] = FALSE;
3388
3389     ChangeCount[x][y] = 0;
3390     ChangeEvent[x][y] = -1;
3391
3392     ExplodePhase[x][y] = 0;
3393     ExplodeDelay[x][y] = 0;
3394     ExplodeField[x][y] = EX_TYPE_NONE;
3395
3396     RunnerVisit[x][y] = 0;
3397     PlayerVisit[x][y] = 0;
3398
3399     GfxFrame[x][y] = 0;
3400     GfxRandom[x][y] = INIT_GFX_RANDOM();
3401     GfxElement[x][y] = EL_UNDEFINED;
3402     GfxAction[x][y] = ACTION_DEFAULT;
3403     GfxDir[x][y] = MV_NONE;
3404     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3405   }
3406
3407   SCAN_PLAYFIELD(x, y)
3408   {
3409     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3410       emulate_bd = FALSE;
3411     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3412       emulate_sb = FALSE;
3413     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3414       emulate_sp = FALSE;
3415
3416     InitField(x, y, TRUE);
3417
3418     ResetGfxAnimation(x, y);
3419   }
3420
3421   InitBeltMovement();
3422
3423   for (i = 0; i < MAX_PLAYERS; i++)
3424   {
3425     struct PlayerInfo *player = &stored_player[i];
3426
3427     /* set number of special actions for bored and sleeping animation */
3428     player->num_special_action_bored =
3429       get_num_special_action(player->artwork_element,
3430                              ACTION_BORING_1, ACTION_BORING_LAST);
3431     player->num_special_action_sleeping =
3432       get_num_special_action(player->artwork_element,
3433                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3434   }
3435
3436   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3437                     emulate_sb ? EMU_SOKOBAN :
3438                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3439
3440   /* initialize type of slippery elements */
3441   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442   {
3443     if (!IS_CUSTOM_ELEMENT(i))
3444     {
3445       /* default: elements slip down either to the left or right randomly */
3446       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3447
3448       /* SP style elements prefer to slip down on the left side */
3449       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3450         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3451
3452       /* BD style elements prefer to slip down on the left side */
3453       if (game.emulation == EMU_BOULDERDASH)
3454         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3455     }
3456   }
3457
3458   /* initialize explosion and ignition delay */
3459   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3460   {
3461     if (!IS_CUSTOM_ELEMENT(i))
3462     {
3463       int num_phase = 8;
3464       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3465                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3466                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3467       int last_phase = (num_phase + 1) * delay;
3468       int half_phase = (num_phase / 2) * delay;
3469
3470       element_info[i].explosion_delay = last_phase - 1;
3471       element_info[i].ignition_delay = half_phase;
3472
3473       if (i == EL_BLACK_ORB)
3474         element_info[i].ignition_delay = 1;
3475     }
3476   }
3477
3478   /* correct non-moving belts to start moving left */
3479   for (i = 0; i < NUM_BELTS; i++)
3480     if (game.belt_dir[i] == MV_NONE)
3481       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3482
3483 #if USE_NEW_PLAYER_ASSIGNMENTS
3484   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3485   /* choose default local player */
3486   local_player = &stored_player[0];
3487
3488   for (i = 0; i < MAX_PLAYERS; i++)
3489     stored_player[i].connected = FALSE;
3490
3491   local_player->connected = TRUE;
3492   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3493
3494   if (tape.playing)
3495   {
3496     for (i = 0; i < MAX_PLAYERS; i++)
3497       stored_player[i].connected = tape.player_participates[i];
3498   }
3499   else if (game.team_mode && !options.network)
3500   {
3501     /* try to guess locally connected team mode players (needed for correct
3502        assignment of player figures from level to locally playing players) */
3503
3504     for (i = 0; i < MAX_PLAYERS; i++)
3505       if (setup.input[i].use_joystick ||
3506           setup.input[i].key.left != KSYM_UNDEFINED)
3507         stored_player[i].connected = TRUE;
3508   }
3509
3510 #if DEBUG_INIT_PLAYER
3511   if (options.debug)
3512   {
3513     printf("Player status after level initialization:\n");
3514
3515     for (i = 0; i < MAX_PLAYERS; i++)
3516     {
3517       struct PlayerInfo *player = &stored_player[i];
3518
3519       printf("- player %d: present == %d, connected == %d, active == %d",
3520              i + 1,
3521              player->present,
3522              player->connected,
3523              player->active);
3524
3525       if (local_player == player)
3526         printf(" (local player)");
3527
3528       printf("\n");
3529     }
3530   }
3531 #endif
3532
3533 #if DEBUG_INIT_PLAYER
3534   if (options.debug)
3535     printf("Reassigning players ...\n");
3536 #endif
3537
3538   /* check if any connected player was not found in playfield */
3539   for (i = 0; i < MAX_PLAYERS; i++)
3540   {
3541     struct PlayerInfo *player = &stored_player[i];
3542
3543     if (player->connected && !player->present)
3544     {
3545       struct PlayerInfo *field_player = NULL;
3546
3547 #if DEBUG_INIT_PLAYER
3548       if (options.debug)
3549         printf("- looking for field player for player %d ...\n", i + 1);
3550 #endif
3551
3552       /* assign first free player found that is present in the playfield */
3553
3554       /* first try: look for unmapped playfield player that is not connected */
3555       for (j = 0; j < MAX_PLAYERS; j++)
3556         if (field_player == NULL &&
3557             stored_player[j].present &&
3558             !stored_player[j].mapped &&
3559             !stored_player[j].connected)
3560           field_player = &stored_player[j];
3561
3562       /* second try: look for *any* unmapped playfield player */
3563       for (j = 0; j < MAX_PLAYERS; j++)
3564         if (field_player == NULL &&
3565             stored_player[j].present &&
3566             !stored_player[j].mapped)
3567           field_player = &stored_player[j];
3568
3569       if (field_player != NULL)
3570       {
3571         int jx = field_player->jx, jy = field_player->jy;
3572
3573 #if DEBUG_INIT_PLAYER
3574         if (options.debug)
3575           printf("- found player %d\n", field_player->index_nr + 1);
3576 #endif
3577
3578         player->present = FALSE;
3579         player->active = FALSE;
3580
3581         field_player->present = TRUE;
3582         field_player->active = TRUE;
3583
3584         /*
3585         player->initial_element = field_player->initial_element;
3586         player->artwork_element = field_player->artwork_element;
3587
3588         player->block_last_field       = field_player->block_last_field;
3589         player->block_delay_adjustment = field_player->block_delay_adjustment;
3590         */
3591
3592         StorePlayer[jx][jy] = field_player->element_nr;
3593
3594         field_player->jx = field_player->last_jx = jx;
3595         field_player->jy = field_player->last_jy = jy;
3596
3597         if (local_player == player)
3598           local_player = field_player;
3599
3600         map_player_action[field_player->index_nr] = i;
3601
3602         field_player->mapped = TRUE;
3603
3604 #if DEBUG_INIT_PLAYER
3605         if (options.debug)
3606           printf("- map_player_action[%d] == %d\n",
3607                  field_player->index_nr + 1, i + 1);
3608 #endif
3609       }
3610     }
3611
3612     if (player->connected && player->present)
3613       player->mapped = TRUE;
3614   }
3615
3616 #if DEBUG_INIT_PLAYER
3617   if (options.debug)
3618   {
3619     printf("Player status after player assignment (first stage):\n");
3620
3621     for (i = 0; i < MAX_PLAYERS; i++)
3622     {
3623       struct PlayerInfo *player = &stored_player[i];
3624
3625       printf("- player %d: present == %d, connected == %d, active == %d",
3626              i + 1,
3627              player->present,
3628              player->connected,
3629              player->active);
3630
3631       if (local_player == player)
3632         printf(" (local player)");
3633
3634       printf("\n");
3635     }
3636   }
3637 #endif
3638
3639 #else
3640
3641   /* check if any connected player was not found in playfield */
3642   for (i = 0; i < MAX_PLAYERS; i++)
3643   {
3644     struct PlayerInfo *player = &stored_player[i];
3645
3646     if (player->connected && !player->present)
3647     {
3648       for (j = 0; j < MAX_PLAYERS; j++)
3649       {
3650         struct PlayerInfo *field_player = &stored_player[j];
3651         int jx = field_player->jx, jy = field_player->jy;
3652
3653         /* assign first free player found that is present in the playfield */
3654         if (field_player->present && !field_player->connected)
3655         {
3656           player->present = TRUE;
3657           player->active = TRUE;
3658
3659           field_player->present = FALSE;
3660           field_player->active = FALSE;
3661
3662           player->initial_element = field_player->initial_element;
3663           player->artwork_element = field_player->artwork_element;
3664
3665           player->block_last_field       = field_player->block_last_field;
3666           player->block_delay_adjustment = field_player->block_delay_adjustment;
3667
3668           StorePlayer[jx][jy] = player->element_nr;
3669
3670           player->jx = player->last_jx = jx;
3671           player->jy = player->last_jy = jy;
3672
3673           break;
3674         }
3675       }
3676     }
3677   }
3678 #endif
3679
3680 #if 0
3681   printf("::: local_player->present == %d\n", local_player->present);
3682 #endif
3683
3684   if (tape.playing)
3685   {
3686     /* when playing a tape, eliminate all players who do not participate */
3687
3688 #if USE_NEW_PLAYER_ASSIGNMENTS
3689
3690     if (!game.team_mode)
3691     {
3692       for (i = 0; i < MAX_PLAYERS; i++)
3693       {
3694         if (stored_player[i].active &&
3695             !tape.player_participates[map_player_action[i]])
3696         {
3697           struct PlayerInfo *player = &stored_player[i];
3698           int jx = player->jx, jy = player->jy;
3699
3700 #if DEBUG_INIT_PLAYER
3701           if (options.debug)
3702             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3703 #endif
3704
3705           player->active = FALSE;
3706           StorePlayer[jx][jy] = 0;
3707           Feld[jx][jy] = EL_EMPTY;
3708         }
3709       }
3710     }
3711
3712 #else
3713
3714     for (i = 0; i < MAX_PLAYERS; i++)
3715     {
3716       if (stored_player[i].active &&
3717           !tape.player_participates[i])
3718       {
3719         struct PlayerInfo *player = &stored_player[i];
3720         int jx = player->jx, jy = player->jy;
3721
3722         player->active = FALSE;
3723         StorePlayer[jx][jy] = 0;
3724         Feld[jx][jy] = EL_EMPTY;
3725       }
3726     }
3727 #endif
3728   }
3729   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3730   {
3731     /* when in single player mode, eliminate all but the first active player */
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734     {
3735       if (stored_player[i].active)
3736       {
3737         for (j = i + 1; j < MAX_PLAYERS; j++)
3738         {
3739           if (stored_player[j].active)
3740           {
3741             struct PlayerInfo *player = &stored_player[j];
3742             int jx = player->jx, jy = player->jy;
3743
3744             player->active = FALSE;
3745             player->present = FALSE;
3746
3747             StorePlayer[jx][jy] = 0;
3748             Feld[jx][jy] = EL_EMPTY;
3749           }
3750         }
3751       }
3752     }
3753   }
3754
3755   /* when recording the game, store which players take part in the game */
3756   if (tape.recording)
3757   {
3758 #if USE_NEW_PLAYER_ASSIGNMENTS
3759     for (i = 0; i < MAX_PLAYERS; i++)
3760       if (stored_player[i].connected)
3761         tape.player_participates[i] = TRUE;
3762 #else
3763     for (i = 0; i < MAX_PLAYERS; i++)
3764       if (stored_player[i].active)
3765         tape.player_participates[i] = TRUE;
3766 #endif
3767   }
3768
3769 #if DEBUG_INIT_PLAYER
3770   if (options.debug)
3771   {
3772     printf("Player status after player assignment (final stage):\n");
3773
3774     for (i = 0; i < MAX_PLAYERS; i++)
3775     {
3776       struct PlayerInfo *player = &stored_player[i];
3777
3778       printf("- player %d: present == %d, connected == %d, active == %d",
3779              i + 1,
3780              player->present,
3781              player->connected,
3782              player->active);
3783
3784       if (local_player == player)
3785         printf(" (local player)");
3786
3787       printf("\n");
3788     }
3789   }
3790 #endif
3791
3792   if (BorderElement == EL_EMPTY)
3793   {
3794     SBX_Left = 0;
3795     SBX_Right = lev_fieldx - SCR_FIELDX;
3796     SBY_Upper = 0;
3797     SBY_Lower = lev_fieldy - SCR_FIELDY;
3798   }
3799   else
3800   {
3801     SBX_Left = -1;
3802     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3803     SBY_Upper = -1;
3804     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3805   }
3806
3807   if (full_lev_fieldx <= SCR_FIELDX)
3808     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3809   if (full_lev_fieldy <= SCR_FIELDY)
3810     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3811
3812   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3813     SBX_Left--;
3814   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3815     SBY_Upper--;
3816
3817   /* if local player not found, look for custom element that might create
3818      the player (make some assumptions about the right custom element) */
3819   if (!local_player->present)
3820   {
3821     int start_x = 0, start_y = 0;
3822     int found_rating = 0;
3823     int found_element = EL_UNDEFINED;
3824     int player_nr = local_player->index_nr;
3825
3826     SCAN_PLAYFIELD(x, y)
3827     {
3828       int element = Feld[x][y];
3829       int content;
3830       int xx, yy;
3831       boolean is_player;
3832
3833       if (level.use_start_element[player_nr] &&
3834           level.start_element[player_nr] == element &&
3835           found_rating < 4)
3836       {
3837         start_x = x;
3838         start_y = y;
3839
3840         found_rating = 4;
3841         found_element = element;
3842       }
3843
3844       if (!IS_CUSTOM_ELEMENT(element))
3845         continue;
3846
3847       if (CAN_CHANGE(element))
3848       {
3849         for (i = 0; i < element_info[element].num_change_pages; i++)
3850         {
3851           /* check for player created from custom element as single target */
3852           content = element_info[element].change_page[i].target_element;
3853           is_player = ELEM_IS_PLAYER(content);
3854
3855           if (is_player && (found_rating < 3 ||
3856                             (found_rating == 3 && element < found_element)))
3857           {
3858             start_x = x;
3859             start_y = y;
3860
3861             found_rating = 3;
3862             found_element = element;
3863           }
3864         }
3865       }
3866
3867       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3868       {
3869         /* check for player created from custom element as explosion content */
3870         content = element_info[element].content.e[xx][yy];
3871         is_player = ELEM_IS_PLAYER(content);
3872
3873         if (is_player && (found_rating < 2 ||
3874                           (found_rating == 2 && element < found_element)))
3875         {
3876           start_x = x + xx - 1;
3877           start_y = y + yy - 1;
3878
3879           found_rating = 2;
3880           found_element = element;
3881         }
3882
3883         if (!CAN_CHANGE(element))
3884           continue;
3885
3886         for (i = 0; i < element_info[element].num_change_pages; i++)
3887         {
3888           /* check for player created from custom element as extended target */
3889           content =
3890             element_info[element].change_page[i].target_content.e[xx][yy];
3891
3892           is_player = ELEM_IS_PLAYER(content);
3893
3894           if (is_player && (found_rating < 1 ||
3895                             (found_rating == 1 && element < found_element)))
3896           {
3897             start_x = x + xx - 1;
3898             start_y = y + yy - 1;
3899
3900             found_rating = 1;
3901             found_element = element;
3902           }
3903         }
3904       }
3905     }
3906
3907     scroll_x = SCROLL_POSITION_X(start_x);
3908     scroll_y = SCROLL_POSITION_Y(start_y);
3909   }
3910   else
3911   {
3912     scroll_x = SCROLL_POSITION_X(local_player->jx);
3913     scroll_y = SCROLL_POSITION_Y(local_player->jy);
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                         int actual_player_x, int actual_player_y)
4036 {
4037   /* this is used for non-R'n'D game engines to update certain engine values */
4038
4039   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4040   {
4041     actual_player_x = correctLevelPosX_EM(actual_player_x);
4042     actual_player_y = correctLevelPosY_EM(actual_player_y);
4043   }
4044
4045   /* needed to determine if sounds are played within the visible screen area */
4046   scroll_x = actual_scroll_x;
4047   scroll_y = actual_scroll_y;
4048
4049   /* needed to get player position for "follow finger" playing input method */
4050   local_player->jx = actual_player_x;
4051   local_player->jy = actual_player_y;
4052 }
4053
4054 void InitMovDir(int x, int y)
4055 {
4056   int i, element = Feld[x][y];
4057   static int xy[4][2] =
4058   {
4059     {  0, +1 },
4060     { +1,  0 },
4061     {  0, -1 },
4062     { -1,  0 }
4063   };
4064   static int direction[3][4] =
4065   {
4066     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4067     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4068     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4069   };
4070
4071   switch (element)
4072   {
4073     case EL_BUG_RIGHT:
4074     case EL_BUG_UP:
4075     case EL_BUG_LEFT:
4076     case EL_BUG_DOWN:
4077       Feld[x][y] = EL_BUG;
4078       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4079       break;
4080
4081     case EL_SPACESHIP_RIGHT:
4082     case EL_SPACESHIP_UP:
4083     case EL_SPACESHIP_LEFT:
4084     case EL_SPACESHIP_DOWN:
4085       Feld[x][y] = EL_SPACESHIP;
4086       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4087       break;
4088
4089     case EL_BD_BUTTERFLY_RIGHT:
4090     case EL_BD_BUTTERFLY_UP:
4091     case EL_BD_BUTTERFLY_LEFT:
4092     case EL_BD_BUTTERFLY_DOWN:
4093       Feld[x][y] = EL_BD_BUTTERFLY;
4094       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4095       break;
4096
4097     case EL_BD_FIREFLY_RIGHT:
4098     case EL_BD_FIREFLY_UP:
4099     case EL_BD_FIREFLY_LEFT:
4100     case EL_BD_FIREFLY_DOWN:
4101       Feld[x][y] = EL_BD_FIREFLY;
4102       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4103       break;
4104
4105     case EL_PACMAN_RIGHT:
4106     case EL_PACMAN_UP:
4107     case EL_PACMAN_LEFT:
4108     case EL_PACMAN_DOWN:
4109       Feld[x][y] = EL_PACMAN;
4110       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4111       break;
4112
4113     case EL_YAMYAM_LEFT:
4114     case EL_YAMYAM_RIGHT:
4115     case EL_YAMYAM_UP:
4116     case EL_YAMYAM_DOWN:
4117       Feld[x][y] = EL_YAMYAM;
4118       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4119       break;
4120
4121     case EL_SP_SNIKSNAK:
4122       MovDir[x][y] = MV_UP;
4123       break;
4124
4125     case EL_SP_ELECTRON:
4126       MovDir[x][y] = MV_LEFT;
4127       break;
4128
4129     case EL_MOLE_LEFT:
4130     case EL_MOLE_RIGHT:
4131     case EL_MOLE_UP:
4132     case EL_MOLE_DOWN:
4133       Feld[x][y] = EL_MOLE;
4134       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4135       break;
4136
4137     default:
4138       if (IS_CUSTOM_ELEMENT(element))
4139       {
4140         struct ElementInfo *ei = &element_info[element];
4141         int move_direction_initial = ei->move_direction_initial;
4142         int move_pattern = ei->move_pattern;
4143
4144         if (move_direction_initial == MV_START_PREVIOUS)
4145         {
4146           if (MovDir[x][y] != MV_NONE)
4147             return;
4148
4149           move_direction_initial = MV_START_AUTOMATIC;
4150         }
4151
4152         if (move_direction_initial == MV_START_RANDOM)
4153           MovDir[x][y] = 1 << RND(4);
4154         else if (move_direction_initial & MV_ANY_DIRECTION)
4155           MovDir[x][y] = move_direction_initial;
4156         else if (move_pattern == MV_ALL_DIRECTIONS ||
4157                  move_pattern == MV_TURNING_LEFT ||
4158                  move_pattern == MV_TURNING_RIGHT ||
4159                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4160                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4161                  move_pattern == MV_TURNING_RANDOM)
4162           MovDir[x][y] = 1 << RND(4);
4163         else if (move_pattern == MV_HORIZONTAL)
4164           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4165         else if (move_pattern == MV_VERTICAL)
4166           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4167         else if (move_pattern & MV_ANY_DIRECTION)
4168           MovDir[x][y] = element_info[element].move_pattern;
4169         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4170                  move_pattern == MV_ALONG_RIGHT_SIDE)
4171         {
4172           /* use random direction as default start direction */
4173           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4174             MovDir[x][y] = 1 << RND(4);
4175
4176           for (i = 0; i < NUM_DIRECTIONS; i++)
4177           {
4178             int x1 = x + xy[i][0];
4179             int y1 = y + xy[i][1];
4180
4181             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4182             {
4183               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4184                 MovDir[x][y] = direction[0][i];
4185               else
4186                 MovDir[x][y] = direction[1][i];
4187
4188               break;
4189             }
4190           }
4191         }                
4192       }
4193       else
4194       {
4195         MovDir[x][y] = 1 << RND(4);
4196
4197         if (element != EL_BUG &&
4198             element != EL_SPACESHIP &&
4199             element != EL_BD_BUTTERFLY &&
4200             element != EL_BD_FIREFLY)
4201           break;
4202
4203         for (i = 0; i < NUM_DIRECTIONS; i++)
4204         {
4205           int x1 = x + xy[i][0];
4206           int y1 = y + xy[i][1];
4207
4208           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4209           {
4210             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4211             {
4212               MovDir[x][y] = direction[0][i];
4213               break;
4214             }
4215             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4216                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4217             {
4218               MovDir[x][y] = direction[1][i];
4219               break;
4220             }
4221           }
4222         }
4223       }
4224       break;
4225   }
4226
4227   GfxDir[x][y] = MovDir[x][y];
4228 }
4229
4230 void InitAmoebaNr(int x, int y)
4231 {
4232   int i;
4233   int group_nr = AmoebeNachbarNr(x, y);
4234
4235   if (group_nr == 0)
4236   {
4237     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4238     {
4239       if (AmoebaCnt[i] == 0)
4240       {
4241         group_nr = i;
4242         break;
4243       }
4244     }
4245   }
4246
4247   AmoebaNr[x][y] = group_nr;
4248   AmoebaCnt[group_nr]++;
4249   AmoebaCnt2[group_nr]++;
4250 }
4251
4252 static void PlayerWins(struct PlayerInfo *player)
4253 {
4254   player->LevelSolved = TRUE;
4255   player->GameOver = TRUE;
4256
4257   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4258                          level.native_em_level->lev->score : player->score);
4259
4260   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4261                                       TimeLeft);
4262   player->LevelSolved_CountingScore = player->score_final;
4263 }
4264
4265 void GameWon()
4266 {
4267   static int time, time_final;
4268   static int score, score_final;
4269   static int game_over_delay_1 = 0;
4270   static int game_over_delay_2 = 0;
4271   int game_over_delay_value_1 = 50;
4272   int game_over_delay_value_2 = 50;
4273
4274   if (!local_player->LevelSolved_GameWon)
4275   {
4276     int i;
4277
4278     /* do not start end game actions before the player stops moving (to exit) */
4279     if (local_player->MovPos)
4280       return;
4281
4282     local_player->LevelSolved_GameWon = TRUE;
4283     local_player->LevelSolved_SaveTape = tape.recording;
4284     local_player->LevelSolved_SaveScore = !tape.playing;
4285
4286     if (!tape.playing)
4287     {
4288       LevelStats_incSolved(level_nr);
4289
4290       SaveLevelSetup_SeriesInfo();
4291     }
4292
4293     if (tape.auto_play)         /* tape might already be stopped here */
4294       tape.auto_play_level_solved = TRUE;
4295
4296     TapeStop();
4297
4298     game_over_delay_1 = game_over_delay_value_1;
4299     game_over_delay_2 = game_over_delay_value_2;
4300
4301     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4302     score = score_final = local_player->score_final;
4303
4304     if (TimeLeft > 0)
4305     {
4306       time_final = 0;
4307       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4308     }
4309     else if (game.no_time_limit && TimePlayed < 999)
4310     {
4311       time_final = 999;
4312       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4313     }
4314
4315     local_player->score_final = score_final;
4316
4317     if (level_editor_test_game)
4318     {
4319       time = time_final;
4320       score = score_final;
4321
4322       local_player->LevelSolved_CountingTime = time;
4323       local_player->LevelSolved_CountingScore = score;
4324
4325       game_panel_controls[GAME_PANEL_TIME].value = time;
4326       game_panel_controls[GAME_PANEL_SCORE].value = score;
4327
4328       DisplayGameControlValues();
4329     }
4330
4331     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4332     {
4333       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4334       {
4335         /* close exit door after last player */
4336         if ((AllPlayersGone &&
4337              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4338               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4339               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4340             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4341             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4342         {
4343           int element = Feld[ExitX][ExitY];
4344
4345           Feld[ExitX][ExitY] =
4346             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4347              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4348              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4349              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4350              EL_EM_STEEL_EXIT_CLOSING);
4351
4352           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4353         }
4354
4355         /* player disappears */
4356         DrawLevelField(ExitX, ExitY);
4357       }
4358
4359       for (i = 0; i < MAX_PLAYERS; i++)
4360       {
4361         struct PlayerInfo *player = &stored_player[i];
4362
4363         if (player->present)
4364         {
4365           RemovePlayer(player);
4366
4367           /* player disappears */
4368           DrawLevelField(player->jx, player->jy);
4369         }
4370       }
4371     }
4372
4373     PlaySound(SND_GAME_WINNING);
4374   }
4375
4376   if (game_over_delay_1 > 0)
4377   {
4378     game_over_delay_1--;
4379
4380     return;
4381   }
4382
4383   if (time != time_final)
4384   {
4385     int time_to_go = ABS(time_final - time);
4386     int time_count_dir = (time < time_final ? +1 : -1);
4387     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4388
4389     time  += time_count_steps * time_count_dir;
4390     score += time_count_steps * level.score[SC_TIME_BONUS];
4391
4392     local_player->LevelSolved_CountingTime = time;
4393     local_player->LevelSolved_CountingScore = score;
4394
4395     game_panel_controls[GAME_PANEL_TIME].value = time;
4396     game_panel_controls[GAME_PANEL_SCORE].value = score;
4397
4398     DisplayGameControlValues();
4399
4400     if (time == time_final)
4401       StopSound(SND_GAME_LEVELTIME_BONUS);
4402     else if (setup.sound_loops)
4403       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4404     else
4405       PlaySound(SND_GAME_LEVELTIME_BONUS);
4406
4407     return;
4408   }
4409
4410   local_player->LevelSolved_PanelOff = TRUE;
4411
4412   if (game_over_delay_2 > 0)
4413   {
4414     game_over_delay_2--;
4415
4416     return;
4417   }
4418
4419   GameEnd();
4420 }
4421
4422 void GameEnd()
4423 {
4424   int hi_pos;
4425   boolean raise_level = FALSE;
4426
4427   local_player->LevelSolved_GameEnd = TRUE;
4428
4429   if (!global.use_envelope_request)
4430     CloseDoor(DOOR_CLOSE_1);
4431
4432   if (local_player->LevelSolved_SaveTape)
4433   {
4434     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4435   }
4436
4437   CloseDoor(DOOR_CLOSE_ALL);
4438
4439   if (level_editor_test_game)
4440   {
4441     SetGameStatus(GAME_MODE_MAIN);
4442
4443     DrawMainMenu();
4444
4445     return;
4446   }
4447
4448   if (!local_player->LevelSolved_SaveScore)
4449   {
4450     SetGameStatus(GAME_MODE_MAIN);
4451
4452     DrawMainMenu();
4453
4454     return;
4455   }
4456
4457   if (level_nr == leveldir_current->handicap_level)
4458   {
4459     leveldir_current->handicap_level++;
4460
4461     SaveLevelSetup_SeriesInfo();
4462   }
4463
4464   if (level_nr < leveldir_current->last_level)
4465     raise_level = TRUE;                 /* advance to next level */
4466
4467   if ((hi_pos = NewHiScore()) >= 0) 
4468   {
4469     SetGameStatus(GAME_MODE_SCORES);
4470
4471     DrawHallOfFame(hi_pos);
4472
4473     if (raise_level)
4474     {
4475       level_nr++;
4476       TapeErase();
4477     }
4478   }
4479   else
4480   {
4481     SetGameStatus(GAME_MODE_MAIN);
4482
4483     if (raise_level)
4484     {
4485       level_nr++;
4486       TapeErase();
4487     }
4488
4489     DrawMainMenu();
4490   }
4491 }
4492
4493 int NewHiScore()
4494 {
4495   int k, l;
4496   int position = -1;
4497
4498   LoadScore(level_nr);
4499
4500   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4501       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4502     return -1;
4503
4504   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4505   {
4506     if (local_player->score_final > highscore[k].Score)
4507     {
4508       /* player has made it to the hall of fame */
4509
4510       if (k < MAX_SCORE_ENTRIES - 1)
4511       {
4512         int m = MAX_SCORE_ENTRIES - 1;
4513
4514 #ifdef ONE_PER_NAME
4515         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4516           if (strEqual(setup.player_name, highscore[l].Name))
4517             m = l;
4518         if (m == k)     /* player's new highscore overwrites his old one */
4519           goto put_into_list;
4520 #endif
4521
4522         for (l = m; l > k; l--)
4523         {
4524           strcpy(highscore[l].Name, highscore[l - 1].Name);
4525           highscore[l].Score = highscore[l - 1].Score;
4526         }
4527       }
4528
4529 #ifdef ONE_PER_NAME
4530       put_into_list:
4531 #endif
4532       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4533       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4534       highscore[k].Score = local_player->score_final; 
4535       position = k;
4536       break;
4537     }
4538
4539 #ifdef ONE_PER_NAME
4540     else if (!strncmp(setup.player_name, highscore[k].Name,
4541                       MAX_PLAYER_NAME_LEN))
4542       break;    /* player already there with a higher score */
4543 #endif
4544
4545   }
4546
4547   if (position >= 0) 
4548     SaveScore(level_nr);
4549
4550   return position;
4551 }
4552
4553 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4554 {
4555   int element = Feld[x][y];
4556   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4557   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4558   int horiz_move = (dx != 0);
4559   int sign = (horiz_move ? dx : dy);
4560   int step = sign * element_info[element].move_stepsize;
4561
4562   /* special values for move stepsize for spring and things on conveyor belt */
4563   if (horiz_move)
4564   {
4565     if (CAN_FALL(element) &&
4566         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4567       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4568     else if (element == EL_SPRING)
4569       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4570   }
4571
4572   return step;
4573 }
4574
4575 inline static int getElementMoveStepsize(int x, int y)
4576 {
4577   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4578 }
4579
4580 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4581 {
4582   if (player->GfxAction != action || player->GfxDir != dir)
4583   {
4584     player->GfxAction = action;
4585     player->GfxDir = dir;
4586     player->Frame = 0;
4587     player->StepFrame = 0;
4588   }
4589 }
4590
4591 static void ResetGfxFrame(int x, int y)
4592 {
4593   int element = Feld[x][y];
4594   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4595
4596   if (graphic_info[graphic].anim_global_sync)
4597     GfxFrame[x][y] = FrameCounter;
4598   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4599     GfxFrame[x][y] = CustomValue[x][y];
4600   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4601     GfxFrame[x][y] = element_info[element].collect_score;
4602   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4603     GfxFrame[x][y] = ChangeDelay[x][y];
4604 }
4605
4606 static void ResetGfxAnimation(int x, int y)
4607 {
4608   GfxAction[x][y] = ACTION_DEFAULT;
4609   GfxDir[x][y] = MovDir[x][y];
4610   GfxFrame[x][y] = 0;
4611
4612   ResetGfxFrame(x, y);
4613 }
4614
4615 static void ResetRandomAnimationValue(int x, int y)
4616 {
4617   GfxRandom[x][y] = INIT_GFX_RANDOM();
4618 }
4619
4620 void InitMovingField(int x, int y, int direction)
4621 {
4622   int element = Feld[x][y];
4623   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4624   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4625   int newx = x + dx;
4626   int newy = y + dy;
4627   boolean is_moving_before, is_moving_after;
4628
4629   /* check if element was/is moving or being moved before/after mode change */
4630   is_moving_before = (WasJustMoving[x][y] != 0);
4631   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4632
4633   /* reset animation only for moving elements which change direction of moving
4634      or which just started or stopped moving
4635      (else CEs with property "can move" / "not moving" are reset each frame) */
4636   if (is_moving_before != is_moving_after ||
4637       direction != MovDir[x][y])
4638     ResetGfxAnimation(x, y);
4639
4640   MovDir[x][y] = direction;
4641   GfxDir[x][y] = direction;
4642
4643   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4644                      direction == MV_DOWN && CAN_FALL(element) ?
4645                      ACTION_FALLING : ACTION_MOVING);
4646
4647   /* this is needed for CEs with property "can move" / "not moving" */
4648
4649   if (is_moving_after)
4650   {
4651     if (Feld[newx][newy] == EL_EMPTY)
4652       Feld[newx][newy] = EL_BLOCKED;
4653
4654     MovDir[newx][newy] = MovDir[x][y];
4655
4656     CustomValue[newx][newy] = CustomValue[x][y];
4657
4658     GfxFrame[newx][newy] = GfxFrame[x][y];
4659     GfxRandom[newx][newy] = GfxRandom[x][y];
4660     GfxAction[newx][newy] = GfxAction[x][y];
4661     GfxDir[newx][newy] = GfxDir[x][y];
4662   }
4663 }
4664
4665 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4666 {
4667   int direction = MovDir[x][y];
4668   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4669   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4670
4671   *goes_to_x = newx;
4672   *goes_to_y = newy;
4673 }
4674
4675 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4676 {
4677   int oldx = x, oldy = y;
4678   int direction = MovDir[x][y];
4679
4680   if (direction == MV_LEFT)
4681     oldx++;
4682   else if (direction == MV_RIGHT)
4683     oldx--;
4684   else if (direction == MV_UP)
4685     oldy++;
4686   else if (direction == MV_DOWN)
4687     oldy--;
4688
4689   *comes_from_x = oldx;
4690   *comes_from_y = oldy;
4691 }
4692
4693 int MovingOrBlocked2Element(int x, int y)
4694 {
4695   int element = Feld[x][y];
4696
4697   if (element == EL_BLOCKED)
4698   {
4699     int oldx, oldy;
4700
4701     Blocked2Moving(x, y, &oldx, &oldy);
4702     return Feld[oldx][oldy];
4703   }
4704   else
4705     return element;
4706 }
4707
4708 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4709 {
4710   /* like MovingOrBlocked2Element(), but if element is moving
4711      and (x,y) is the field the moving element is just leaving,
4712      return EL_BLOCKED instead of the element value */
4713   int element = Feld[x][y];
4714
4715   if (IS_MOVING(x, y))
4716   {
4717     if (element == EL_BLOCKED)
4718     {
4719       int oldx, oldy;
4720
4721       Blocked2Moving(x, y, &oldx, &oldy);
4722       return Feld[oldx][oldy];
4723     }
4724     else
4725       return EL_BLOCKED;
4726   }
4727   else
4728     return element;
4729 }
4730
4731 static void RemoveField(int x, int y)
4732 {
4733   Feld[x][y] = EL_EMPTY;
4734
4735   MovPos[x][y] = 0;
4736   MovDir[x][y] = 0;
4737   MovDelay[x][y] = 0;
4738
4739   CustomValue[x][y] = 0;
4740
4741   AmoebaNr[x][y] = 0;
4742   ChangeDelay[x][y] = 0;
4743   ChangePage[x][y] = -1;
4744   Pushed[x][y] = FALSE;
4745
4746   GfxElement[x][y] = EL_UNDEFINED;
4747   GfxAction[x][y] = ACTION_DEFAULT;
4748   GfxDir[x][y] = MV_NONE;
4749 }
4750
4751 void RemoveMovingField(int x, int y)
4752 {
4753   int oldx = x, oldy = y, newx = x, newy = y;
4754   int element = Feld[x][y];
4755   int next_element = EL_UNDEFINED;
4756
4757   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4758     return;
4759
4760   if (IS_MOVING(x, y))
4761   {
4762     Moving2Blocked(x, y, &newx, &newy);
4763
4764     if (Feld[newx][newy] != EL_BLOCKED)
4765     {
4766       /* element is moving, but target field is not free (blocked), but
4767          already occupied by something different (example: acid pool);
4768          in this case, only remove the moving field, but not the target */
4769
4770       RemoveField(oldx, oldy);
4771
4772       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4773
4774       TEST_DrawLevelField(oldx, oldy);
4775
4776       return;
4777     }
4778   }
4779   else if (element == EL_BLOCKED)
4780   {
4781     Blocked2Moving(x, y, &oldx, &oldy);
4782     if (!IS_MOVING(oldx, oldy))
4783       return;
4784   }
4785
4786   if (element == EL_BLOCKED &&
4787       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4788        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4789        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4790        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4791        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4792        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4793     next_element = get_next_element(Feld[oldx][oldy]);
4794
4795   RemoveField(oldx, oldy);
4796   RemoveField(newx, newy);
4797
4798   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4799
4800   if (next_element != EL_UNDEFINED)
4801     Feld[oldx][oldy] = next_element;
4802
4803   TEST_DrawLevelField(oldx, oldy);
4804   TEST_DrawLevelField(newx, newy);
4805 }
4806
4807 void DrawDynamite(int x, int y)
4808 {
4809   int sx = SCREENX(x), sy = SCREENY(y);
4810   int graphic = el2img(Feld[x][y]);
4811   int frame;
4812
4813   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4814     return;
4815
4816   if (IS_WALKABLE_INSIDE(Back[x][y]))
4817     return;
4818
4819   if (Back[x][y])
4820     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4821   else if (Store[x][y])
4822     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4823
4824   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4825
4826   if (Back[x][y] || Store[x][y])
4827     DrawGraphicThruMask(sx, sy, graphic, frame);
4828   else
4829     DrawGraphic(sx, sy, graphic, frame);
4830 }
4831
4832 void CheckDynamite(int x, int y)
4833 {
4834   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4835   {
4836     MovDelay[x][y]--;
4837
4838     if (MovDelay[x][y] != 0)
4839     {
4840       DrawDynamite(x, y);
4841       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4842
4843       return;
4844     }
4845   }
4846
4847   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4848
4849   Bang(x, y);
4850 }
4851
4852 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4853 {
4854   boolean num_checked_players = 0;
4855   int i;
4856
4857   for (i = 0; i < MAX_PLAYERS; i++)
4858   {
4859     if (stored_player[i].active)
4860     {
4861       int sx = stored_player[i].jx;
4862       int sy = stored_player[i].jy;
4863
4864       if (num_checked_players == 0)
4865       {
4866         *sx1 = *sx2 = sx;
4867         *sy1 = *sy2 = sy;
4868       }
4869       else
4870       {
4871         *sx1 = MIN(*sx1, sx);
4872         *sy1 = MIN(*sy1, sy);
4873         *sx2 = MAX(*sx2, sx);
4874         *sy2 = MAX(*sy2, sy);
4875       }
4876
4877       num_checked_players++;
4878     }
4879   }
4880 }
4881
4882 static boolean checkIfAllPlayersFitToScreen_RND()
4883 {
4884   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4885
4886   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4887
4888   return (sx2 - sx1 < SCR_FIELDX &&
4889           sy2 - sy1 < SCR_FIELDY);
4890 }
4891
4892 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4893 {
4894   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4895
4896   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4897
4898   *sx = (sx1 + sx2) / 2;
4899   *sy = (sy1 + sy2) / 2;
4900 }
4901
4902 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4903                         boolean center_screen, boolean quick_relocation)
4904 {
4905   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4906   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4907   boolean no_delay = (tape.warp_forward);
4908   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4909   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4910   int new_scroll_x, new_scroll_y;
4911
4912   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4913   {
4914     /* case 1: quick relocation inside visible screen (without scrolling) */
4915
4916     RedrawPlayfield();
4917
4918     return;
4919   }
4920
4921   if (!level.shifted_relocation || center_screen)
4922   {
4923     /* relocation _with_ centering of screen */
4924
4925     new_scroll_x = SCROLL_POSITION_X(x);
4926     new_scroll_y = SCROLL_POSITION_Y(y);
4927   }
4928   else
4929   {
4930     /* relocation _without_ centering of screen */
4931
4932     int center_scroll_x = SCROLL_POSITION_X(old_x);
4933     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4934     int offset_x = x + (scroll_x - center_scroll_x);
4935     int offset_y = y + (scroll_y - center_scroll_y);
4936
4937     /* for new screen position, apply previous offset to center position */
4938     new_scroll_x = SCROLL_POSITION_X(offset_x);
4939     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4940   }
4941
4942   if (quick_relocation)
4943   {
4944     /* case 2: quick relocation (redraw without visible scrolling) */
4945
4946     scroll_x = new_scroll_x;
4947     scroll_y = new_scroll_y;
4948
4949     RedrawPlayfield();
4950
4951     return;
4952   }
4953
4954   /* case 3: visible relocation (with scrolling to new position) */
4955
4956   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4957
4958   SetVideoFrameDelay(wait_delay_value);
4959
4960   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4961   {
4962     int dx = 0, dy = 0;
4963     int fx = FX, fy = FY;
4964
4965     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4966     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4967
4968     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4969       break;
4970
4971     scroll_x -= dx;
4972     scroll_y -= dy;
4973
4974     fx += dx * TILEX / 2;
4975     fy += dy * TILEY / 2;
4976
4977     ScrollLevel(dx, dy);
4978     DrawAllPlayers();
4979
4980     /* scroll in two steps of half tile size to make things smoother */
4981     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4982
4983     /* scroll second step to align at full tile size */
4984     BlitScreenToBitmap(window);
4985   }
4986
4987   DrawAllPlayers();
4988   BackToFront();
4989
4990   SetVideoFrameDelay(frame_delay_value_old);
4991 }
4992
4993 void RelocatePlayer(int jx, int jy, int el_player_raw)
4994 {
4995   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4996   int player_nr = GET_PLAYER_NR(el_player);
4997   struct PlayerInfo *player = &stored_player[player_nr];
4998   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4999   boolean no_delay = (tape.warp_forward);
5000   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5001   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5002   int old_jx = player->jx;
5003   int old_jy = player->jy;
5004   int old_element = Feld[old_jx][old_jy];
5005   int element = Feld[jx][jy];
5006   boolean player_relocated = (old_jx != jx || old_jy != jy);
5007
5008   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5009   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5010   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5011   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5012   int leave_side_horiz = move_dir_horiz;
5013   int leave_side_vert  = move_dir_vert;
5014   int enter_side = enter_side_horiz | enter_side_vert;
5015   int leave_side = leave_side_horiz | leave_side_vert;
5016
5017   if (player->GameOver)         /* do not reanimate dead player */
5018     return;
5019
5020   if (!player_relocated)        /* no need to relocate the player */
5021     return;
5022
5023   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5024   {
5025     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5026     DrawLevelField(jx, jy);
5027   }
5028
5029   if (player->present)
5030   {
5031     while (player->MovPos)
5032     {
5033       ScrollPlayer(player, SCROLL_GO_ON);
5034       ScrollScreen(NULL, SCROLL_GO_ON);
5035
5036       AdvanceFrameAndPlayerCounters(player->index_nr);
5037
5038       DrawPlayer(player);
5039
5040       BackToFront_WithFrameDelay(wait_delay_value);
5041     }
5042
5043     DrawPlayer(player);         /* needed here only to cleanup last field */
5044     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5045
5046     player->is_moving = FALSE;
5047   }
5048
5049   if (IS_CUSTOM_ELEMENT(old_element))
5050     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5051                                CE_LEFT_BY_PLAYER,
5052                                player->index_bit, leave_side);
5053
5054   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5055                                       CE_PLAYER_LEAVES_X,
5056                                       player->index_bit, leave_side);
5057
5058   Feld[jx][jy] = el_player;
5059   InitPlayerField(jx, jy, el_player, TRUE);
5060
5061   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5062      possible that the relocation target field did not contain a player element,
5063      but a walkable element, to which the new player was relocated -- in this
5064      case, restore that (already initialized!) element on the player field */
5065   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5066   {
5067     Feld[jx][jy] = element;     /* restore previously existing element */
5068   }
5069
5070   /* only visually relocate centered player */
5071   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5072                      FALSE, level.instant_relocation);
5073
5074   TestIfPlayerTouchesBadThing(jx, jy);
5075   TestIfPlayerTouchesCustomElement(jx, jy);
5076
5077   if (IS_CUSTOM_ELEMENT(element))
5078     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5079                                player->index_bit, enter_side);
5080
5081   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5082                                       player->index_bit, enter_side);
5083
5084   if (player->is_switching)
5085   {
5086     /* ensure that relocation while still switching an element does not cause
5087        a new element to be treated as also switched directly after relocation
5088        (this is important for teleporter switches that teleport the player to
5089        a place where another teleporter switch is in the same direction, which
5090        would then incorrectly be treated as immediately switched before the
5091        direction key that caused the switch was released) */
5092
5093     player->switch_x += jx - old_jx;
5094     player->switch_y += jy - old_jy;
5095   }
5096 }
5097
5098 void Explode(int ex, int ey, int phase, int mode)
5099 {
5100   int x, y;
5101   int last_phase;
5102   int border_element;
5103
5104   /* !!! eliminate this variable !!! */
5105   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5106
5107   if (game.explosions_delayed)
5108   {
5109     ExplodeField[ex][ey] = mode;
5110     return;
5111   }
5112
5113   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5114   {
5115     int center_element = Feld[ex][ey];
5116     int artwork_element, explosion_element;     /* set these values later */
5117
5118     /* remove things displayed in background while burning dynamite */
5119     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5120       Back[ex][ey] = 0;
5121
5122     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5123     {
5124       /* put moving element to center field (and let it explode there) */
5125       center_element = MovingOrBlocked2Element(ex, ey);
5126       RemoveMovingField(ex, ey);
5127       Feld[ex][ey] = center_element;
5128     }
5129
5130     /* now "center_element" is finally determined -- set related values now */
5131     artwork_element = center_element;           /* for custom player artwork */
5132     explosion_element = center_element;         /* for custom player artwork */
5133
5134     if (IS_PLAYER(ex, ey))
5135     {
5136       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5137
5138       artwork_element = stored_player[player_nr].artwork_element;
5139
5140       if (level.use_explosion_element[player_nr])
5141       {
5142         explosion_element = level.explosion_element[player_nr];
5143         artwork_element = explosion_element;
5144       }
5145     }
5146
5147     if (mode == EX_TYPE_NORMAL ||
5148         mode == EX_TYPE_CENTER ||
5149         mode == EX_TYPE_CROSS)
5150       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5151
5152     last_phase = element_info[explosion_element].explosion_delay + 1;
5153
5154     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5155     {
5156       int xx = x - ex + 1;
5157       int yy = y - ey + 1;
5158       int element;
5159
5160       if (!IN_LEV_FIELD(x, y) ||
5161           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5162           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5163         continue;
5164
5165       element = Feld[x][y];
5166
5167       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5168       {
5169         element = MovingOrBlocked2Element(x, y);
5170
5171         if (!IS_EXPLOSION_PROOF(element))
5172           RemoveMovingField(x, y);
5173       }
5174
5175       /* indestructible elements can only explode in center (but not flames) */
5176       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5177                                            mode == EX_TYPE_BORDER)) ||
5178           element == EL_FLAMES)
5179         continue;
5180
5181       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5182          behaviour, for example when touching a yamyam that explodes to rocks
5183          with active deadly shield, a rock is created under the player !!! */
5184       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5185 #if 0
5186       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5187           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5188            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5189 #else
5190       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5191 #endif
5192       {
5193         if (IS_ACTIVE_BOMB(element))
5194         {
5195           /* re-activate things under the bomb like gate or penguin */
5196           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5197           Back[x][y] = 0;
5198         }
5199
5200         continue;
5201       }
5202
5203       /* save walkable background elements while explosion on same tile */
5204       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5205           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5206         Back[x][y] = element;
5207
5208       /* ignite explodable elements reached by other explosion */
5209       if (element == EL_EXPLOSION)
5210         element = Store2[x][y];
5211
5212       if (AmoebaNr[x][y] &&
5213           (element == EL_AMOEBA_FULL ||
5214            element == EL_BD_AMOEBA ||
5215            element == EL_AMOEBA_GROWING))
5216       {
5217         AmoebaCnt[AmoebaNr[x][y]]--;
5218         AmoebaCnt2[AmoebaNr[x][y]]--;
5219       }
5220
5221       RemoveField(x, y);
5222
5223       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5224       {
5225         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5226
5227         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5228
5229         if (PLAYERINFO(ex, ey)->use_murphy)
5230           Store[x][y] = EL_EMPTY;
5231       }
5232
5233       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5234          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5235       else if (ELEM_IS_PLAYER(center_element))
5236         Store[x][y] = EL_EMPTY;
5237       else if (center_element == EL_YAMYAM)
5238         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5239       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5240         Store[x][y] = element_info[center_element].content.e[xx][yy];
5241 #if 1
5242       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5243          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5244          otherwise) -- FIX THIS !!! */
5245       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5246         Store[x][y] = element_info[element].content.e[1][1];
5247 #else
5248       else if (!CAN_EXPLODE(element))
5249         Store[x][y] = element_info[element].content.e[1][1];
5250 #endif
5251       else
5252         Store[x][y] = EL_EMPTY;
5253
5254       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5255           center_element == EL_AMOEBA_TO_DIAMOND)
5256         Store2[x][y] = element;
5257
5258       Feld[x][y] = EL_EXPLOSION;
5259       GfxElement[x][y] = artwork_element;
5260
5261       ExplodePhase[x][y] = 1;
5262       ExplodeDelay[x][y] = last_phase;
5263
5264       Stop[x][y] = TRUE;
5265     }
5266
5267     if (center_element == EL_YAMYAM)
5268       game.yamyam_content_nr =
5269         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5270
5271     return;
5272   }
5273
5274   if (Stop[ex][ey])
5275     return;
5276
5277   x = ex;
5278   y = ey;
5279
5280   if (phase == 1)
5281     GfxFrame[x][y] = 0;         /* restart explosion animation */
5282
5283   last_phase = ExplodeDelay[x][y];
5284
5285   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5286
5287   /* this can happen if the player leaves an explosion just in time */
5288   if (GfxElement[x][y] == EL_UNDEFINED)
5289     GfxElement[x][y] = EL_EMPTY;
5290
5291   border_element = Store2[x][y];
5292   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5293     border_element = StorePlayer[x][y];
5294
5295   if (phase == element_info[border_element].ignition_delay ||
5296       phase == last_phase)
5297   {
5298     boolean border_explosion = FALSE;
5299
5300     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5301         !PLAYER_EXPLOSION_PROTECTED(x, y))
5302     {
5303       KillPlayerUnlessExplosionProtected(x, y);
5304       border_explosion = TRUE;
5305     }
5306     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5307     {
5308       Feld[x][y] = Store2[x][y];
5309       Store2[x][y] = 0;
5310       Bang(x, y);
5311       border_explosion = TRUE;
5312     }
5313     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5314     {
5315       AmoebeUmwandeln(x, y);
5316       Store2[x][y] = 0;
5317       border_explosion = TRUE;
5318     }
5319
5320     /* if an element just explodes due to another explosion (chain-reaction),
5321        do not immediately end the new explosion when it was the last frame of
5322        the explosion (as it would be done in the following "if"-statement!) */
5323     if (border_explosion && phase == last_phase)
5324       return;
5325   }
5326
5327   if (phase == last_phase)
5328   {
5329     int element;
5330
5331     element = Feld[x][y] = Store[x][y];
5332     Store[x][y] = Store2[x][y] = 0;
5333     GfxElement[x][y] = EL_UNDEFINED;
5334
5335     /* player can escape from explosions and might therefore be still alive */
5336     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5337         element <= EL_PLAYER_IS_EXPLODING_4)
5338     {
5339       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5340       int explosion_element = EL_PLAYER_1 + player_nr;
5341       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5342       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5343
5344       if (level.use_explosion_element[player_nr])
5345         explosion_element = level.explosion_element[player_nr];
5346
5347       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5348                     element_info[explosion_element].content.e[xx][yy]);
5349     }
5350
5351     /* restore probably existing indestructible background element */
5352     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5353       element = Feld[x][y] = Back[x][y];
5354     Back[x][y] = 0;
5355
5356     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5357     GfxDir[x][y] = MV_NONE;
5358     ChangeDelay[x][y] = 0;
5359     ChangePage[x][y] = -1;
5360
5361     CustomValue[x][y] = 0;
5362
5363     InitField_WithBug2(x, y, FALSE);
5364
5365     TEST_DrawLevelField(x, y);
5366
5367     TestIfElementTouchesCustomElement(x, y);
5368
5369     if (GFX_CRUMBLED(element))
5370       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5371
5372     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5373       StorePlayer[x][y] = 0;
5374
5375     if (ELEM_IS_PLAYER(element))
5376       RelocatePlayer(x, y, element);
5377   }
5378   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5379   {
5380     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5381     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5382
5383     if (phase == delay)
5384       TEST_DrawLevelFieldCrumbled(x, y);
5385
5386     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5387     {
5388       DrawLevelElement(x, y, Back[x][y]);
5389       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5390     }
5391     else if (IS_WALKABLE_UNDER(Back[x][y]))
5392     {
5393       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5394       DrawLevelElementThruMask(x, y, Back[x][y]);
5395     }
5396     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5397       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5398   }
5399 }
5400
5401 void DynaExplode(int ex, int ey)
5402 {
5403   int i, j;
5404   int dynabomb_element = Feld[ex][ey];
5405   int dynabomb_size = 1;
5406   boolean dynabomb_xl = FALSE;
5407   struct PlayerInfo *player;
5408   static int xy[4][2] =
5409   {
5410     { 0, -1 },
5411     { -1, 0 },
5412     { +1, 0 },
5413     { 0, +1 }
5414   };
5415
5416   if (IS_ACTIVE_BOMB(dynabomb_element))
5417   {
5418     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5419     dynabomb_size = player->dynabomb_size;
5420     dynabomb_xl = player->dynabomb_xl;
5421     player->dynabombs_left++;
5422   }
5423
5424   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5425
5426   for (i = 0; i < NUM_DIRECTIONS; i++)
5427   {
5428     for (j = 1; j <= dynabomb_size; j++)
5429     {
5430       int x = ex + j * xy[i][0];
5431       int y = ey + j * xy[i][1];
5432       int element;
5433
5434       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5435         break;
5436
5437       element = Feld[x][y];
5438
5439       /* do not restart explosions of fields with active bombs */
5440       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5441         continue;
5442
5443       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5444
5445       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5446           !IS_DIGGABLE(element) && !dynabomb_xl)
5447         break;
5448     }
5449   }
5450 }
5451
5452 void Bang(int x, int y)
5453 {
5454   int element = MovingOrBlocked2Element(x, y);
5455   int explosion_type = EX_TYPE_NORMAL;
5456
5457   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5458   {
5459     struct PlayerInfo *player = PLAYERINFO(x, y);
5460
5461     element = Feld[x][y] = player->initial_element;
5462
5463     if (level.use_explosion_element[player->index_nr])
5464     {
5465       int explosion_element = level.explosion_element[player->index_nr];
5466
5467       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5468         explosion_type = EX_TYPE_CROSS;
5469       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5470         explosion_type = EX_TYPE_CENTER;
5471     }
5472   }
5473
5474   switch (element)
5475   {
5476     case EL_BUG:
5477     case EL_SPACESHIP:
5478     case EL_BD_BUTTERFLY:
5479     case EL_BD_FIREFLY:
5480     case EL_YAMYAM:
5481     case EL_DARK_YAMYAM:
5482     case EL_ROBOT:
5483     case EL_PACMAN:
5484     case EL_MOLE:
5485       RaiseScoreElement(element);
5486       break;
5487
5488     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5489     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5490     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5491     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5492     case EL_DYNABOMB_INCREASE_NUMBER:
5493     case EL_DYNABOMB_INCREASE_SIZE:
5494     case EL_DYNABOMB_INCREASE_POWER:
5495       explosion_type = EX_TYPE_DYNA;
5496       break;
5497
5498     case EL_DC_LANDMINE:
5499       explosion_type = EX_TYPE_CENTER;
5500       break;
5501
5502     case EL_PENGUIN:
5503     case EL_LAMP:
5504     case EL_LAMP_ACTIVE:
5505     case EL_AMOEBA_TO_DIAMOND:
5506       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5507         explosion_type = EX_TYPE_CENTER;
5508       break;
5509
5510     default:
5511       if (element_info[element].explosion_type == EXPLODES_CROSS)
5512         explosion_type = EX_TYPE_CROSS;
5513       else if (element_info[element].explosion_type == EXPLODES_1X1)
5514         explosion_type = EX_TYPE_CENTER;
5515       break;
5516   }
5517
5518   if (explosion_type == EX_TYPE_DYNA)
5519     DynaExplode(x, y);
5520   else
5521     Explode(x, y, EX_PHASE_START, explosion_type);
5522
5523   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5524 }
5525
5526 void SplashAcid(int x, int y)
5527 {
5528   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5529       (!IN_LEV_FIELD(x - 1, y - 2) ||
5530        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5531     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5532
5533   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5534       (!IN_LEV_FIELD(x + 1, y - 2) ||
5535        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5536     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5537
5538   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5539 }
5540
5541 static void InitBeltMovement()
5542 {
5543   static int belt_base_element[4] =
5544   {
5545     EL_CONVEYOR_BELT_1_LEFT,
5546     EL_CONVEYOR_BELT_2_LEFT,
5547     EL_CONVEYOR_BELT_3_LEFT,
5548     EL_CONVEYOR_BELT_4_LEFT
5549   };
5550   static int belt_base_active_element[4] =
5551   {
5552     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5553     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5554     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5555     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5556   };
5557
5558   int x, y, i, j;
5559
5560   /* set frame order for belt animation graphic according to belt direction */
5561   for (i = 0; i < NUM_BELTS; i++)
5562   {
5563     int belt_nr = i;
5564
5565     for (j = 0; j < NUM_BELT_PARTS; j++)
5566     {
5567       int element = belt_base_active_element[belt_nr] + j;
5568       int graphic_1 = el2img(element);
5569       int graphic_2 = el2panelimg(element);
5570
5571       if (game.belt_dir[i] == MV_LEFT)
5572       {
5573         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5574         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5575       }
5576       else
5577       {
5578         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5579         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5580       }
5581     }
5582   }
5583
5584   SCAN_PLAYFIELD(x, y)
5585   {
5586     int element = Feld[x][y];
5587
5588     for (i = 0; i < NUM_BELTS; i++)
5589     {
5590       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5591       {
5592         int e_belt_nr = getBeltNrFromBeltElement(element);
5593         int belt_nr = i;
5594
5595         if (e_belt_nr == belt_nr)
5596         {
5597           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5598
5599           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5600         }
5601       }
5602     }
5603   }
5604 }
5605
5606 static void ToggleBeltSwitch(int x, int y)
5607 {
5608   static int belt_base_element[4] =
5609   {
5610     EL_CONVEYOR_BELT_1_LEFT,
5611     EL_CONVEYOR_BELT_2_LEFT,
5612     EL_CONVEYOR_BELT_3_LEFT,
5613     EL_CONVEYOR_BELT_4_LEFT
5614   };
5615   static int belt_base_active_element[4] =
5616   {
5617     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5618     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5619     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5620     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5621   };
5622   static int belt_base_switch_element[4] =
5623   {
5624     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5625     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5626     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5627     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5628   };
5629   static int belt_move_dir[4] =
5630   {
5631     MV_LEFT,
5632     MV_NONE,
5633     MV_RIGHT,
5634     MV_NONE,
5635   };
5636
5637   int element = Feld[x][y];
5638   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5639   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5640   int belt_dir = belt_move_dir[belt_dir_nr];
5641   int xx, yy, i;
5642
5643   if (!IS_BELT_SWITCH(element))
5644     return;
5645
5646   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5647   game.belt_dir[belt_nr] = belt_dir;
5648
5649   if (belt_dir_nr == 3)
5650     belt_dir_nr = 1;
5651
5652   /* set frame order for belt animation graphic according to belt direction */
5653   for (i = 0; i < NUM_BELT_PARTS; i++)
5654   {
5655     int element = belt_base_active_element[belt_nr] + i;
5656     int graphic_1 = el2img(element);
5657     int graphic_2 = el2panelimg(element);
5658
5659     if (belt_dir == MV_LEFT)
5660     {
5661       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5662       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5663     }
5664     else
5665     {
5666       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5667       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5668     }
5669   }
5670
5671   SCAN_PLAYFIELD(xx, yy)
5672   {
5673     int element = Feld[xx][yy];
5674
5675     if (IS_BELT_SWITCH(element))
5676     {
5677       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5678
5679       if (e_belt_nr == belt_nr)
5680       {
5681         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5682         TEST_DrawLevelField(xx, yy);
5683       }
5684     }
5685     else if (IS_BELT(element) && belt_dir != MV_NONE)
5686     {
5687       int e_belt_nr = getBeltNrFromBeltElement(element);
5688
5689       if (e_belt_nr == belt_nr)
5690       {
5691         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5692
5693         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5694         TEST_DrawLevelField(xx, yy);
5695       }
5696     }
5697     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5698     {
5699       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5700
5701       if (e_belt_nr == belt_nr)
5702       {
5703         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5704
5705         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5706         TEST_DrawLevelField(xx, yy);
5707       }
5708     }
5709   }
5710 }
5711
5712 static void ToggleSwitchgateSwitch(int x, int y)
5713 {
5714   int xx, yy;
5715
5716   game.switchgate_pos = !game.switchgate_pos;
5717
5718   SCAN_PLAYFIELD(xx, yy)
5719   {
5720     int element = Feld[xx][yy];
5721
5722     if (element == EL_SWITCHGATE_SWITCH_UP)
5723     {
5724       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5725       TEST_DrawLevelField(xx, yy);
5726     }
5727     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5728     {
5729       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5730       TEST_DrawLevelField(xx, yy);
5731     }
5732     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5733     {
5734       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5735       TEST_DrawLevelField(xx, yy);
5736     }
5737     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5738     {
5739       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5740       TEST_DrawLevelField(xx, yy);
5741     }
5742     else if (element == EL_SWITCHGATE_OPEN ||
5743              element == EL_SWITCHGATE_OPENING)
5744     {
5745       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5746
5747       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5748     }
5749     else if (element == EL_SWITCHGATE_CLOSED ||
5750              element == EL_SWITCHGATE_CLOSING)
5751     {
5752       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5753
5754       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5755     }
5756   }
5757 }
5758
5759 static int getInvisibleActiveFromInvisibleElement(int element)
5760 {
5761   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5762           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5763           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5764           element);
5765 }
5766
5767 static int getInvisibleFromInvisibleActiveElement(int element)
5768 {
5769   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5770           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5771           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5772           element);
5773 }
5774
5775 static void RedrawAllLightSwitchesAndInvisibleElements()
5776 {
5777   int x, y;
5778
5779   SCAN_PLAYFIELD(x, y)
5780   {
5781     int element = Feld[x][y];
5782
5783     if (element == EL_LIGHT_SWITCH &&
5784         game.light_time_left > 0)
5785     {
5786       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5787       TEST_DrawLevelField(x, y);
5788     }
5789     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5790              game.light_time_left == 0)
5791     {
5792       Feld[x][y] = EL_LIGHT_SWITCH;
5793       TEST_DrawLevelField(x, y);
5794     }
5795     else if (element == EL_EMC_DRIPPER &&
5796              game.light_time_left > 0)
5797     {
5798       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5799       TEST_DrawLevelField(x, y);
5800     }
5801     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5802              game.light_time_left == 0)
5803     {
5804       Feld[x][y] = EL_EMC_DRIPPER;
5805       TEST_DrawLevelField(x, y);
5806     }
5807     else if (element == EL_INVISIBLE_STEELWALL ||
5808              element == EL_INVISIBLE_WALL ||
5809              element == EL_INVISIBLE_SAND)
5810     {
5811       if (game.light_time_left > 0)
5812         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5813
5814       TEST_DrawLevelField(x, y);
5815
5816       /* uncrumble neighbour fields, if needed */
5817       if (element == EL_INVISIBLE_SAND)
5818         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5819     }
5820     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5821              element == EL_INVISIBLE_WALL_ACTIVE ||
5822              element == EL_INVISIBLE_SAND_ACTIVE)
5823     {
5824       if (game.light_time_left == 0)
5825         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5826
5827       TEST_DrawLevelField(x, y);
5828
5829       /* re-crumble neighbour fields, if needed */
5830       if (element == EL_INVISIBLE_SAND)
5831         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5832     }
5833   }
5834 }
5835
5836 static void RedrawAllInvisibleElementsForLenses()
5837 {
5838   int x, y;
5839
5840   SCAN_PLAYFIELD(x, y)
5841   {
5842     int element = Feld[x][y];
5843
5844     if (element == EL_EMC_DRIPPER &&
5845         game.lenses_time_left > 0)
5846     {
5847       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5848       TEST_DrawLevelField(x, y);
5849     }
5850     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5851              game.lenses_time_left == 0)
5852     {
5853       Feld[x][y] = EL_EMC_DRIPPER;
5854       TEST_DrawLevelField(x, y);
5855     }
5856     else if (element == EL_INVISIBLE_STEELWALL ||
5857              element == EL_INVISIBLE_WALL ||
5858              element == EL_INVISIBLE_SAND)
5859     {
5860       if (game.lenses_time_left > 0)
5861         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5862
5863       TEST_DrawLevelField(x, y);
5864
5865       /* uncrumble neighbour fields, if needed */
5866       if (element == EL_INVISIBLE_SAND)
5867         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5868     }
5869     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5870              element == EL_INVISIBLE_WALL_ACTIVE ||
5871              element == EL_INVISIBLE_SAND_ACTIVE)
5872     {
5873       if (game.lenses_time_left == 0)
5874         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5875
5876       TEST_DrawLevelField(x, y);
5877
5878       /* re-crumble neighbour fields, if needed */
5879       if (element == EL_INVISIBLE_SAND)
5880         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5881     }
5882   }
5883 }
5884
5885 static void RedrawAllInvisibleElementsForMagnifier()
5886 {
5887   int x, y;
5888
5889   SCAN_PLAYFIELD(x, y)
5890   {
5891     int element = Feld[x][y];
5892
5893     if (element == EL_EMC_FAKE_GRASS &&
5894         game.magnify_time_left > 0)
5895     {
5896       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5897       TEST_DrawLevelField(x, y);
5898     }
5899     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5900              game.magnify_time_left == 0)
5901     {
5902       Feld[x][y] = EL_EMC_FAKE_GRASS;
5903       TEST_DrawLevelField(x, y);
5904     }
5905     else if (IS_GATE_GRAY(element) &&
5906              game.magnify_time_left > 0)
5907     {
5908       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5909                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5910                     IS_EM_GATE_GRAY(element) ?
5911                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5912                     IS_EMC_GATE_GRAY(element) ?
5913                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5914                     IS_DC_GATE_GRAY(element) ?
5915                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5916                     element);
5917       TEST_DrawLevelField(x, y);
5918     }
5919     else if (IS_GATE_GRAY_ACTIVE(element) &&
5920              game.magnify_time_left == 0)
5921     {
5922       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5923                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5924                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5925                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5926                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5927                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5928                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5929                     EL_DC_GATE_WHITE_GRAY :
5930                     element);
5931       TEST_DrawLevelField(x, y);
5932     }
5933   }
5934 }
5935
5936 static void ToggleLightSwitch(int x, int y)
5937 {
5938   int element = Feld[x][y];
5939
5940   game.light_time_left =
5941     (element == EL_LIGHT_SWITCH ?
5942      level.time_light * FRAMES_PER_SECOND : 0);
5943
5944   RedrawAllLightSwitchesAndInvisibleElements();
5945 }
5946
5947 static void ActivateTimegateSwitch(int x, int y)
5948 {
5949   int xx, yy;
5950
5951   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5952
5953   SCAN_PLAYFIELD(xx, yy)
5954   {
5955     int element = Feld[xx][yy];
5956
5957     if (element == EL_TIMEGATE_CLOSED ||
5958         element == EL_TIMEGATE_CLOSING)
5959     {
5960       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5961       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5962     }
5963
5964     /*
5965     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5966     {
5967       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5968       TEST_DrawLevelField(xx, yy);
5969     }
5970     */
5971
5972   }
5973
5974   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5975                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5976 }
5977
5978 void Impact(int x, int y)
5979 {
5980   boolean last_line = (y == lev_fieldy - 1);
5981   boolean object_hit = FALSE;
5982   boolean impact = (last_line || object_hit);
5983   int element = Feld[x][y];
5984   int smashed = EL_STEELWALL;
5985
5986   if (!last_line)       /* check if element below was hit */
5987   {
5988     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5989       return;
5990
5991     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5992                                          MovDir[x][y + 1] != MV_DOWN ||
5993                                          MovPos[x][y + 1] <= TILEY / 2));
5994
5995     /* do not smash moving elements that left the smashed field in time */
5996     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5997         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5998       object_hit = FALSE;
5999
6000 #if USE_QUICKSAND_IMPACT_BUGFIX
6001     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6002     {
6003       RemoveMovingField(x, y + 1);
6004       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6005       Feld[x][y + 2] = EL_ROCK;
6006       TEST_DrawLevelField(x, y + 2);
6007
6008       object_hit = TRUE;
6009     }
6010
6011     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6012     {
6013       RemoveMovingField(x, y + 1);
6014       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6015       Feld[x][y + 2] = EL_ROCK;
6016       TEST_DrawLevelField(x, y + 2);
6017
6018       object_hit = TRUE;
6019     }
6020 #endif
6021
6022     if (object_hit)
6023       smashed = MovingOrBlocked2Element(x, y + 1);
6024
6025     impact = (last_line || object_hit);
6026   }
6027
6028   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6029   {
6030     SplashAcid(x, y + 1);
6031     return;
6032   }
6033
6034   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6035   /* only reset graphic animation if graphic really changes after impact */
6036   if (impact &&
6037       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6038   {
6039     ResetGfxAnimation(x, y);
6040     TEST_DrawLevelField(x, y);
6041   }
6042
6043   if (impact && CAN_EXPLODE_IMPACT(element))
6044   {
6045     Bang(x, y);
6046     return;
6047   }
6048   else if (impact && element == EL_PEARL &&
6049            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6050   {
6051     ResetGfxAnimation(x, y);
6052
6053     Feld[x][y] = EL_PEARL_BREAKING;
6054     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6055     return;
6056   }
6057   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6058   {
6059     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6060
6061     return;
6062   }
6063
6064   if (impact && element == EL_AMOEBA_DROP)
6065   {
6066     if (object_hit && IS_PLAYER(x, y + 1))
6067       KillPlayerUnlessEnemyProtected(x, y + 1);
6068     else if (object_hit && smashed == EL_PENGUIN)
6069       Bang(x, y + 1);
6070     else
6071     {
6072       Feld[x][y] = EL_AMOEBA_GROWING;
6073       Store[x][y] = EL_AMOEBA_WET;
6074
6075       ResetRandomAnimationValue(x, y);
6076     }
6077     return;
6078   }
6079
6080   if (object_hit)               /* check which object was hit */
6081   {
6082     if ((CAN_PASS_MAGIC_WALL(element) && 
6083          (smashed == EL_MAGIC_WALL ||
6084           smashed == EL_BD_MAGIC_WALL)) ||
6085         (CAN_PASS_DC_MAGIC_WALL(element) &&
6086          smashed == EL_DC_MAGIC_WALL))
6087     {
6088       int xx, yy;
6089       int activated_magic_wall =
6090         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6091          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6092          EL_DC_MAGIC_WALL_ACTIVE);
6093
6094       /* activate magic wall / mill */
6095       SCAN_PLAYFIELD(xx, yy)
6096       {
6097         if (Feld[xx][yy] == smashed)
6098           Feld[xx][yy] = activated_magic_wall;
6099       }
6100
6101       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6102       game.magic_wall_active = TRUE;
6103
6104       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6105                             SND_MAGIC_WALL_ACTIVATING :
6106                             smashed == EL_BD_MAGIC_WALL ?
6107                             SND_BD_MAGIC_WALL_ACTIVATING :
6108                             SND_DC_MAGIC_WALL_ACTIVATING));
6109     }
6110
6111     if (IS_PLAYER(x, y + 1))
6112     {
6113       if (CAN_SMASH_PLAYER(element))
6114       {
6115         KillPlayerUnlessEnemyProtected(x, y + 1);
6116         return;
6117       }
6118     }
6119     else if (smashed == EL_PENGUIN)
6120     {
6121       if (CAN_SMASH_PLAYER(element))
6122       {
6123         Bang(x, y + 1);
6124         return;
6125       }
6126     }
6127     else if (element == EL_BD_DIAMOND)
6128     {
6129       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6130       {
6131         Bang(x, y + 1);
6132         return;
6133       }
6134     }
6135     else if (((element == EL_SP_INFOTRON ||
6136                element == EL_SP_ZONK) &&
6137               (smashed == EL_SP_SNIKSNAK ||
6138                smashed == EL_SP_ELECTRON ||
6139                smashed == EL_SP_DISK_ORANGE)) ||
6140              (element == EL_SP_INFOTRON &&
6141               smashed == EL_SP_DISK_YELLOW))
6142     {
6143       Bang(x, y + 1);
6144       return;
6145     }
6146     else if (CAN_SMASH_EVERYTHING(element))
6147     {
6148       if (IS_CLASSIC_ENEMY(smashed) ||
6149           CAN_EXPLODE_SMASHED(smashed))
6150       {
6151         Bang(x, y + 1);
6152         return;
6153       }
6154       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6155       {
6156         if (smashed == EL_LAMP ||
6157             smashed == EL_LAMP_ACTIVE)
6158         {
6159           Bang(x, y + 1);
6160           return;
6161         }
6162         else if (smashed == EL_NUT)
6163         {
6164           Feld[x][y + 1] = EL_NUT_BREAKING;
6165           PlayLevelSound(x, y, SND_NUT_BREAKING);
6166           RaiseScoreElement(EL_NUT);
6167           return;
6168         }
6169         else if (smashed == EL_PEARL)
6170         {
6171           ResetGfxAnimation(x, y);
6172
6173           Feld[x][y + 1] = EL_PEARL_BREAKING;
6174           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6175           return;
6176         }
6177         else if (smashed == EL_DIAMOND)
6178         {
6179           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6180           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6181           return;
6182         }
6183         else if (IS_BELT_SWITCH(smashed))
6184         {
6185           ToggleBeltSwitch(x, y + 1);
6186         }
6187         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6188                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6189                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6190                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6191         {
6192           ToggleSwitchgateSwitch(x, y + 1);
6193         }
6194         else if (smashed == EL_LIGHT_SWITCH ||
6195                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6196         {
6197           ToggleLightSwitch(x, y + 1);
6198         }
6199         else
6200         {
6201           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6202
6203           CheckElementChangeBySide(x, y + 1, smashed, element,
6204                                    CE_SWITCHED, CH_SIDE_TOP);
6205           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6206                                             CH_SIDE_TOP);
6207         }
6208       }
6209       else
6210       {
6211         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6212       }
6213     }
6214   }
6215
6216   /* play sound of magic wall / mill */
6217   if (!last_line &&
6218       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6219        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6220        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6221   {
6222     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6223       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6224     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6225       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6226     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6227       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6228
6229     return;
6230   }
6231
6232   /* play sound of object that hits the ground */
6233   if (last_line || object_hit)
6234     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6235 }
6236
6237 inline static void TurnRoundExt(int x, int y)
6238 {
6239   static struct
6240   {
6241     int dx, dy;
6242   } move_xy[] =
6243   {
6244     {  0,  0 },
6245     { -1,  0 },
6246     { +1,  0 },
6247     {  0,  0 },
6248     {  0, -1 },
6249     {  0,  0 }, { 0, 0 }, { 0, 0 },
6250     {  0, +1 }
6251   };
6252   static struct
6253   {
6254     int left, right, back;
6255   } turn[] =
6256   {
6257     { 0,        0,              0        },
6258     { MV_DOWN,  MV_UP,          MV_RIGHT },
6259     { MV_UP,    MV_DOWN,        MV_LEFT  },
6260     { 0,        0,              0        },
6261     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6262     { 0,        0,              0        },
6263     { 0,        0,              0        },
6264     { 0,        0,              0        },
6265     { MV_RIGHT, MV_LEFT,        MV_UP    }
6266   };
6267
6268   int element = Feld[x][y];
6269   int move_pattern = element_info[element].move_pattern;
6270
6271   int old_move_dir = MovDir[x][y];
6272   int left_dir  = turn[old_move_dir].left;
6273   int right_dir = turn[old_move_dir].right;
6274   int back_dir  = turn[old_move_dir].back;
6275
6276   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6277   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6278   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6279   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6280
6281   int left_x  = x + left_dx,  left_y  = y + left_dy;
6282   int right_x = x + right_dx, right_y = y + right_dy;
6283   int move_x  = x + move_dx,  move_y  = y + move_dy;
6284
6285   int xx, yy;
6286
6287   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6288   {
6289     TestIfBadThingTouchesOtherBadThing(x, y);
6290
6291     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6292       MovDir[x][y] = right_dir;
6293     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6294       MovDir[x][y] = left_dir;
6295
6296     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6297       MovDelay[x][y] = 9;
6298     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6299       MovDelay[x][y] = 1;
6300   }
6301   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6302   {
6303     TestIfBadThingTouchesOtherBadThing(x, y);
6304
6305     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6306       MovDir[x][y] = left_dir;
6307     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6308       MovDir[x][y] = right_dir;
6309
6310     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6311       MovDelay[x][y] = 9;
6312     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6313       MovDelay[x][y] = 1;
6314   }
6315   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6316   {
6317     TestIfBadThingTouchesOtherBadThing(x, y);
6318
6319     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6320       MovDir[x][y] = left_dir;
6321     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6322       MovDir[x][y] = right_dir;
6323
6324     if (MovDir[x][y] != old_move_dir)
6325       MovDelay[x][y] = 9;
6326   }
6327   else if (element == EL_YAMYAM)
6328   {
6329     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6330     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6331
6332     if (can_turn_left && can_turn_right)
6333       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6334     else if (can_turn_left)
6335       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6336     else if (can_turn_right)
6337       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6338     else
6339       MovDir[x][y] = back_dir;
6340
6341     MovDelay[x][y] = 16 + 16 * RND(3);
6342   }
6343   else if (element == EL_DARK_YAMYAM)
6344   {
6345     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6346                                                          left_x, left_y);
6347     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6348                                                          right_x, right_y);
6349
6350     if (can_turn_left && can_turn_right)
6351       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6352     else if (can_turn_left)
6353       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6354     else if (can_turn_right)
6355       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6356     else
6357       MovDir[x][y] = back_dir;
6358
6359     MovDelay[x][y] = 16 + 16 * RND(3);
6360   }
6361   else if (element == EL_PACMAN)
6362   {
6363     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6364     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6365
6366     if (can_turn_left && can_turn_right)
6367       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6368     else if (can_turn_left)
6369       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6370     else if (can_turn_right)
6371       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6372     else
6373       MovDir[x][y] = back_dir;
6374
6375     MovDelay[x][y] = 6 + RND(40);
6376   }
6377   else if (element == EL_PIG)
6378   {
6379     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6380     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6381     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6382     boolean should_turn_left, should_turn_right, should_move_on;
6383     int rnd_value = 24;
6384     int rnd = RND(rnd_value);
6385
6386     should_turn_left = (can_turn_left &&
6387                         (!can_move_on ||
6388                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6389                                                    y + back_dy + left_dy)));
6390     should_turn_right = (can_turn_right &&
6391                          (!can_move_on ||
6392                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6393                                                     y + back_dy + right_dy)));
6394     should_move_on = (can_move_on &&
6395                       (!can_turn_left ||
6396                        !can_turn_right ||
6397                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6398                                                  y + move_dy + left_dy) ||
6399                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6400                                                  y + move_dy + right_dy)));
6401
6402     if (should_turn_left || should_turn_right || should_move_on)
6403     {
6404       if (should_turn_left && should_turn_right && should_move_on)
6405         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6406                         rnd < 2 * rnd_value / 3 ? right_dir :
6407                         old_move_dir);
6408       else if (should_turn_left && should_turn_right)
6409         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6410       else if (should_turn_left && should_move_on)
6411         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6412       else if (should_turn_right && should_move_on)
6413         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6414       else if (should_turn_left)
6415         MovDir[x][y] = left_dir;
6416       else if (should_turn_right)
6417         MovDir[x][y] = right_dir;
6418       else if (should_move_on)
6419         MovDir[x][y] = old_move_dir;
6420     }
6421     else if (can_move_on && rnd > rnd_value / 8)
6422       MovDir[x][y] = old_move_dir;
6423     else if (can_turn_left && can_turn_right)
6424       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6425     else if (can_turn_left && rnd > rnd_value / 8)
6426       MovDir[x][y] = left_dir;
6427     else if (can_turn_right && rnd > rnd_value/8)
6428       MovDir[x][y] = right_dir;
6429     else
6430       MovDir[x][y] = back_dir;
6431
6432     xx = x + move_xy[MovDir[x][y]].dx;
6433     yy = y + move_xy[MovDir[x][y]].dy;
6434
6435     if (!IN_LEV_FIELD(xx, yy) ||
6436         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6437       MovDir[x][y] = old_move_dir;
6438
6439     MovDelay[x][y] = 0;
6440   }
6441   else if (element == EL_DRAGON)
6442   {
6443     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6444     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6445     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6446     int rnd_value = 24;
6447     int rnd = RND(rnd_value);
6448
6449     if (can_move_on && rnd > rnd_value / 8)
6450       MovDir[x][y] = old_move_dir;
6451     else if (can_turn_left && can_turn_right)
6452       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6453     else if (can_turn_left && rnd > rnd_value / 8)
6454       MovDir[x][y] = left_dir;
6455     else if (can_turn_right && rnd > rnd_value / 8)
6456       MovDir[x][y] = right_dir;
6457     else
6458       MovDir[x][y] = back_dir;
6459
6460     xx = x + move_xy[MovDir[x][y]].dx;
6461     yy = y + move_xy[MovDir[x][y]].dy;
6462
6463     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6464       MovDir[x][y] = old_move_dir;
6465
6466     MovDelay[x][y] = 0;
6467   }
6468   else if (element == EL_MOLE)
6469   {
6470     boolean can_move_on =
6471       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6472                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6473                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6474     if (!can_move_on)
6475     {
6476       boolean can_turn_left =
6477         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6478                               IS_AMOEBOID(Feld[left_x][left_y])));
6479
6480       boolean can_turn_right =
6481         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6482                               IS_AMOEBOID(Feld[right_x][right_y])));
6483
6484       if (can_turn_left && can_turn_right)
6485         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6486       else if (can_turn_left)
6487         MovDir[x][y] = left_dir;
6488       else
6489         MovDir[x][y] = right_dir;
6490     }
6491
6492     if (MovDir[x][y] != old_move_dir)
6493       MovDelay[x][y] = 9;
6494   }
6495   else if (element == EL_BALLOON)
6496   {
6497     MovDir[x][y] = game.wind_direction;
6498     MovDelay[x][y] = 0;
6499   }
6500   else if (element == EL_SPRING)
6501   {
6502     if (MovDir[x][y] & MV_HORIZONTAL)
6503     {
6504       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6505           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6506       {
6507         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6508         ResetGfxAnimation(move_x, move_y);
6509         TEST_DrawLevelField(move_x, move_y);
6510
6511         MovDir[x][y] = back_dir;
6512       }
6513       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6514                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6515         MovDir[x][y] = MV_NONE;
6516     }
6517
6518     MovDelay[x][y] = 0;
6519   }
6520   else if (element == EL_ROBOT ||
6521            element == EL_SATELLITE ||
6522            element == EL_PENGUIN ||
6523            element == EL_EMC_ANDROID)
6524   {
6525     int attr_x = -1, attr_y = -1;
6526
6527     if (AllPlayersGone)
6528     {
6529       attr_x = ExitX;
6530       attr_y = ExitY;
6531     }
6532     else
6533     {
6534       int i;
6535
6536       for (i = 0; i < MAX_PLAYERS; i++)
6537       {
6538         struct PlayerInfo *player = &stored_player[i];
6539         int jx = player->jx, jy = player->jy;
6540
6541         if (!player->active)
6542           continue;
6543
6544         if (attr_x == -1 ||
6545             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6546         {
6547           attr_x = jx;
6548           attr_y = jy;
6549         }
6550       }
6551     }
6552
6553     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6554         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6555          game.engine_version < VERSION_IDENT(3,1,0,0)))
6556     {
6557       attr_x = ZX;
6558       attr_y = ZY;
6559     }
6560
6561     if (element == EL_PENGUIN)
6562     {
6563       int i;
6564       static int xy[4][2] =
6565       {
6566         { 0, -1 },
6567         { -1, 0 },
6568         { +1, 0 },
6569         { 0, +1 }
6570       };
6571
6572       for (i = 0; i < NUM_DIRECTIONS; i++)
6573       {
6574         int ex = x + xy[i][0];
6575         int ey = y + xy[i][1];
6576
6577         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6578                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6579                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6580                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6581         {
6582           attr_x = ex;
6583           attr_y = ey;
6584           break;
6585         }
6586       }
6587     }
6588
6589     MovDir[x][y] = MV_NONE;
6590     if (attr_x < x)
6591       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6592     else if (attr_x > x)
6593       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6594     if (attr_y < y)
6595       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6596     else if (attr_y > y)
6597       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6598
6599     if (element == EL_ROBOT)
6600     {
6601       int newx, newy;
6602
6603       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6604         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6605       Moving2Blocked(x, y, &newx, &newy);
6606
6607       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6608         MovDelay[x][y] = 8 + 8 * !RND(3);
6609       else
6610         MovDelay[x][y] = 16;
6611     }
6612     else if (element == EL_PENGUIN)
6613     {
6614       int newx, newy;
6615
6616       MovDelay[x][y] = 1;
6617
6618       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6619       {
6620         boolean first_horiz = RND(2);
6621         int new_move_dir = MovDir[x][y];
6622
6623         MovDir[x][y] =
6624           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6625         Moving2Blocked(x, y, &newx, &newy);
6626
6627         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6628           return;
6629
6630         MovDir[x][y] =
6631           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6632         Moving2Blocked(x, y, &newx, &newy);
6633
6634         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6635           return;
6636
6637         MovDir[x][y] = old_move_dir;
6638         return;
6639       }
6640     }
6641     else if (element == EL_SATELLITE)
6642     {
6643       int newx, newy;
6644
6645       MovDelay[x][y] = 1;
6646
6647       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6648       {
6649         boolean first_horiz = RND(2);
6650         int new_move_dir = MovDir[x][y];
6651
6652         MovDir[x][y] =
6653           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6654         Moving2Blocked(x, y, &newx, &newy);
6655
6656         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6657           return;
6658
6659         MovDir[x][y] =
6660           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6661         Moving2Blocked(x, y, &newx, &newy);
6662
6663         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6664           return;
6665
6666         MovDir[x][y] = old_move_dir;
6667         return;
6668       }
6669     }
6670     else if (element == EL_EMC_ANDROID)
6671     {
6672       static int check_pos[16] =
6673       {
6674         -1,             /*  0 => (invalid)          */
6675         7,              /*  1 => MV_LEFT            */
6676         3,              /*  2 => MV_RIGHT           */
6677         -1,             /*  3 => (invalid)          */
6678         1,              /*  4 =>            MV_UP   */
6679         0,              /*  5 => MV_LEFT  | MV_UP   */
6680         2,              /*  6 => MV_RIGHT | MV_UP   */
6681         -1,             /*  7 => (invalid)          */
6682         5,              /*  8 =>            MV_DOWN */
6683         6,              /*  9 => MV_LEFT  | MV_DOWN */
6684         4,              /* 10 => MV_RIGHT | MV_DOWN */
6685         -1,             /* 11 => (invalid)          */
6686         -1,             /* 12 => (invalid)          */
6687         -1,             /* 13 => (invalid)          */
6688         -1,             /* 14 => (invalid)          */
6689         -1,             /* 15 => (invalid)          */
6690       };
6691       static struct
6692       {
6693         int dx, dy;
6694         int dir;
6695       } check_xy[8] =
6696       {
6697         { -1, -1,       MV_LEFT  | MV_UP   },
6698         {  0, -1,                  MV_UP   },
6699         { +1, -1,       MV_RIGHT | MV_UP   },
6700         { +1,  0,       MV_RIGHT           },
6701         { +1, +1,       MV_RIGHT | MV_DOWN },
6702         {  0, +1,                  MV_DOWN },
6703         { -1, +1,       MV_LEFT  | MV_DOWN },
6704         { -1,  0,       MV_LEFT            },
6705       };
6706       int start_pos, check_order;
6707       boolean can_clone = FALSE;
6708       int i;
6709
6710       /* check if there is any free field around current position */
6711       for (i = 0; i < 8; i++)
6712       {
6713         int newx = x + check_xy[i].dx;
6714         int newy = y + check_xy[i].dy;
6715
6716         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6717         {
6718           can_clone = TRUE;
6719
6720           break;
6721         }
6722       }
6723
6724       if (can_clone)            /* randomly find an element to clone */
6725       {
6726         can_clone = FALSE;
6727
6728         start_pos = check_pos[RND(8)];
6729         check_order = (RND(2) ? -1 : +1);
6730
6731         for (i = 0; i < 8; i++)
6732         {
6733           int pos_raw = start_pos + i * check_order;
6734           int pos = (pos_raw + 8) % 8;
6735           int newx = x + check_xy[pos].dx;
6736           int newy = y + check_xy[pos].dy;
6737
6738           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6739           {
6740             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6741             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6742
6743             Store[x][y] = Feld[newx][newy];
6744
6745             can_clone = TRUE;
6746
6747             break;
6748           }
6749         }
6750       }
6751
6752       if (can_clone)            /* randomly find a direction to move */
6753       {
6754         can_clone = FALSE;
6755
6756         start_pos = check_pos[RND(8)];
6757         check_order = (RND(2) ? -1 : +1);
6758
6759         for (i = 0; i < 8; i++)
6760         {
6761           int pos_raw = start_pos + i * check_order;
6762           int pos = (pos_raw + 8) % 8;
6763           int newx = x + check_xy[pos].dx;
6764           int newy = y + check_xy[pos].dy;
6765           int new_move_dir = check_xy[pos].dir;
6766
6767           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6768           {
6769             MovDir[x][y] = new_move_dir;
6770             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6771
6772             can_clone = TRUE;
6773
6774             break;
6775           }
6776         }
6777       }
6778
6779       if (can_clone)            /* cloning and moving successful */
6780         return;
6781
6782       /* cannot clone -- try to move towards player */
6783
6784       start_pos = check_pos[MovDir[x][y] & 0x0f];
6785       check_order = (RND(2) ? -1 : +1);
6786
6787       for (i = 0; i < 3; i++)
6788       {
6789         /* first check start_pos, then previous/next or (next/previous) pos */
6790         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6791         int pos = (pos_raw + 8) % 8;
6792         int newx = x + check_xy[pos].dx;
6793         int newy = y + check_xy[pos].dy;
6794         int new_move_dir = check_xy[pos].dir;
6795
6796         if (IS_PLAYER(newx, newy))
6797           break;
6798
6799         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6800         {
6801           MovDir[x][y] = new_move_dir;
6802           MovDelay[x][y] = level.android_move_time * 8 + 1;
6803
6804           break;
6805         }
6806       }
6807     }
6808   }
6809   else if (move_pattern == MV_TURNING_LEFT ||
6810            move_pattern == MV_TURNING_RIGHT ||
6811            move_pattern == MV_TURNING_LEFT_RIGHT ||
6812            move_pattern == MV_TURNING_RIGHT_LEFT ||
6813            move_pattern == MV_TURNING_RANDOM ||
6814            move_pattern == MV_ALL_DIRECTIONS)
6815   {
6816     boolean can_turn_left =
6817       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6818     boolean can_turn_right =
6819       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6820
6821     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6822       return;
6823
6824     if (move_pattern == MV_TURNING_LEFT)
6825       MovDir[x][y] = left_dir;
6826     else if (move_pattern == MV_TURNING_RIGHT)
6827       MovDir[x][y] = right_dir;
6828     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6829       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6830     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6831       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6832     else if (move_pattern == MV_TURNING_RANDOM)
6833       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6834                       can_turn_right && !can_turn_left ? right_dir :
6835                       RND(2) ? left_dir : right_dir);
6836     else if (can_turn_left && can_turn_right)
6837       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6838     else if (can_turn_left)
6839       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6840     else if (can_turn_right)
6841       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6842     else
6843       MovDir[x][y] = back_dir;
6844
6845     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6846   }
6847   else if (move_pattern == MV_HORIZONTAL ||
6848            move_pattern == MV_VERTICAL)
6849   {
6850     if (move_pattern & old_move_dir)
6851       MovDir[x][y] = back_dir;
6852     else if (move_pattern == MV_HORIZONTAL)
6853       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6854     else if (move_pattern == MV_VERTICAL)
6855       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6856
6857     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6858   }
6859   else if (move_pattern & MV_ANY_DIRECTION)
6860   {
6861     MovDir[x][y] = move_pattern;
6862     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6863   }
6864   else if (move_pattern & MV_WIND_DIRECTION)
6865   {
6866     MovDir[x][y] = game.wind_direction;
6867     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6868   }
6869   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6870   {
6871     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6872       MovDir[x][y] = left_dir;
6873     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6874       MovDir[x][y] = right_dir;
6875
6876     if (MovDir[x][y] != old_move_dir)
6877       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6878   }
6879   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6880   {
6881     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6882       MovDir[x][y] = right_dir;
6883     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6884       MovDir[x][y] = left_dir;
6885
6886     if (MovDir[x][y] != old_move_dir)
6887       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6888   }
6889   else if (move_pattern == MV_TOWARDS_PLAYER ||
6890            move_pattern == MV_AWAY_FROM_PLAYER)
6891   {
6892     int attr_x = -1, attr_y = -1;
6893     int newx, newy;
6894     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6895
6896     if (AllPlayersGone)
6897     {
6898       attr_x = ExitX;
6899       attr_y = ExitY;
6900     }
6901     else
6902     {
6903       int i;
6904
6905       for (i = 0; i < MAX_PLAYERS; i++)
6906       {
6907         struct PlayerInfo *player = &stored_player[i];
6908         int jx = player->jx, jy = player->jy;
6909
6910         if (!player->active)
6911           continue;
6912
6913         if (attr_x == -1 ||
6914             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6915         {
6916           attr_x = jx;
6917           attr_y = jy;
6918         }
6919       }
6920     }
6921
6922     MovDir[x][y] = MV_NONE;
6923     if (attr_x < x)
6924       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6925     else if (attr_x > x)
6926       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6927     if (attr_y < y)
6928       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6929     else if (attr_y > y)
6930       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6931
6932     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6933
6934     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6935     {
6936       boolean first_horiz = RND(2);
6937       int new_move_dir = MovDir[x][y];
6938
6939       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6940       {
6941         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6942         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6943
6944         return;
6945       }
6946
6947       MovDir[x][y] =
6948         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6949       Moving2Blocked(x, y, &newx, &newy);
6950
6951       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6952         return;
6953
6954       MovDir[x][y] =
6955         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6956       Moving2Blocked(x, y, &newx, &newy);
6957
6958       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6959         return;
6960
6961       MovDir[x][y] = old_move_dir;
6962     }
6963   }
6964   else if (move_pattern == MV_WHEN_PUSHED ||
6965            move_pattern == MV_WHEN_DROPPED)
6966   {
6967     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6968       MovDir[x][y] = MV_NONE;
6969
6970     MovDelay[x][y] = 0;
6971   }
6972   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6973   {
6974     static int test_xy[7][2] =
6975     {
6976       { 0, -1 },
6977       { -1, 0 },
6978       { +1, 0 },
6979       { 0, +1 },
6980       { 0, -1 },
6981       { -1, 0 },
6982       { +1, 0 },
6983     };
6984     static int test_dir[7] =
6985     {
6986       MV_UP,
6987       MV_LEFT,
6988       MV_RIGHT,
6989       MV_DOWN,
6990       MV_UP,
6991       MV_LEFT,
6992       MV_RIGHT,
6993     };
6994     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6995     int move_preference = -1000000;     /* start with very low preference */
6996     int new_move_dir = MV_NONE;
6997     int start_test = RND(4);
6998     int i;
6999
7000     for (i = 0; i < NUM_DIRECTIONS; i++)
7001     {
7002       int move_dir = test_dir[start_test + i];
7003       int move_dir_preference;
7004
7005       xx = x + test_xy[start_test + i][0];
7006       yy = y + test_xy[start_test + i][1];
7007
7008       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7009           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7010       {
7011         new_move_dir = move_dir;
7012
7013         break;
7014       }
7015
7016       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7017         continue;
7018
7019       move_dir_preference = -1 * RunnerVisit[xx][yy];
7020       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7021         move_dir_preference = PlayerVisit[xx][yy];
7022
7023       if (move_dir_preference > move_preference)
7024       {
7025         /* prefer field that has not been visited for the longest time */
7026         move_preference = move_dir_preference;
7027         new_move_dir = move_dir;
7028       }
7029       else if (move_dir_preference == move_preference &&
7030                move_dir == old_move_dir)
7031       {
7032         /* prefer last direction when all directions are preferred equally */
7033         move_preference = move_dir_preference;
7034         new_move_dir = move_dir;
7035       }
7036     }
7037
7038     MovDir[x][y] = new_move_dir;
7039     if (old_move_dir != new_move_dir)
7040       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7041   }
7042 }
7043
7044 static void TurnRound(int x, int y)
7045 {
7046   int direction = MovDir[x][y];
7047
7048   TurnRoundExt(x, y);
7049
7050   GfxDir[x][y] = MovDir[x][y];
7051
7052   if (direction != MovDir[x][y])
7053     GfxFrame[x][y] = 0;
7054
7055   if (MovDelay[x][y])
7056     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7057
7058   ResetGfxFrame(x, y);
7059 }
7060
7061 static boolean JustBeingPushed(int x, int y)
7062 {
7063   int i;
7064
7065   for (i = 0; i < MAX_PLAYERS; i++)
7066   {
7067     struct PlayerInfo *player = &stored_player[i];
7068
7069     if (player->active && player->is_pushing && player->MovPos)
7070     {
7071       int next_jx = player->jx + (player->jx - player->last_jx);
7072       int next_jy = player->jy + (player->jy - player->last_jy);
7073
7074       if (x == next_jx && y == next_jy)
7075         return TRUE;
7076     }
7077   }
7078
7079   return FALSE;
7080 }
7081
7082 void StartMoving(int x, int y)
7083 {
7084   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7085   int element = Feld[x][y];
7086
7087   if (Stop[x][y])
7088     return;
7089
7090   if (MovDelay[x][y] == 0)
7091     GfxAction[x][y] = ACTION_DEFAULT;
7092
7093   if (CAN_FALL(element) && y < lev_fieldy - 1)
7094   {
7095     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7096         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7097       if (JustBeingPushed(x, y))
7098         return;
7099
7100     if (element == EL_QUICKSAND_FULL)
7101     {
7102       if (IS_FREE(x, y + 1))
7103       {
7104         InitMovingField(x, y, MV_DOWN);
7105         started_moving = TRUE;
7106
7107         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7108 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7109         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7110           Store[x][y] = EL_ROCK;
7111 #else
7112         Store[x][y] = EL_ROCK;
7113 #endif
7114
7115         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7116       }
7117       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7118       {
7119         if (!MovDelay[x][y])
7120         {
7121           MovDelay[x][y] = TILEY + 1;
7122
7123           ResetGfxAnimation(x, y);
7124           ResetGfxAnimation(x, y + 1);
7125         }
7126
7127         if (MovDelay[x][y])
7128         {
7129           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7130           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7131
7132           MovDelay[x][y]--;
7133           if (MovDelay[x][y])
7134             return;
7135         }
7136
7137         Feld[x][y] = EL_QUICKSAND_EMPTY;
7138         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7139         Store[x][y + 1] = Store[x][y];
7140         Store[x][y] = 0;
7141
7142         PlayLevelSoundAction(x, y, ACTION_FILLING);
7143       }
7144       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7145       {
7146         if (!MovDelay[x][y])
7147         {
7148           MovDelay[x][y] = TILEY + 1;
7149
7150           ResetGfxAnimation(x, y);
7151           ResetGfxAnimation(x, y + 1);
7152         }
7153
7154         if (MovDelay[x][y])
7155         {
7156           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7157           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7158
7159           MovDelay[x][y]--;
7160           if (MovDelay[x][y])
7161             return;
7162         }
7163
7164         Feld[x][y] = EL_QUICKSAND_EMPTY;
7165         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7166         Store[x][y + 1] = Store[x][y];
7167         Store[x][y] = 0;
7168
7169         PlayLevelSoundAction(x, y, ACTION_FILLING);
7170       }
7171     }
7172     else if (element == EL_QUICKSAND_FAST_FULL)
7173     {
7174       if (IS_FREE(x, y + 1))
7175       {
7176         InitMovingField(x, y, MV_DOWN);
7177         started_moving = TRUE;
7178
7179         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7180 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7181         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7182           Store[x][y] = EL_ROCK;
7183 #else
7184         Store[x][y] = EL_ROCK;
7185 #endif
7186
7187         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7188       }
7189       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7190       {
7191         if (!MovDelay[x][y])
7192         {
7193           MovDelay[x][y] = TILEY + 1;
7194
7195           ResetGfxAnimation(x, y);
7196           ResetGfxAnimation(x, y + 1);
7197         }
7198
7199         if (MovDelay[x][y])
7200         {
7201           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7202           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7203
7204           MovDelay[x][y]--;
7205           if (MovDelay[x][y])
7206             return;
7207         }
7208
7209         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7210         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7211         Store[x][y + 1] = Store[x][y];
7212         Store[x][y] = 0;
7213
7214         PlayLevelSoundAction(x, y, ACTION_FILLING);
7215       }
7216       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7217       {
7218         if (!MovDelay[x][y])
7219         {
7220           MovDelay[x][y] = TILEY + 1;
7221
7222           ResetGfxAnimation(x, y);
7223           ResetGfxAnimation(x, y + 1);
7224         }
7225
7226         if (MovDelay[x][y])
7227         {
7228           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7229           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7230
7231           MovDelay[x][y]--;
7232           if (MovDelay[x][y])
7233             return;
7234         }
7235
7236         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7237         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7238         Store[x][y + 1] = Store[x][y];
7239         Store[x][y] = 0;
7240
7241         PlayLevelSoundAction(x, y, ACTION_FILLING);
7242       }
7243     }
7244     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7245              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7246     {
7247       InitMovingField(x, y, MV_DOWN);
7248       started_moving = TRUE;
7249
7250       Feld[x][y] = EL_QUICKSAND_FILLING;
7251       Store[x][y] = element;
7252
7253       PlayLevelSoundAction(x, y, ACTION_FILLING);
7254     }
7255     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7256              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7257     {
7258       InitMovingField(x, y, MV_DOWN);
7259       started_moving = TRUE;
7260
7261       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7262       Store[x][y] = element;
7263
7264       PlayLevelSoundAction(x, y, ACTION_FILLING);
7265     }
7266     else if (element == EL_MAGIC_WALL_FULL)
7267     {
7268       if (IS_FREE(x, y + 1))
7269       {
7270         InitMovingField(x, y, MV_DOWN);
7271         started_moving = TRUE;
7272
7273         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7274         Store[x][y] = EL_CHANGED(Store[x][y]);
7275       }
7276       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7277       {
7278         if (!MovDelay[x][y])
7279           MovDelay[x][y] = TILEY / 4 + 1;
7280
7281         if (MovDelay[x][y])
7282         {
7283           MovDelay[x][y]--;
7284           if (MovDelay[x][y])
7285             return;
7286         }
7287
7288         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7289         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7290         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7291         Store[x][y] = 0;
7292       }
7293     }
7294     else if (element == EL_BD_MAGIC_WALL_FULL)
7295     {
7296       if (IS_FREE(x, y + 1))
7297       {
7298         InitMovingField(x, y, MV_DOWN);
7299         started_moving = TRUE;
7300
7301         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7302         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7303       }
7304       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7305       {
7306         if (!MovDelay[x][y])
7307           MovDelay[x][y] = TILEY / 4 + 1;
7308
7309         if (MovDelay[x][y])
7310         {
7311           MovDelay[x][y]--;
7312           if (MovDelay[x][y])
7313             return;
7314         }
7315
7316         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7317         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7318         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7319         Store[x][y] = 0;
7320       }
7321     }
7322     else if (element == EL_DC_MAGIC_WALL_FULL)
7323     {
7324       if (IS_FREE(x, y + 1))
7325       {
7326         InitMovingField(x, y, MV_DOWN);
7327         started_moving = TRUE;
7328
7329         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7330         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7331       }
7332       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7333       {
7334         if (!MovDelay[x][y])
7335           MovDelay[x][y] = TILEY / 4 + 1;
7336
7337         if (MovDelay[x][y])
7338         {
7339           MovDelay[x][y]--;
7340           if (MovDelay[x][y])
7341             return;
7342         }
7343
7344         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7345         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7346         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7347         Store[x][y] = 0;
7348       }
7349     }
7350     else if ((CAN_PASS_MAGIC_WALL(element) &&
7351               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7352                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7353              (CAN_PASS_DC_MAGIC_WALL(element) &&
7354               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7355
7356     {
7357       InitMovingField(x, y, MV_DOWN);
7358       started_moving = TRUE;
7359
7360       Feld[x][y] =
7361         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7362          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7363          EL_DC_MAGIC_WALL_FILLING);
7364       Store[x][y] = element;
7365     }
7366     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7367     {
7368       SplashAcid(x, y + 1);
7369
7370       InitMovingField(x, y, MV_DOWN);
7371       started_moving = TRUE;
7372
7373       Store[x][y] = EL_ACID;
7374     }
7375     else if (
7376              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7377               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7378              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7379               CAN_FALL(element) && WasJustFalling[x][y] &&
7380               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7381
7382              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7383               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7384               (Feld[x][y + 1] == EL_BLOCKED)))
7385     {
7386       /* this is needed for a special case not covered by calling "Impact()"
7387          from "ContinueMoving()": if an element moves to a tile directly below
7388          another element which was just falling on that tile (which was empty
7389          in the previous frame), the falling element above would just stop
7390          instead of smashing the element below (in previous version, the above
7391          element was just checked for "moving" instead of "falling", resulting
7392          in incorrect smashes caused by horizontal movement of the above
7393          element; also, the case of the player being the element to smash was
7394          simply not covered here... :-/ ) */
7395
7396       CheckCollision[x][y] = 0;
7397       CheckImpact[x][y] = 0;
7398
7399       Impact(x, y);
7400     }
7401     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7402     {
7403       if (MovDir[x][y] == MV_NONE)
7404       {
7405         InitMovingField(x, y, MV_DOWN);
7406         started_moving = TRUE;
7407       }
7408     }
7409     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7410     {
7411       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7412         MovDir[x][y] = MV_DOWN;
7413
7414       InitMovingField(x, y, MV_DOWN);
7415       started_moving = TRUE;
7416     }
7417     else if (element == EL_AMOEBA_DROP)
7418     {
7419       Feld[x][y] = EL_AMOEBA_GROWING;
7420       Store[x][y] = EL_AMOEBA_WET;
7421     }
7422     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7423               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7424              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7425              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7426     {
7427       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7428                                 (IS_FREE(x - 1, y + 1) ||
7429                                  Feld[x - 1][y + 1] == EL_ACID));
7430       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7431                                 (IS_FREE(x + 1, y + 1) ||
7432                                  Feld[x + 1][y + 1] == EL_ACID));
7433       boolean can_fall_any  = (can_fall_left || can_fall_right);
7434       boolean can_fall_both = (can_fall_left && can_fall_right);
7435       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7436
7437       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7438       {
7439         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7440           can_fall_right = FALSE;
7441         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7442           can_fall_left = FALSE;
7443         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7444           can_fall_right = FALSE;
7445         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7446           can_fall_left = FALSE;
7447
7448         can_fall_any  = (can_fall_left || can_fall_right);
7449         can_fall_both = FALSE;
7450       }
7451
7452       if (can_fall_both)
7453       {
7454         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7455           can_fall_right = FALSE;       /* slip down on left side */
7456         else
7457           can_fall_left = !(can_fall_right = RND(2));
7458
7459         can_fall_both = FALSE;
7460       }
7461
7462       if (can_fall_any)
7463       {
7464         /* if not determined otherwise, prefer left side for slipping down */
7465         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7466         started_moving = TRUE;
7467       }
7468     }
7469     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7470     {
7471       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7472       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7473       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7474       int belt_dir = game.belt_dir[belt_nr];
7475
7476       if ((belt_dir == MV_LEFT  && left_is_free) ||
7477           (belt_dir == MV_RIGHT && right_is_free))
7478       {
7479         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7480
7481         InitMovingField(x, y, belt_dir);
7482         started_moving = TRUE;
7483
7484         Pushed[x][y] = TRUE;
7485         Pushed[nextx][y] = TRUE;
7486
7487         GfxAction[x][y] = ACTION_DEFAULT;
7488       }
7489       else
7490       {
7491         MovDir[x][y] = 0;       /* if element was moving, stop it */
7492       }
7493     }
7494   }
7495
7496   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7497   if (CAN_MOVE(element) && !started_moving)
7498   {
7499     int move_pattern = element_info[element].move_pattern;
7500     int newx, newy;
7501
7502     Moving2Blocked(x, y, &newx, &newy);
7503
7504     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7505       return;
7506
7507     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7508         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7509     {
7510       WasJustMoving[x][y] = 0;
7511       CheckCollision[x][y] = 0;
7512
7513       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7514
7515       if (Feld[x][y] != element)        /* element has changed */
7516         return;
7517     }
7518
7519     if (!MovDelay[x][y])        /* start new movement phase */
7520     {
7521       /* all objects that can change their move direction after each step
7522          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7523
7524       if (element != EL_YAMYAM &&
7525           element != EL_DARK_YAMYAM &&
7526           element != EL_PACMAN &&
7527           !(move_pattern & MV_ANY_DIRECTION) &&
7528           move_pattern != MV_TURNING_LEFT &&
7529           move_pattern != MV_TURNING_RIGHT &&
7530           move_pattern != MV_TURNING_LEFT_RIGHT &&
7531           move_pattern != MV_TURNING_RIGHT_LEFT &&
7532           move_pattern != MV_TURNING_RANDOM)
7533       {
7534         TurnRound(x, y);
7535
7536         if (MovDelay[x][y] && (element == EL_BUG ||
7537                                element == EL_SPACESHIP ||
7538                                element == EL_SP_SNIKSNAK ||
7539                                element == EL_SP_ELECTRON ||
7540                                element == EL_MOLE))
7541           TEST_DrawLevelField(x, y);
7542       }
7543     }
7544
7545     if (MovDelay[x][y])         /* wait some time before next movement */
7546     {
7547       MovDelay[x][y]--;
7548
7549       if (element == EL_ROBOT ||
7550           element == EL_YAMYAM ||
7551           element == EL_DARK_YAMYAM)
7552       {
7553         DrawLevelElementAnimationIfNeeded(x, y, element);
7554         PlayLevelSoundAction(x, y, ACTION_WAITING);
7555       }
7556       else if (element == EL_SP_ELECTRON)
7557         DrawLevelElementAnimationIfNeeded(x, y, element);
7558       else if (element == EL_DRAGON)
7559       {
7560         int i;
7561         int dir = MovDir[x][y];
7562         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7563         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7564         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7565                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7566                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7567                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7568         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7569
7570         GfxAction[x][y] = ACTION_ATTACKING;
7571
7572         if (IS_PLAYER(x, y))
7573           DrawPlayerField(x, y);
7574         else
7575           TEST_DrawLevelField(x, y);
7576
7577         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7578
7579         for (i = 1; i <= 3; i++)
7580         {
7581           int xx = x + i * dx;
7582           int yy = y + i * dy;
7583           int sx = SCREENX(xx);
7584           int sy = SCREENY(yy);
7585           int flame_graphic = graphic + (i - 1);
7586
7587           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7588             break;
7589
7590           if (MovDelay[x][y])
7591           {
7592             int flamed = MovingOrBlocked2Element(xx, yy);
7593
7594             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7595               Bang(xx, yy);
7596             else
7597               RemoveMovingField(xx, yy);
7598
7599             ChangeDelay[xx][yy] = 0;
7600
7601             Feld[xx][yy] = EL_FLAMES;
7602
7603             if (IN_SCR_FIELD(sx, sy))
7604             {
7605               TEST_DrawLevelFieldCrumbled(xx, yy);
7606               DrawGraphic(sx, sy, flame_graphic, frame);
7607             }
7608           }
7609           else
7610           {
7611             if (Feld[xx][yy] == EL_FLAMES)
7612               Feld[xx][yy] = EL_EMPTY;
7613             TEST_DrawLevelField(xx, yy);
7614           }
7615         }
7616       }
7617
7618       if (MovDelay[x][y])       /* element still has to wait some time */
7619       {
7620         PlayLevelSoundAction(x, y, ACTION_WAITING);
7621
7622         return;
7623       }
7624     }
7625
7626     /* now make next step */
7627
7628     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7629
7630     if (DONT_COLLIDE_WITH(element) &&
7631         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7632         !PLAYER_ENEMY_PROTECTED(newx, newy))
7633     {
7634       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7635
7636       return;
7637     }
7638
7639     else if (CAN_MOVE_INTO_ACID(element) &&
7640              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7641              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7642              (MovDir[x][y] == MV_DOWN ||
7643               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7644     {
7645       SplashAcid(newx, newy);
7646       Store[x][y] = EL_ACID;
7647     }
7648     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7649     {
7650       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7651           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7652           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7653           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7654       {
7655         RemoveField(x, y);
7656         TEST_DrawLevelField(x, y);
7657
7658         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7659         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7660           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7661
7662         local_player->friends_still_needed--;
7663         if (!local_player->friends_still_needed &&
7664             !local_player->GameOver && AllPlayersGone)
7665           PlayerWins(local_player);
7666
7667         return;
7668       }
7669       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7670       {
7671         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7672           TEST_DrawLevelField(newx, newy);
7673         else
7674           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7675       }
7676       else if (!IS_FREE(newx, newy))
7677       {
7678         GfxAction[x][y] = ACTION_WAITING;
7679
7680         if (IS_PLAYER(x, y))
7681           DrawPlayerField(x, y);
7682         else
7683           TEST_DrawLevelField(x, y);
7684
7685         return;
7686       }
7687     }
7688     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7689     {
7690       if (IS_FOOD_PIG(Feld[newx][newy]))
7691       {
7692         if (IS_MOVING(newx, newy))
7693           RemoveMovingField(newx, newy);
7694         else
7695         {
7696           Feld[newx][newy] = EL_EMPTY;
7697           TEST_DrawLevelField(newx, newy);
7698         }
7699
7700         PlayLevelSound(x, y, SND_PIG_DIGGING);
7701       }
7702       else if (!IS_FREE(newx, newy))
7703       {
7704         if (IS_PLAYER(x, y))
7705           DrawPlayerField(x, y);
7706         else
7707           TEST_DrawLevelField(x, y);
7708
7709         return;
7710       }
7711     }
7712     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7713     {
7714       if (Store[x][y] != EL_EMPTY)
7715       {
7716         boolean can_clone = FALSE;
7717         int xx, yy;
7718
7719         /* check if element to clone is still there */
7720         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7721         {
7722           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7723           {
7724             can_clone = TRUE;
7725
7726             break;
7727           }
7728         }
7729
7730         /* cannot clone or target field not free anymore -- do not clone */
7731         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7732           Store[x][y] = EL_EMPTY;
7733       }
7734
7735       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7736       {
7737         if (IS_MV_DIAGONAL(MovDir[x][y]))
7738         {
7739           int diagonal_move_dir = MovDir[x][y];
7740           int stored = Store[x][y];
7741           int change_delay = 8;
7742           int graphic;
7743
7744           /* android is moving diagonally */
7745
7746           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7747
7748           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7749           GfxElement[x][y] = EL_EMC_ANDROID;
7750           GfxAction[x][y] = ACTION_SHRINKING;
7751           GfxDir[x][y] = diagonal_move_dir;
7752           ChangeDelay[x][y] = change_delay;
7753
7754           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7755                                    GfxDir[x][y]);
7756
7757           DrawLevelGraphicAnimation(x, y, graphic);
7758           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7759
7760           if (Feld[newx][newy] == EL_ACID)
7761           {
7762             SplashAcid(newx, newy);
7763
7764             return;
7765           }
7766
7767           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7768
7769           Store[newx][newy] = EL_EMC_ANDROID;
7770           GfxElement[newx][newy] = EL_EMC_ANDROID;
7771           GfxAction[newx][newy] = ACTION_GROWING;
7772           GfxDir[newx][newy] = diagonal_move_dir;
7773           ChangeDelay[newx][newy] = change_delay;
7774
7775           graphic = el_act_dir2img(GfxElement[newx][newy],
7776                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7777
7778           DrawLevelGraphicAnimation(newx, newy, graphic);
7779           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7780
7781           return;
7782         }
7783         else
7784         {
7785           Feld[newx][newy] = EL_EMPTY;
7786           TEST_DrawLevelField(newx, newy);
7787
7788           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7789         }
7790       }
7791       else if (!IS_FREE(newx, newy))
7792       {
7793         return;
7794       }
7795     }
7796     else if (IS_CUSTOM_ELEMENT(element) &&
7797              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7798     {
7799       if (!DigFieldByCE(newx, newy, element))
7800         return;
7801
7802       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7803       {
7804         RunnerVisit[x][y] = FrameCounter;
7805         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7806       }
7807     }
7808     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7809     {
7810       if (!IS_FREE(newx, newy))
7811       {
7812         if (IS_PLAYER(x, y))
7813           DrawPlayerField(x, y);
7814         else
7815           TEST_DrawLevelField(x, y);
7816
7817         return;
7818       }
7819       else
7820       {
7821         boolean wanna_flame = !RND(10);
7822         int dx = newx - x, dy = newy - y;
7823         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7824         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7825         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7826                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7827         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7828                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7829
7830         if ((wanna_flame ||
7831              IS_CLASSIC_ENEMY(element1) ||
7832              IS_CLASSIC_ENEMY(element2)) &&
7833             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7834             element1 != EL_FLAMES && element2 != EL_FLAMES)
7835         {
7836           ResetGfxAnimation(x, y);
7837           GfxAction[x][y] = ACTION_ATTACKING;
7838
7839           if (IS_PLAYER(x, y))
7840             DrawPlayerField(x, y);
7841           else
7842             TEST_DrawLevelField(x, y);
7843
7844           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7845
7846           MovDelay[x][y] = 50;
7847
7848           Feld[newx][newy] = EL_FLAMES;
7849           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7850             Feld[newx1][newy1] = EL_FLAMES;
7851           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7852             Feld[newx2][newy2] = EL_FLAMES;
7853
7854           return;
7855         }
7856       }
7857     }
7858     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7859              Feld[newx][newy] == EL_DIAMOND)
7860     {
7861       if (IS_MOVING(newx, newy))
7862         RemoveMovingField(newx, newy);
7863       else
7864       {
7865         Feld[newx][newy] = EL_EMPTY;
7866         TEST_DrawLevelField(newx, newy);
7867       }
7868
7869       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7870     }
7871     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7872              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7873     {
7874       if (AmoebaNr[newx][newy])
7875       {
7876         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7877         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7878             Feld[newx][newy] == EL_BD_AMOEBA)
7879           AmoebaCnt[AmoebaNr[newx][newy]]--;
7880       }
7881
7882       if (IS_MOVING(newx, newy))
7883       {
7884         RemoveMovingField(newx, newy);
7885       }
7886       else
7887       {
7888         Feld[newx][newy] = EL_EMPTY;
7889         TEST_DrawLevelField(newx, newy);
7890       }
7891
7892       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7893     }
7894     else if ((element == EL_PACMAN || element == EL_MOLE)
7895              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7896     {
7897       if (AmoebaNr[newx][newy])
7898       {
7899         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7900         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7901             Feld[newx][newy] == EL_BD_AMOEBA)
7902           AmoebaCnt[AmoebaNr[newx][newy]]--;
7903       }
7904
7905       if (element == EL_MOLE)
7906       {
7907         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7908         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7909
7910         ResetGfxAnimation(x, y);
7911         GfxAction[x][y] = ACTION_DIGGING;
7912         TEST_DrawLevelField(x, y);
7913
7914         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7915
7916         return;                         /* wait for shrinking amoeba */
7917       }
7918       else      /* element == EL_PACMAN */
7919       {
7920         Feld[newx][newy] = EL_EMPTY;
7921         TEST_DrawLevelField(newx, newy);
7922         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7923       }
7924     }
7925     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7926              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7927               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7928     {
7929       /* wait for shrinking amoeba to completely disappear */
7930       return;
7931     }
7932     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7933     {
7934       /* object was running against a wall */
7935
7936       TurnRound(x, y);
7937
7938       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7939         DrawLevelElementAnimation(x, y, element);
7940
7941       if (DONT_TOUCH(element))
7942         TestIfBadThingTouchesPlayer(x, y);
7943
7944       return;
7945     }
7946
7947     InitMovingField(x, y, MovDir[x][y]);
7948
7949     PlayLevelSoundAction(x, y, ACTION_MOVING);
7950   }
7951
7952   if (MovDir[x][y])
7953     ContinueMoving(x, y);
7954 }
7955
7956 void ContinueMoving(int x, int y)
7957 {
7958   int element = Feld[x][y];
7959   struct ElementInfo *ei = &element_info[element];
7960   int direction = MovDir[x][y];
7961   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7962   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7963   int newx = x + dx, newy = y + dy;
7964   int stored = Store[x][y];
7965   int stored_new = Store[newx][newy];
7966   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7967   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7968   boolean last_line = (newy == lev_fieldy - 1);
7969
7970   MovPos[x][y] += getElementMoveStepsize(x, y);
7971
7972   if (pushed_by_player) /* special case: moving object pushed by player */
7973     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7974
7975   if (ABS(MovPos[x][y]) < TILEX)
7976   {
7977     TEST_DrawLevelField(x, y);
7978
7979     return;     /* element is still moving */
7980   }
7981
7982   /* element reached destination field */
7983
7984   Feld[x][y] = EL_EMPTY;
7985   Feld[newx][newy] = element;
7986   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7987
7988   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7989   {
7990     element = Feld[newx][newy] = EL_ACID;
7991   }
7992   else if (element == EL_MOLE)
7993   {
7994     Feld[x][y] = EL_SAND;
7995
7996     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
7997   }
7998   else if (element == EL_QUICKSAND_FILLING)
7999   {
8000     element = Feld[newx][newy] = get_next_element(element);
8001     Store[newx][newy] = Store[x][y];
8002   }
8003   else if (element == EL_QUICKSAND_EMPTYING)
8004   {
8005     Feld[x][y] = get_next_element(element);
8006     element = Feld[newx][newy] = Store[x][y];
8007   }
8008   else if (element == EL_QUICKSAND_FAST_FILLING)
8009   {
8010     element = Feld[newx][newy] = get_next_element(element);
8011     Store[newx][newy] = Store[x][y];
8012   }
8013   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8014   {
8015     Feld[x][y] = get_next_element(element);
8016     element = Feld[newx][newy] = Store[x][y];
8017   }
8018   else if (element == EL_MAGIC_WALL_FILLING)
8019   {
8020     element = Feld[newx][newy] = get_next_element(element);
8021     if (!game.magic_wall_active)
8022       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8023     Store[newx][newy] = Store[x][y];
8024   }
8025   else if (element == EL_MAGIC_WALL_EMPTYING)
8026   {
8027     Feld[x][y] = get_next_element(element);
8028     if (!game.magic_wall_active)
8029       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8030     element = Feld[newx][newy] = Store[x][y];
8031
8032     InitField(newx, newy, FALSE);
8033   }
8034   else if (element == EL_BD_MAGIC_WALL_FILLING)
8035   {
8036     element = Feld[newx][newy] = get_next_element(element);
8037     if (!game.magic_wall_active)
8038       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8039     Store[newx][newy] = Store[x][y];
8040   }
8041   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8042   {
8043     Feld[x][y] = get_next_element(element);
8044     if (!game.magic_wall_active)
8045       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8046     element = Feld[newx][newy] = Store[x][y];
8047
8048     InitField(newx, newy, FALSE);
8049   }
8050   else if (element == EL_DC_MAGIC_WALL_FILLING)
8051   {
8052     element = Feld[newx][newy] = get_next_element(element);
8053     if (!game.magic_wall_active)
8054       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8055     Store[newx][newy] = Store[x][y];
8056   }
8057   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8058   {
8059     Feld[x][y] = get_next_element(element);
8060     if (!game.magic_wall_active)
8061       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8062     element = Feld[newx][newy] = Store[x][y];
8063
8064     InitField(newx, newy, FALSE);
8065   }
8066   else if (element == EL_AMOEBA_DROPPING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     element = Feld[newx][newy] = Store[x][y];
8070   }
8071   else if (element == EL_SOKOBAN_OBJECT)
8072   {
8073     if (Back[x][y])
8074       Feld[x][y] = Back[x][y];
8075
8076     if (Back[newx][newy])
8077       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8078
8079     Back[x][y] = Back[newx][newy] = 0;
8080   }
8081
8082   Store[x][y] = EL_EMPTY;
8083   MovPos[x][y] = 0;
8084   MovDir[x][y] = 0;
8085   MovDelay[x][y] = 0;
8086
8087   MovDelay[newx][newy] = 0;
8088
8089   if (CAN_CHANGE_OR_HAS_ACTION(element))
8090   {
8091     /* copy element change control values to new field */
8092     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8093     ChangePage[newx][newy]  = ChangePage[x][y];
8094     ChangeCount[newx][newy] = ChangeCount[x][y];
8095     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8096   }
8097
8098   CustomValue[newx][newy] = CustomValue[x][y];
8099
8100   ChangeDelay[x][y] = 0;
8101   ChangePage[x][y] = -1;
8102   ChangeCount[x][y] = 0;
8103   ChangeEvent[x][y] = -1;
8104
8105   CustomValue[x][y] = 0;
8106
8107   /* copy animation control values to new field */
8108   GfxFrame[newx][newy]  = GfxFrame[x][y];
8109   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8110   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8111   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8112
8113   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8114
8115   /* some elements can leave other elements behind after moving */
8116   if (ei->move_leave_element != EL_EMPTY &&
8117       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8118       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8119   {
8120     int move_leave_element = ei->move_leave_element;
8121
8122     /* this makes it possible to leave the removed element again */
8123     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8124       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8125
8126     Feld[x][y] = move_leave_element;
8127
8128     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8129       MovDir[x][y] = direction;
8130
8131     InitField(x, y, FALSE);
8132
8133     if (GFX_CRUMBLED(Feld[x][y]))
8134       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8135
8136     if (ELEM_IS_PLAYER(move_leave_element))
8137       RelocatePlayer(x, y, move_leave_element);
8138   }
8139
8140   /* do this after checking for left-behind element */
8141   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8142
8143   if (!CAN_MOVE(element) ||
8144       (CAN_FALL(element) && direction == MV_DOWN &&
8145        (element == EL_SPRING ||
8146         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8147         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8148     GfxDir[x][y] = MovDir[newx][newy] = 0;
8149
8150   TEST_DrawLevelField(x, y);
8151   TEST_DrawLevelField(newx, newy);
8152
8153   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8154
8155   /* prevent pushed element from moving on in pushed direction */
8156   if (pushed_by_player && CAN_MOVE(element) &&
8157       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8158       !(element_info[element].move_pattern & direction))
8159     TurnRound(newx, newy);
8160
8161   /* prevent elements on conveyor belt from moving on in last direction */
8162   if (pushed_by_conveyor && CAN_FALL(element) &&
8163       direction & MV_HORIZONTAL)
8164     MovDir[newx][newy] = 0;
8165
8166   if (!pushed_by_player)
8167   {
8168     int nextx = newx + dx, nexty = newy + dy;
8169     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8170
8171     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8172
8173     if (CAN_FALL(element) && direction == MV_DOWN)
8174       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8175
8176     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8177       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8178
8179     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8180       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8181   }
8182
8183   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8184   {
8185     TestIfBadThingTouchesPlayer(newx, newy);
8186     TestIfBadThingTouchesFriend(newx, newy);
8187
8188     if (!IS_CUSTOM_ELEMENT(element))
8189       TestIfBadThingTouchesOtherBadThing(newx, newy);
8190   }
8191   else if (element == EL_PENGUIN)
8192     TestIfFriendTouchesBadThing(newx, newy);
8193
8194   if (DONT_GET_HIT_BY(element))
8195   {
8196     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8197   }
8198
8199   /* give the player one last chance (one more frame) to move away */
8200   if (CAN_FALL(element) && direction == MV_DOWN &&
8201       (last_line || (!IS_FREE(x, newy + 1) &&
8202                      (!IS_PLAYER(x, newy + 1) ||
8203                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8204     Impact(x, newy);
8205
8206   if (pushed_by_player && !game.use_change_when_pushing_bug)
8207   {
8208     int push_side = MV_DIR_OPPOSITE(direction);
8209     struct PlayerInfo *player = PLAYERINFO(x, y);
8210
8211     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8212                                player->index_bit, push_side);
8213     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8214                                         player->index_bit, push_side);
8215   }
8216
8217   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8218     MovDelay[newx][newy] = 1;
8219
8220   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8221
8222   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8223   TestIfElementHitsCustomElement(newx, newy, direction);
8224   TestIfPlayerTouchesCustomElement(newx, newy);
8225   TestIfElementTouchesCustomElement(newx, newy);
8226
8227   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8228       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8229     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8230                              MV_DIR_OPPOSITE(direction));
8231 }
8232
8233 int AmoebeNachbarNr(int ax, int ay)
8234 {
8235   int i;
8236   int element = Feld[ax][ay];
8237   int group_nr = 0;
8238   static int xy[4][2] =
8239   {
8240     { 0, -1 },
8241     { -1, 0 },
8242     { +1, 0 },
8243     { 0, +1 }
8244   };
8245
8246   for (i = 0; i < NUM_DIRECTIONS; i++)
8247   {
8248     int x = ax + xy[i][0];
8249     int y = ay + xy[i][1];
8250
8251     if (!IN_LEV_FIELD(x, y))
8252       continue;
8253
8254     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8255       group_nr = AmoebaNr[x][y];
8256   }
8257
8258   return group_nr;
8259 }
8260
8261 void AmoebenVereinigen(int ax, int ay)
8262 {
8263   int i, x, y, xx, yy;
8264   int new_group_nr = AmoebaNr[ax][ay];
8265   static int xy[4][2] =
8266   {
8267     { 0, -1 },
8268     { -1, 0 },
8269     { +1, 0 },
8270     { 0, +1 }
8271   };
8272
8273   if (new_group_nr == 0)
8274     return;
8275
8276   for (i = 0; i < NUM_DIRECTIONS; i++)
8277   {
8278     x = ax + xy[i][0];
8279     y = ay + xy[i][1];
8280
8281     if (!IN_LEV_FIELD(x, y))
8282       continue;
8283
8284     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8285          Feld[x][y] == EL_BD_AMOEBA ||
8286          Feld[x][y] == EL_AMOEBA_DEAD) &&
8287         AmoebaNr[x][y] != new_group_nr)
8288     {
8289       int old_group_nr = AmoebaNr[x][y];
8290
8291       if (old_group_nr == 0)
8292         return;
8293
8294       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8295       AmoebaCnt[old_group_nr] = 0;
8296       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8297       AmoebaCnt2[old_group_nr] = 0;
8298
8299       SCAN_PLAYFIELD(xx, yy)
8300       {
8301         if (AmoebaNr[xx][yy] == old_group_nr)
8302           AmoebaNr[xx][yy] = new_group_nr;
8303       }
8304     }
8305   }
8306 }
8307
8308 void AmoebeUmwandeln(int ax, int ay)
8309 {
8310   int i, x, y;
8311
8312   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8313   {
8314     int group_nr = AmoebaNr[ax][ay];
8315
8316 #ifdef DEBUG
8317     if (group_nr == 0)
8318     {
8319       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8320       printf("AmoebeUmwandeln(): This should never happen!\n");
8321       return;
8322     }
8323 #endif
8324
8325     SCAN_PLAYFIELD(x, y)
8326     {
8327       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8328       {
8329         AmoebaNr[x][y] = 0;
8330         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8331       }
8332     }
8333
8334     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8335                             SND_AMOEBA_TURNING_TO_GEM :
8336                             SND_AMOEBA_TURNING_TO_ROCK));
8337     Bang(ax, ay);
8338   }
8339   else
8340   {
8341     static int xy[4][2] =
8342     {
8343       { 0, -1 },
8344       { -1, 0 },
8345       { +1, 0 },
8346       { 0, +1 }
8347     };
8348
8349     for (i = 0; i < NUM_DIRECTIONS; i++)
8350     {
8351       x = ax + xy[i][0];
8352       y = ay + xy[i][1];
8353
8354       if (!IN_LEV_FIELD(x, y))
8355         continue;
8356
8357       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8358       {
8359         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8360                               SND_AMOEBA_TURNING_TO_GEM :
8361                               SND_AMOEBA_TURNING_TO_ROCK));
8362         Bang(x, y);
8363       }
8364     }
8365   }
8366 }
8367
8368 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8369 {
8370   int x, y;
8371   int group_nr = AmoebaNr[ax][ay];
8372   boolean done = FALSE;
8373
8374 #ifdef DEBUG
8375   if (group_nr == 0)
8376   {
8377     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8378     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8379     return;
8380   }
8381 #endif
8382
8383   SCAN_PLAYFIELD(x, y)
8384   {
8385     if (AmoebaNr[x][y] == group_nr &&
8386         (Feld[x][y] == EL_AMOEBA_DEAD ||
8387          Feld[x][y] == EL_BD_AMOEBA ||
8388          Feld[x][y] == EL_AMOEBA_GROWING))
8389     {
8390       AmoebaNr[x][y] = 0;
8391       Feld[x][y] = new_element;
8392       InitField(x, y, FALSE);
8393       TEST_DrawLevelField(x, y);
8394       done = TRUE;
8395     }
8396   }
8397
8398   if (done)
8399     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8400                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8401                             SND_BD_AMOEBA_TURNING_TO_GEM));
8402 }
8403
8404 void AmoebeWaechst(int x, int y)
8405 {
8406   static unsigned int sound_delay = 0;
8407   static unsigned int sound_delay_value = 0;
8408
8409   if (!MovDelay[x][y])          /* start new growing cycle */
8410   {
8411     MovDelay[x][y] = 7;
8412
8413     if (DelayReached(&sound_delay, sound_delay_value))
8414     {
8415       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8416       sound_delay_value = 30;
8417     }
8418   }
8419
8420   if (MovDelay[x][y])           /* wait some time before growing bigger */
8421   {
8422     MovDelay[x][y]--;
8423     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8424     {
8425       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8426                                            6 - MovDelay[x][y]);
8427
8428       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8429     }
8430
8431     if (!MovDelay[x][y])
8432     {
8433       Feld[x][y] = Store[x][y];
8434       Store[x][y] = 0;
8435       TEST_DrawLevelField(x, y);
8436     }
8437   }
8438 }
8439
8440 void AmoebaDisappearing(int x, int y)
8441 {
8442   static unsigned int sound_delay = 0;
8443   static unsigned int sound_delay_value = 0;
8444
8445   if (!MovDelay[x][y])          /* start new shrinking cycle */
8446   {
8447     MovDelay[x][y] = 7;
8448
8449     if (DelayReached(&sound_delay, sound_delay_value))
8450       sound_delay_value = 30;
8451   }
8452
8453   if (MovDelay[x][y])           /* wait some time before shrinking */
8454   {
8455     MovDelay[x][y]--;
8456     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8457     {
8458       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8459                                            6 - MovDelay[x][y]);
8460
8461       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8462     }
8463
8464     if (!MovDelay[x][y])
8465     {
8466       Feld[x][y] = EL_EMPTY;
8467       TEST_DrawLevelField(x, y);
8468
8469       /* don't let mole enter this field in this cycle;
8470          (give priority to objects falling to this field from above) */
8471       Stop[x][y] = TRUE;
8472     }
8473   }
8474 }
8475
8476 void AmoebeAbleger(int ax, int ay)
8477 {
8478   int i;
8479   int element = Feld[ax][ay];
8480   int graphic = el2img(element);
8481   int newax = ax, neway = ay;
8482   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8483   static int xy[4][2] =
8484   {
8485     { 0, -1 },
8486     { -1, 0 },
8487     { +1, 0 },
8488     { 0, +1 }
8489   };
8490
8491   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8492   {
8493     Feld[ax][ay] = EL_AMOEBA_DEAD;
8494     TEST_DrawLevelField(ax, ay);
8495     return;
8496   }
8497
8498   if (IS_ANIMATED(graphic))
8499     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8500
8501   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8502     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8503
8504   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8505   {
8506     MovDelay[ax][ay]--;
8507     if (MovDelay[ax][ay])
8508       return;
8509   }
8510
8511   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8512   {
8513     int start = RND(4);
8514     int x = ax + xy[start][0];
8515     int y = ay + xy[start][1];
8516
8517     if (!IN_LEV_FIELD(x, y))
8518       return;
8519
8520     if (IS_FREE(x, y) ||
8521         CAN_GROW_INTO(Feld[x][y]) ||
8522         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8523         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8524     {
8525       newax = x;
8526       neway = y;
8527     }
8528
8529     if (newax == ax && neway == ay)
8530       return;
8531   }
8532   else                          /* normal or "filled" (BD style) amoeba */
8533   {
8534     int start = RND(4);
8535     boolean waiting_for_player = FALSE;
8536
8537     for (i = 0; i < NUM_DIRECTIONS; i++)
8538     {
8539       int j = (start + i) % 4;
8540       int x = ax + xy[j][0];
8541       int y = ay + xy[j][1];
8542
8543       if (!IN_LEV_FIELD(x, y))
8544         continue;
8545
8546       if (IS_FREE(x, y) ||
8547           CAN_GROW_INTO(Feld[x][y]) ||
8548           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8549           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8550       {
8551         newax = x;
8552         neway = y;
8553         break;
8554       }
8555       else if (IS_PLAYER(x, y))
8556         waiting_for_player = TRUE;
8557     }
8558
8559     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8560     {
8561       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8562       {
8563         Feld[ax][ay] = EL_AMOEBA_DEAD;
8564         TEST_DrawLevelField(ax, ay);
8565         AmoebaCnt[AmoebaNr[ax][ay]]--;
8566
8567         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8568         {
8569           if (element == EL_AMOEBA_FULL)
8570             AmoebeUmwandeln(ax, ay);
8571           else if (element == EL_BD_AMOEBA)
8572             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8573         }
8574       }
8575       return;
8576     }
8577     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8578     {
8579       /* amoeba gets larger by growing in some direction */
8580
8581       int new_group_nr = AmoebaNr[ax][ay];
8582
8583 #ifdef DEBUG
8584   if (new_group_nr == 0)
8585   {
8586     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8587     printf("AmoebeAbleger(): This should never happen!\n");
8588     return;
8589   }
8590 #endif
8591
8592       AmoebaNr[newax][neway] = new_group_nr;
8593       AmoebaCnt[new_group_nr]++;
8594       AmoebaCnt2[new_group_nr]++;
8595
8596       /* if amoeba touches other amoeba(s) after growing, unify them */
8597       AmoebenVereinigen(newax, neway);
8598
8599       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8600       {
8601         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8602         return;
8603       }
8604     }
8605   }
8606
8607   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8608       (neway == lev_fieldy - 1 && newax != ax))
8609   {
8610     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8611     Store[newax][neway] = element;
8612   }
8613   else if (neway == ay || element == EL_EMC_DRIPPER)
8614   {
8615     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8616
8617     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8618   }
8619   else
8620   {
8621     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8622     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8623     Store[ax][ay] = EL_AMOEBA_DROP;
8624     ContinueMoving(ax, ay);
8625     return;
8626   }
8627
8628   TEST_DrawLevelField(newax, neway);
8629 }
8630
8631 void Life(int ax, int ay)
8632 {
8633   int x1, y1, x2, y2;
8634   int life_time = 40;
8635   int element = Feld[ax][ay];
8636   int graphic = el2img(element);
8637   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8638                          level.biomaze);
8639   boolean changed = FALSE;
8640
8641   if (IS_ANIMATED(graphic))
8642     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8643
8644   if (Stop[ax][ay])
8645     return;
8646
8647   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8648     MovDelay[ax][ay] = life_time;
8649
8650   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8651   {
8652     MovDelay[ax][ay]--;
8653     if (MovDelay[ax][ay])
8654       return;
8655   }
8656
8657   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8658   {
8659     int xx = ax+x1, yy = ay+y1;
8660     int nachbarn = 0;
8661
8662     if (!IN_LEV_FIELD(xx, yy))
8663       continue;
8664
8665     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8666     {
8667       int x = xx+x2, y = yy+y2;
8668
8669       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8670         continue;
8671
8672       if (((Feld[x][y] == element ||
8673             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8674            !Stop[x][y]) ||
8675           (IS_FREE(x, y) && Stop[x][y]))
8676         nachbarn++;
8677     }
8678
8679     if (xx == ax && yy == ay)           /* field in the middle */
8680     {
8681       if (nachbarn < life_parameter[0] ||
8682           nachbarn > life_parameter[1])
8683       {
8684         Feld[xx][yy] = EL_EMPTY;
8685         if (!Stop[xx][yy])
8686           TEST_DrawLevelField(xx, yy);
8687         Stop[xx][yy] = TRUE;
8688         changed = TRUE;
8689       }
8690     }
8691     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8692     {                                   /* free border field */
8693       if (nachbarn >= life_parameter[2] &&
8694           nachbarn <= life_parameter[3])
8695       {
8696         Feld[xx][yy] = element;
8697         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8698         if (!Stop[xx][yy])
8699           TEST_DrawLevelField(xx, yy);
8700         Stop[xx][yy] = TRUE;
8701         changed = TRUE;
8702       }
8703     }
8704   }
8705
8706   if (changed)
8707     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8708                    SND_GAME_OF_LIFE_GROWING);
8709 }
8710
8711 static void InitRobotWheel(int x, int y)
8712 {
8713   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8714 }
8715
8716 static void RunRobotWheel(int x, int y)
8717 {
8718   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8719 }
8720
8721 static void StopRobotWheel(int x, int y)
8722 {
8723   if (ZX == x && ZY == y)
8724   {
8725     ZX = ZY = -1;
8726
8727     game.robot_wheel_active = FALSE;
8728   }
8729 }
8730
8731 static void InitTimegateWheel(int x, int y)
8732 {
8733   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8734 }
8735
8736 static void RunTimegateWheel(int x, int y)
8737 {
8738   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8739 }
8740
8741 static void InitMagicBallDelay(int x, int y)
8742 {
8743   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8744 }
8745
8746 static void ActivateMagicBall(int bx, int by)
8747 {
8748   int x, y;
8749
8750   if (level.ball_random)
8751   {
8752     int pos_border = RND(8);    /* select one of the eight border elements */
8753     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8754     int xx = pos_content % 3;
8755     int yy = pos_content / 3;
8756
8757     x = bx - 1 + xx;
8758     y = by - 1 + yy;
8759
8760     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8761       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8762   }
8763   else
8764   {
8765     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8766     {
8767       int xx = x - bx + 1;
8768       int yy = y - by + 1;
8769
8770       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8771         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8772     }
8773   }
8774
8775   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8776 }
8777
8778 void CheckExit(int x, int y)
8779 {
8780   if (local_player->gems_still_needed > 0 ||
8781       local_player->sokobanfields_still_needed > 0 ||
8782       local_player->lights_still_needed > 0)
8783   {
8784     int element = Feld[x][y];
8785     int graphic = el2img(element);
8786
8787     if (IS_ANIMATED(graphic))
8788       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8789
8790     return;
8791   }
8792
8793   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8794     return;
8795
8796   Feld[x][y] = EL_EXIT_OPENING;
8797
8798   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8799 }
8800
8801 void CheckExitEM(int x, int y)
8802 {
8803   if (local_player->gems_still_needed > 0 ||
8804       local_player->sokobanfields_still_needed > 0 ||
8805       local_player->lights_still_needed > 0)
8806   {
8807     int element = Feld[x][y];
8808     int graphic = el2img(element);
8809
8810     if (IS_ANIMATED(graphic))
8811       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8812
8813     return;
8814   }
8815
8816   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8817     return;
8818
8819   Feld[x][y] = EL_EM_EXIT_OPENING;
8820
8821   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8822 }
8823
8824 void CheckExitSteel(int x, int y)
8825 {
8826   if (local_player->gems_still_needed > 0 ||
8827       local_player->sokobanfields_still_needed > 0 ||
8828       local_player->lights_still_needed > 0)
8829   {
8830     int element = Feld[x][y];
8831     int graphic = el2img(element);
8832
8833     if (IS_ANIMATED(graphic))
8834       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8835
8836     return;
8837   }
8838
8839   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8840     return;
8841
8842   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8843
8844   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8845 }
8846
8847 void CheckExitSteelEM(int x, int y)
8848 {
8849   if (local_player->gems_still_needed > 0 ||
8850       local_player->sokobanfields_still_needed > 0 ||
8851       local_player->lights_still_needed > 0)
8852   {
8853     int element = Feld[x][y];
8854     int graphic = el2img(element);
8855
8856     if (IS_ANIMATED(graphic))
8857       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8858
8859     return;
8860   }
8861
8862   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8863     return;
8864
8865   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8866
8867   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8868 }
8869
8870 void CheckExitSP(int x, int y)
8871 {
8872   if (local_player->gems_still_needed > 0)
8873   {
8874     int element = Feld[x][y];
8875     int graphic = el2img(element);
8876
8877     if (IS_ANIMATED(graphic))
8878       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879
8880     return;
8881   }
8882
8883   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8884     return;
8885
8886   Feld[x][y] = EL_SP_EXIT_OPENING;
8887
8888   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8889 }
8890
8891 static void CloseAllOpenTimegates()
8892 {
8893   int x, y;
8894
8895   SCAN_PLAYFIELD(x, y)
8896   {
8897     int element = Feld[x][y];
8898
8899     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8900     {
8901       Feld[x][y] = EL_TIMEGATE_CLOSING;
8902
8903       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8904     }
8905   }
8906 }
8907
8908 void DrawTwinkleOnField(int x, int y)
8909 {
8910   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8911     return;
8912
8913   if (Feld[x][y] == EL_BD_DIAMOND)
8914     return;
8915
8916   if (MovDelay[x][y] == 0)      /* next animation frame */
8917     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8918
8919   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8920   {
8921     MovDelay[x][y]--;
8922
8923     DrawLevelElementAnimation(x, y, Feld[x][y]);
8924
8925     if (MovDelay[x][y] != 0)
8926     {
8927       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8928                                            10 - MovDelay[x][y]);
8929
8930       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8931     }
8932   }
8933 }
8934
8935 void MauerWaechst(int x, int y)
8936 {
8937   int delay = 6;
8938
8939   if (!MovDelay[x][y])          /* next animation frame */
8940     MovDelay[x][y] = 3 * delay;
8941
8942   if (MovDelay[x][y])           /* wait some time before next frame */
8943   {
8944     MovDelay[x][y]--;
8945
8946     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8947     {
8948       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8949       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8950
8951       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8952     }
8953
8954     if (!MovDelay[x][y])
8955     {
8956       if (MovDir[x][y] == MV_LEFT)
8957       {
8958         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8959           TEST_DrawLevelField(x - 1, y);
8960       }
8961       else if (MovDir[x][y] == MV_RIGHT)
8962       {
8963         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8964           TEST_DrawLevelField(x + 1, y);
8965       }
8966       else if (MovDir[x][y] == MV_UP)
8967       {
8968         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8969           TEST_DrawLevelField(x, y - 1);
8970       }
8971       else
8972       {
8973         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8974           TEST_DrawLevelField(x, y + 1);
8975       }
8976
8977       Feld[x][y] = Store[x][y];
8978       Store[x][y] = 0;
8979       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8980       TEST_DrawLevelField(x, y);
8981     }
8982   }
8983 }
8984
8985 void MauerAbleger(int ax, int ay)
8986 {
8987   int element = Feld[ax][ay];
8988   int graphic = el2img(element);
8989   boolean oben_frei = FALSE, unten_frei = FALSE;
8990   boolean links_frei = FALSE, rechts_frei = FALSE;
8991   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8992   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8993   boolean new_wall = FALSE;
8994
8995   if (IS_ANIMATED(graphic))
8996     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8997
8998   if (!MovDelay[ax][ay])        /* start building new wall */
8999     MovDelay[ax][ay] = 6;
9000
9001   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9002   {
9003     MovDelay[ax][ay]--;
9004     if (MovDelay[ax][ay])
9005       return;
9006   }
9007
9008   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9009     oben_frei = TRUE;
9010   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9011     unten_frei = TRUE;
9012   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9013     links_frei = TRUE;
9014   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9015     rechts_frei = TRUE;
9016
9017   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9018       element == EL_EXPANDABLE_WALL_ANY)
9019   {
9020     if (oben_frei)
9021     {
9022       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9023       Store[ax][ay-1] = element;
9024       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9025       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9026         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9027                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9028       new_wall = TRUE;
9029     }
9030     if (unten_frei)
9031     {
9032       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9033       Store[ax][ay+1] = element;
9034       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9035       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9036         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9037                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9038       new_wall = TRUE;
9039     }
9040   }
9041
9042   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9043       element == EL_EXPANDABLE_WALL_ANY ||
9044       element == EL_EXPANDABLE_WALL ||
9045       element == EL_BD_EXPANDABLE_WALL)
9046   {
9047     if (links_frei)
9048     {
9049       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9050       Store[ax-1][ay] = element;
9051       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9052       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9053         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9054                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9055       new_wall = TRUE;
9056     }
9057
9058     if (rechts_frei)
9059     {
9060       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9061       Store[ax+1][ay] = element;
9062       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9063       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9064         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9065                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9066       new_wall = TRUE;
9067     }
9068   }
9069
9070   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9071     TEST_DrawLevelField(ax, ay);
9072
9073   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9074     oben_massiv = TRUE;
9075   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9076     unten_massiv = TRUE;
9077   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9078     links_massiv = TRUE;
9079   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9080     rechts_massiv = TRUE;
9081
9082   if (((oben_massiv && unten_massiv) ||
9083        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9084        element == EL_EXPANDABLE_WALL) &&
9085       ((links_massiv && rechts_massiv) ||
9086        element == EL_EXPANDABLE_WALL_VERTICAL))
9087     Feld[ax][ay] = EL_WALL;
9088
9089   if (new_wall)
9090     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9091 }
9092
9093 void MauerAblegerStahl(int ax, int ay)
9094 {
9095   int element = Feld[ax][ay];
9096   int graphic = el2img(element);
9097   boolean oben_frei = FALSE, unten_frei = FALSE;
9098   boolean links_frei = FALSE, rechts_frei = FALSE;
9099   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9100   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9101   boolean new_wall = FALSE;
9102
9103   if (IS_ANIMATED(graphic))
9104     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9105
9106   if (!MovDelay[ax][ay])        /* start building new wall */
9107     MovDelay[ax][ay] = 6;
9108
9109   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9110   {
9111     MovDelay[ax][ay]--;
9112     if (MovDelay[ax][ay])
9113       return;
9114   }
9115
9116   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9117     oben_frei = TRUE;
9118   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9119     unten_frei = TRUE;
9120   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9121     links_frei = TRUE;
9122   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9123     rechts_frei = TRUE;
9124
9125   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9126       element == EL_EXPANDABLE_STEELWALL_ANY)
9127   {
9128     if (oben_frei)
9129     {
9130       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9131       Store[ax][ay-1] = element;
9132       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9133       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9134         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9135                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9136       new_wall = TRUE;
9137     }
9138     if (unten_frei)
9139     {
9140       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9141       Store[ax][ay+1] = element;
9142       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9143       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9144         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9145                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9146       new_wall = TRUE;
9147     }
9148   }
9149
9150   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9151       element == EL_EXPANDABLE_STEELWALL_ANY)
9152   {
9153     if (links_frei)
9154     {
9155       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9156       Store[ax-1][ay] = element;
9157       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9158       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9159         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9160                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9161       new_wall = TRUE;
9162     }
9163
9164     if (rechts_frei)
9165     {
9166       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9167       Store[ax+1][ay] = element;
9168       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9169       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9170         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9171                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9172       new_wall = TRUE;
9173     }
9174   }
9175
9176   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9177     oben_massiv = TRUE;
9178   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9179     unten_massiv = TRUE;
9180   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9181     links_massiv = TRUE;
9182   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9183     rechts_massiv = TRUE;
9184
9185   if (((oben_massiv && unten_massiv) ||
9186        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9187       ((links_massiv && rechts_massiv) ||
9188        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9189     Feld[ax][ay] = EL_STEELWALL;
9190
9191   if (new_wall)
9192     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9193 }
9194
9195 void CheckForDragon(int x, int y)
9196 {
9197   int i, j;
9198   boolean dragon_found = FALSE;
9199   static int xy[4][2] =
9200   {
9201     { 0, -1 },
9202     { -1, 0 },
9203     { +1, 0 },
9204     { 0, +1 }
9205   };
9206
9207   for (i = 0; i < NUM_DIRECTIONS; i++)
9208   {
9209     for (j = 0; j < 4; j++)
9210     {
9211       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9212
9213       if (IN_LEV_FIELD(xx, yy) &&
9214           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9215       {
9216         if (Feld[xx][yy] == EL_DRAGON)
9217           dragon_found = TRUE;
9218       }
9219       else
9220         break;
9221     }
9222   }
9223
9224   if (!dragon_found)
9225   {
9226     for (i = 0; i < NUM_DIRECTIONS; i++)
9227     {
9228       for (j = 0; j < 3; j++)
9229       {
9230         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9231   
9232         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9233         {
9234           Feld[xx][yy] = EL_EMPTY;
9235           TEST_DrawLevelField(xx, yy);
9236         }
9237         else
9238           break;
9239       }
9240     }
9241   }
9242 }
9243
9244 static void InitBuggyBase(int x, int y)
9245 {
9246   int element = Feld[x][y];
9247   int activating_delay = FRAMES_PER_SECOND / 4;
9248
9249   ChangeDelay[x][y] =
9250     (element == EL_SP_BUGGY_BASE ?
9251      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9252      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9253      activating_delay :
9254      element == EL_SP_BUGGY_BASE_ACTIVE ?
9255      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9256 }
9257
9258 static void WarnBuggyBase(int x, int y)
9259 {
9260   int i;
9261   static int xy[4][2] =
9262   {
9263     { 0, -1 },
9264     { -1, 0 },
9265     { +1, 0 },
9266     { 0, +1 }
9267   };
9268
9269   for (i = 0; i < NUM_DIRECTIONS; i++)
9270   {
9271     int xx = x + xy[i][0];
9272     int yy = y + xy[i][1];
9273
9274     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9275     {
9276       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9277
9278       break;
9279     }
9280   }
9281 }
9282
9283 static void InitTrap(int x, int y)
9284 {
9285   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9286 }
9287
9288 static void ActivateTrap(int x, int y)
9289 {
9290   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9291 }
9292
9293 static void ChangeActiveTrap(int x, int y)
9294 {
9295   int graphic = IMG_TRAP_ACTIVE;
9296
9297   /* if new animation frame was drawn, correct crumbled sand border */
9298   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9299     TEST_DrawLevelFieldCrumbled(x, y);
9300 }
9301
9302 static int getSpecialActionElement(int element, int number, int base_element)
9303 {
9304   return (element != EL_EMPTY ? element :
9305           number != -1 ? base_element + number - 1 :
9306           EL_EMPTY);
9307 }
9308
9309 static int getModifiedActionNumber(int value_old, int operator, int operand,
9310                                    int value_min, int value_max)
9311 {
9312   int value_new = (operator == CA_MODE_SET      ? operand :
9313                    operator == CA_MODE_ADD      ? value_old + operand :
9314                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9315                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9316                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9317                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9318                    value_old);
9319
9320   return (value_new < value_min ? value_min :
9321           value_new > value_max ? value_max :
9322           value_new);
9323 }
9324
9325 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9326 {
9327   struct ElementInfo *ei = &element_info[element];
9328   struct ElementChangeInfo *change = &ei->change_page[page];
9329   int target_element = change->target_element;
9330   int action_type = change->action_type;
9331   int action_mode = change->action_mode;
9332   int action_arg = change->action_arg;
9333   int action_element = change->action_element;
9334   int i;
9335
9336   if (!change->has_action)
9337     return;
9338
9339   /* ---------- determine action paramater values -------------------------- */
9340
9341   int level_time_value =
9342     (level.time > 0 ? TimeLeft :
9343      TimePlayed);
9344
9345   int action_arg_element_raw =
9346     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9347      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9348      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9349      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9350      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9351      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9352      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9353      EL_EMPTY);
9354   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9355
9356   int action_arg_direction =
9357     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9358      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9359      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9360      change->actual_trigger_side :
9361      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9362      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9363      MV_NONE);
9364
9365   int action_arg_number_min =
9366     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9367      CA_ARG_MIN);
9368
9369   int action_arg_number_max =
9370     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9371      action_type == CA_SET_LEVEL_GEMS ? 999 :
9372      action_type == CA_SET_LEVEL_TIME ? 9999 :
9373      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9374      action_type == CA_SET_CE_VALUE ? 9999 :
9375      action_type == CA_SET_CE_SCORE ? 9999 :
9376      CA_ARG_MAX);
9377
9378   int action_arg_number_reset =
9379     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9380      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9381      action_type == CA_SET_LEVEL_TIME ? level.time :
9382      action_type == CA_SET_LEVEL_SCORE ? 0 :
9383      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9384      action_type == CA_SET_CE_SCORE ? 0 :
9385      0);
9386
9387   int action_arg_number =
9388     (action_arg <= CA_ARG_MAX ? action_arg :
9389      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9390      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9391      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9392      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9393      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9394      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9395      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9396      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9397      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9398      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9399      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9400      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9401      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9402      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9403      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9404      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9405      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9406      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9407      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9408      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9409      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9410      -1);
9411
9412   int action_arg_number_old =
9413     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9414      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9415      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9416      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9417      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9418      0);
9419
9420   int action_arg_number_new =
9421     getModifiedActionNumber(action_arg_number_old,
9422                             action_mode, action_arg_number,
9423                             action_arg_number_min, action_arg_number_max);
9424
9425   int trigger_player_bits =
9426     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9427      change->actual_trigger_player_bits : change->trigger_player);
9428
9429   int action_arg_player_bits =
9430     (action_arg >= CA_ARG_PLAYER_1 &&
9431      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9432      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9433      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9434      PLAYER_BITS_ANY);
9435
9436   /* ---------- execute action  -------------------------------------------- */
9437
9438   switch (action_type)
9439   {
9440     case CA_NO_ACTION:
9441     {
9442       return;
9443     }
9444
9445     /* ---------- level actions  ------------------------------------------- */
9446
9447     case CA_RESTART_LEVEL:
9448     {
9449       game.restart_level = TRUE;
9450
9451       break;
9452     }
9453
9454     case CA_SHOW_ENVELOPE:
9455     {
9456       int element = getSpecialActionElement(action_arg_element,
9457                                             action_arg_number, EL_ENVELOPE_1);
9458
9459       if (IS_ENVELOPE(element))
9460         local_player->show_envelope = element;
9461
9462       break;
9463     }
9464
9465     case CA_SET_LEVEL_TIME:
9466     {
9467       if (level.time > 0)       /* only modify limited time value */
9468       {
9469         TimeLeft = action_arg_number_new;
9470
9471         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9472
9473         DisplayGameControlValues();
9474
9475         if (!TimeLeft && setup.time_limit)
9476           for (i = 0; i < MAX_PLAYERS; i++)
9477             KillPlayer(&stored_player[i]);
9478       }
9479
9480       break;
9481     }
9482
9483     case CA_SET_LEVEL_SCORE:
9484     {
9485       local_player->score = action_arg_number_new;
9486
9487       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9488
9489       DisplayGameControlValues();
9490
9491       break;
9492     }
9493
9494     case CA_SET_LEVEL_GEMS:
9495     {
9496       local_player->gems_still_needed = action_arg_number_new;
9497
9498       game.snapshot.collected_item = TRUE;
9499
9500       game_panel_controls[GAME_PANEL_GEMS].value =
9501         local_player->gems_still_needed;
9502
9503       DisplayGameControlValues();
9504
9505       break;
9506     }
9507
9508     case CA_SET_LEVEL_WIND:
9509     {
9510       game.wind_direction = action_arg_direction;
9511
9512       break;
9513     }
9514
9515     case CA_SET_LEVEL_RANDOM_SEED:
9516     {
9517       /* ensure that setting a new random seed while playing is predictable */
9518       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9519
9520       break;
9521     }
9522
9523     /* ---------- player actions  ------------------------------------------ */
9524
9525     case CA_MOVE_PLAYER:
9526     {
9527       /* automatically move to the next field in specified direction */
9528       for (i = 0; i < MAX_PLAYERS; i++)
9529         if (trigger_player_bits & (1 << i))
9530           stored_player[i].programmed_action = action_arg_direction;
9531
9532       break;
9533     }
9534
9535     case CA_EXIT_PLAYER:
9536     {
9537       for (i = 0; i < MAX_PLAYERS; i++)
9538         if (action_arg_player_bits & (1 << i))
9539           PlayerWins(&stored_player[i]);
9540
9541       break;
9542     }
9543
9544     case CA_KILL_PLAYER:
9545     {
9546       for (i = 0; i < MAX_PLAYERS; i++)
9547         if (action_arg_player_bits & (1 << i))
9548           KillPlayer(&stored_player[i]);
9549
9550       break;
9551     }
9552
9553     case CA_SET_PLAYER_KEYS:
9554     {
9555       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9556       int element = getSpecialActionElement(action_arg_element,
9557                                             action_arg_number, EL_KEY_1);
9558
9559       if (IS_KEY(element))
9560       {
9561         for (i = 0; i < MAX_PLAYERS; i++)
9562         {
9563           if (trigger_player_bits & (1 << i))
9564           {
9565             stored_player[i].key[KEY_NR(element)] = key_state;
9566
9567             DrawGameDoorValues();
9568           }
9569         }
9570       }
9571
9572       break;
9573     }
9574
9575     case CA_SET_PLAYER_SPEED:
9576     {
9577       for (i = 0; i < MAX_PLAYERS; i++)
9578       {
9579         if (trigger_player_bits & (1 << i))
9580         {
9581           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9582
9583           if (action_arg == CA_ARG_SPEED_FASTER &&
9584               stored_player[i].cannot_move)
9585           {
9586             action_arg_number = STEPSIZE_VERY_SLOW;
9587           }
9588           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9589                    action_arg == CA_ARG_SPEED_FASTER)
9590           {
9591             action_arg_number = 2;
9592             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9593                            CA_MODE_MULTIPLY);
9594           }
9595           else if (action_arg == CA_ARG_NUMBER_RESET)
9596           {
9597             action_arg_number = level.initial_player_stepsize[i];
9598           }
9599
9600           move_stepsize =
9601             getModifiedActionNumber(move_stepsize,
9602                                     action_mode,
9603                                     action_arg_number,
9604                                     action_arg_number_min,
9605                                     action_arg_number_max);
9606
9607           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9608         }
9609       }
9610
9611       break;
9612     }
9613
9614     case CA_SET_PLAYER_SHIELD:
9615     {
9616       for (i = 0; i < MAX_PLAYERS; i++)
9617       {
9618         if (trigger_player_bits & (1 << i))
9619         {
9620           if (action_arg == CA_ARG_SHIELD_OFF)
9621           {
9622             stored_player[i].shield_normal_time_left = 0;
9623             stored_player[i].shield_deadly_time_left = 0;
9624           }
9625           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9626           {
9627             stored_player[i].shield_normal_time_left = 999999;
9628           }
9629           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9630           {
9631             stored_player[i].shield_normal_time_left = 999999;
9632             stored_player[i].shield_deadly_time_left = 999999;
9633           }
9634         }
9635       }
9636
9637       break;
9638     }
9639
9640     case CA_SET_PLAYER_GRAVITY:
9641     {
9642       for (i = 0; i < MAX_PLAYERS; i++)
9643       {
9644         if (trigger_player_bits & (1 << i))
9645         {
9646           stored_player[i].gravity =
9647             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9648              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9649              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9650              stored_player[i].gravity);
9651         }
9652       }
9653
9654       break;
9655     }
9656
9657     case CA_SET_PLAYER_ARTWORK:
9658     {
9659       for (i = 0; i < MAX_PLAYERS; i++)
9660       {
9661         if (trigger_player_bits & (1 << i))
9662         {
9663           int artwork_element = action_arg_element;
9664
9665           if (action_arg == CA_ARG_ELEMENT_RESET)
9666             artwork_element =
9667               (level.use_artwork_element[i] ? level.artwork_element[i] :
9668                stored_player[i].element_nr);
9669
9670           if (stored_player[i].artwork_element != artwork_element)
9671             stored_player[i].Frame = 0;
9672
9673           stored_player[i].artwork_element = artwork_element;
9674
9675           SetPlayerWaiting(&stored_player[i], FALSE);
9676
9677           /* set number of special actions for bored and sleeping animation */
9678           stored_player[i].num_special_action_bored =
9679             get_num_special_action(artwork_element,
9680                                    ACTION_BORING_1, ACTION_BORING_LAST);
9681           stored_player[i].num_special_action_sleeping =
9682             get_num_special_action(artwork_element,
9683                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9684         }
9685       }
9686
9687       break;
9688     }
9689
9690     case CA_SET_PLAYER_INVENTORY:
9691     {
9692       for (i = 0; i < MAX_PLAYERS; i++)
9693       {
9694         struct PlayerInfo *player = &stored_player[i];
9695         int j, k;
9696
9697         if (trigger_player_bits & (1 << i))
9698         {
9699           int inventory_element = action_arg_element;
9700
9701           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9702               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9703               action_arg == CA_ARG_ELEMENT_ACTION)
9704           {
9705             int element = inventory_element;
9706             int collect_count = element_info[element].collect_count_initial;
9707
9708             if (!IS_CUSTOM_ELEMENT(element))
9709               collect_count = 1;
9710
9711             if (collect_count == 0)
9712               player->inventory_infinite_element = element;
9713             else
9714               for (k = 0; k < collect_count; k++)
9715                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9716                   player->inventory_element[player->inventory_size++] =
9717                     element;
9718           }
9719           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9720                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9721                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9722           {
9723             if (player->inventory_infinite_element != EL_UNDEFINED &&
9724                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9725                                      action_arg_element_raw))
9726               player->inventory_infinite_element = EL_UNDEFINED;
9727
9728             for (k = 0, j = 0; j < player->inventory_size; j++)
9729             {
9730               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9731                                         action_arg_element_raw))
9732                 player->inventory_element[k++] = player->inventory_element[j];
9733             }
9734
9735             player->inventory_size = k;
9736           }
9737           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9738           {
9739             if (player->inventory_size > 0)
9740             {
9741               for (j = 0; j < player->inventory_size - 1; j++)
9742                 player->inventory_element[j] = player->inventory_element[j + 1];
9743
9744               player->inventory_size--;
9745             }
9746           }
9747           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9748           {
9749             if (player->inventory_size > 0)
9750               player->inventory_size--;
9751           }
9752           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9753           {
9754             player->inventory_infinite_element = EL_UNDEFINED;
9755             player->inventory_size = 0;
9756           }
9757           else if (action_arg == CA_ARG_INVENTORY_RESET)
9758           {
9759             player->inventory_infinite_element = EL_UNDEFINED;
9760             player->inventory_size = 0;
9761
9762             if (level.use_initial_inventory[i])
9763             {
9764               for (j = 0; j < level.initial_inventory_size[i]; j++)
9765               {
9766                 int element = level.initial_inventory_content[i][j];
9767                 int collect_count = element_info[element].collect_count_initial;
9768
9769                 if (!IS_CUSTOM_ELEMENT(element))
9770                   collect_count = 1;
9771
9772                 if (collect_count == 0)
9773                   player->inventory_infinite_element = element;
9774                 else
9775                   for (k = 0; k < collect_count; k++)
9776                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9777                       player->inventory_element[player->inventory_size++] =
9778                         element;
9779               }
9780             }
9781           }
9782         }
9783       }
9784
9785       break;
9786     }
9787
9788     /* ---------- CE actions  ---------------------------------------------- */
9789
9790     case CA_SET_CE_VALUE:
9791     {
9792       int last_ce_value = CustomValue[x][y];
9793
9794       CustomValue[x][y] = action_arg_number_new;
9795
9796       if (CustomValue[x][y] != last_ce_value)
9797       {
9798         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9799         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9800
9801         if (CustomValue[x][y] == 0)
9802         {
9803           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9804           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9805         }
9806       }
9807
9808       break;
9809     }
9810
9811     case CA_SET_CE_SCORE:
9812     {
9813       int last_ce_score = ei->collect_score;
9814
9815       ei->collect_score = action_arg_number_new;
9816
9817       if (ei->collect_score != last_ce_score)
9818       {
9819         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9820         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9821
9822         if (ei->collect_score == 0)
9823         {
9824           int xx, yy;
9825
9826           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9827           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9828
9829           /*
9830             This is a very special case that seems to be a mixture between
9831             CheckElementChange() and CheckTriggeredElementChange(): while
9832             the first one only affects single elements that are triggered
9833             directly, the second one affects multiple elements in the playfield
9834             that are triggered indirectly by another element. This is a third
9835             case: Changing the CE score always affects multiple identical CEs,
9836             so every affected CE must be checked, not only the single CE for
9837             which the CE score was changed in the first place (as every instance
9838             of that CE shares the same CE score, and therefore also can change)!
9839           */
9840           SCAN_PLAYFIELD(xx, yy)
9841           {
9842             if (Feld[xx][yy] == element)
9843               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9844                                  CE_SCORE_GETS_ZERO);
9845           }
9846         }
9847       }
9848
9849       break;
9850     }
9851
9852     case CA_SET_CE_ARTWORK:
9853     {
9854       int artwork_element = action_arg_element;
9855       boolean reset_frame = FALSE;
9856       int xx, yy;
9857
9858       if (action_arg == CA_ARG_ELEMENT_RESET)
9859         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9860                            element);
9861
9862       if (ei->gfx_element != artwork_element)
9863         reset_frame = TRUE;
9864
9865       ei->gfx_element = artwork_element;
9866
9867       SCAN_PLAYFIELD(xx, yy)
9868       {
9869         if (Feld[xx][yy] == element)
9870         {
9871           if (reset_frame)
9872           {
9873             ResetGfxAnimation(xx, yy);
9874             ResetRandomAnimationValue(xx, yy);
9875           }
9876
9877           TEST_DrawLevelField(xx, yy);
9878         }
9879       }
9880
9881       break;
9882     }
9883
9884     /* ---------- engine actions  ------------------------------------------ */
9885
9886     case CA_SET_ENGINE_SCAN_MODE:
9887     {
9888       InitPlayfieldScanMode(action_arg);
9889
9890       break;
9891     }
9892
9893     default:
9894       break;
9895   }
9896 }
9897
9898 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9899 {
9900   int old_element = Feld[x][y];
9901   int new_element = GetElementFromGroupElement(element);
9902   int previous_move_direction = MovDir[x][y];
9903   int last_ce_value = CustomValue[x][y];
9904   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9905   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9906   boolean add_player_onto_element = (new_element_is_player &&
9907                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9908                                      IS_WALKABLE(old_element));
9909
9910   if (!add_player_onto_element)
9911   {
9912     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9913       RemoveMovingField(x, y);
9914     else
9915       RemoveField(x, y);
9916
9917     Feld[x][y] = new_element;
9918
9919     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9920       MovDir[x][y] = previous_move_direction;
9921
9922     if (element_info[new_element].use_last_ce_value)
9923       CustomValue[x][y] = last_ce_value;
9924
9925     InitField_WithBug1(x, y, FALSE);
9926
9927     new_element = Feld[x][y];   /* element may have changed */
9928
9929     ResetGfxAnimation(x, y);
9930     ResetRandomAnimationValue(x, y);
9931
9932     TEST_DrawLevelField(x, y);
9933
9934     if (GFX_CRUMBLED(new_element))
9935       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9936   }
9937
9938   /* check if element under the player changes from accessible to unaccessible
9939      (needed for special case of dropping element which then changes) */
9940   /* (must be checked after creating new element for walkable group elements) */
9941   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9942       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9943   {
9944     Bang(x, y);
9945
9946     return;
9947   }
9948
9949   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9950   if (new_element_is_player)
9951     RelocatePlayer(x, y, new_element);
9952
9953   if (is_change)
9954     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9955
9956   TestIfBadThingTouchesPlayer(x, y);
9957   TestIfPlayerTouchesCustomElement(x, y);
9958   TestIfElementTouchesCustomElement(x, y);
9959 }
9960
9961 static void CreateField(int x, int y, int element)
9962 {
9963   CreateFieldExt(x, y, element, FALSE);
9964 }
9965
9966 static void CreateElementFromChange(int x, int y, int element)
9967 {
9968   element = GET_VALID_RUNTIME_ELEMENT(element);
9969
9970   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9971   {
9972     int old_element = Feld[x][y];
9973
9974     /* prevent changed element from moving in same engine frame
9975        unless both old and new element can either fall or move */
9976     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9977         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9978       Stop[x][y] = TRUE;
9979   }
9980
9981   CreateFieldExt(x, y, element, TRUE);
9982 }
9983
9984 static boolean ChangeElement(int x, int y, int element, int page)
9985 {
9986   struct ElementInfo *ei = &element_info[element];
9987   struct ElementChangeInfo *change = &ei->change_page[page];
9988   int ce_value = CustomValue[x][y];
9989   int ce_score = ei->collect_score;
9990   int target_element;
9991   int old_element = Feld[x][y];
9992
9993   /* always use default change event to prevent running into a loop */
9994   if (ChangeEvent[x][y] == -1)
9995     ChangeEvent[x][y] = CE_DELAY;
9996
9997   if (ChangeEvent[x][y] == CE_DELAY)
9998   {
9999     /* reset actual trigger element, trigger player and action element */
10000     change->actual_trigger_element = EL_EMPTY;
10001     change->actual_trigger_player = EL_EMPTY;
10002     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10003     change->actual_trigger_side = CH_SIDE_NONE;
10004     change->actual_trigger_ce_value = 0;
10005     change->actual_trigger_ce_score = 0;
10006   }
10007
10008   /* do not change elements more than a specified maximum number of changes */
10009   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10010     return FALSE;
10011
10012   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10013
10014   if (change->explode)
10015   {
10016     Bang(x, y);
10017
10018     return TRUE;
10019   }
10020
10021   if (change->use_target_content)
10022   {
10023     boolean complete_replace = TRUE;
10024     boolean can_replace[3][3];
10025     int xx, yy;
10026
10027     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10028     {
10029       boolean is_empty;
10030       boolean is_walkable;
10031       boolean is_diggable;
10032       boolean is_collectible;
10033       boolean is_removable;
10034       boolean is_destructible;
10035       int ex = x + xx - 1;
10036       int ey = y + yy - 1;
10037       int content_element = change->target_content.e[xx][yy];
10038       int e;
10039
10040       can_replace[xx][yy] = TRUE;
10041
10042       if (ex == x && ey == y)   /* do not check changing element itself */
10043         continue;
10044
10045       if (content_element == EL_EMPTY_SPACE)
10046       {
10047         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10048
10049         continue;
10050       }
10051
10052       if (!IN_LEV_FIELD(ex, ey))
10053       {
10054         can_replace[xx][yy] = FALSE;
10055         complete_replace = FALSE;
10056
10057         continue;
10058       }
10059
10060       e = Feld[ex][ey];
10061
10062       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10063         e = MovingOrBlocked2Element(ex, ey);
10064
10065       is_empty = (IS_FREE(ex, ey) ||
10066                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10067
10068       is_walkable     = (is_empty || IS_WALKABLE(e));
10069       is_diggable     = (is_empty || IS_DIGGABLE(e));
10070       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10071       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10072       is_removable    = (is_diggable || is_collectible);
10073
10074       can_replace[xx][yy] =
10075         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10076           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10077           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10078           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10079           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10080           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10081          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10082
10083       if (!can_replace[xx][yy])
10084         complete_replace = FALSE;
10085     }
10086
10087     if (!change->only_if_complete || complete_replace)
10088     {
10089       boolean something_has_changed = FALSE;
10090
10091       if (change->only_if_complete && change->use_random_replace &&
10092           RND(100) < change->random_percentage)
10093         return FALSE;
10094
10095       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10096       {
10097         int ex = x + xx - 1;
10098         int ey = y + yy - 1;
10099         int content_element;
10100
10101         if (can_replace[xx][yy] && (!change->use_random_replace ||
10102                                     RND(100) < change->random_percentage))
10103         {
10104           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10105             RemoveMovingField(ex, ey);
10106
10107           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10108
10109           content_element = change->target_content.e[xx][yy];
10110           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10111                                               ce_value, ce_score);
10112
10113           CreateElementFromChange(ex, ey, target_element);
10114
10115           something_has_changed = TRUE;
10116
10117           /* for symmetry reasons, freeze newly created border elements */
10118           if (ex != x || ey != y)
10119             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10120         }
10121       }
10122
10123       if (something_has_changed)
10124       {
10125         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10126         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10127       }
10128     }
10129   }
10130   else
10131   {
10132     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10133                                         ce_value, ce_score);
10134
10135     if (element == EL_DIAGONAL_GROWING ||
10136         element == EL_DIAGONAL_SHRINKING)
10137     {
10138       target_element = Store[x][y];
10139
10140       Store[x][y] = EL_EMPTY;
10141     }
10142
10143     CreateElementFromChange(x, y, target_element);
10144
10145     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10146     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10147   }
10148
10149   /* this uses direct change before indirect change */
10150   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10151
10152   return TRUE;
10153 }
10154
10155 static void HandleElementChange(int x, int y, int page)
10156 {
10157   int element = MovingOrBlocked2Element(x, y);
10158   struct ElementInfo *ei = &element_info[element];
10159   struct ElementChangeInfo *change = &ei->change_page[page];
10160   boolean handle_action_before_change = FALSE;
10161
10162 #ifdef DEBUG
10163   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10164       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10165   {
10166     printf("\n\n");
10167     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10168            x, y, element, element_info[element].token_name);
10169     printf("HandleElementChange(): This should never happen!\n");
10170     printf("\n\n");
10171   }
10172 #endif
10173
10174   /* this can happen with classic bombs on walkable, changing elements */
10175   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10176   {
10177     return;
10178   }
10179
10180   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10181   {
10182     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10183
10184     if (change->can_change)
10185     {
10186       /* !!! not clear why graphic animation should be reset at all here !!! */
10187       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10188       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10189
10190       /*
10191         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10192
10193         When using an animation frame delay of 1 (this only happens with
10194         "sp_zonk.moving.left/right" in the classic graphics), the default
10195         (non-moving) animation shows wrong animation frames (while the
10196         moving animation, like "sp_zonk.moving.left/right", is correct,
10197         so this graphical bug never shows up with the classic graphics).
10198         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10199         be drawn instead of the correct frames 0,1,2,3. This is caused by
10200         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10201         an element change: First when the change delay ("ChangeDelay[][]")
10202         counter has reached zero after decrementing, then a second time in
10203         the next frame (after "GfxFrame[][]" was already incremented) when
10204         "ChangeDelay[][]" is reset to the initial delay value again.
10205
10206         This causes frame 0 to be drawn twice, while the last frame won't
10207         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10208
10209         As some animations may already be cleverly designed around this bug
10210         (at least the "Snake Bite" snake tail animation does this), it cannot
10211         simply be fixed here without breaking such existing animations.
10212         Unfortunately, it cannot easily be detected if a graphics set was
10213         designed "before" or "after" the bug was fixed. As a workaround,
10214         a new graphics set option "game.graphics_engine_version" was added
10215         to be able to specify the game's major release version for which the
10216         graphics set was designed, which can then be used to decide if the
10217         bugfix should be used (version 4 and above) or not (version 3 or
10218         below, or if no version was specified at all, as with old sets).
10219
10220         (The wrong/fixed animation frames can be tested with the test level set
10221         "test_gfxframe" and level "000", which contains a specially prepared
10222         custom element at level position (x/y) == (11/9) which uses the zonk
10223         animation mentioned above. Using "game.graphics_engine_version: 4"
10224         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10225         This can also be seen from the debug output for this test element.)
10226       */
10227
10228       /* when a custom element is about to change (for example by change delay),
10229          do not reset graphic animation when the custom element is moving */
10230       if (game.graphics_engine_version < 4 &&
10231           !IS_MOVING(x, y))
10232       {
10233         ResetGfxAnimation(x, y);
10234         ResetRandomAnimationValue(x, y);
10235       }
10236
10237       if (change->pre_change_function)
10238         change->pre_change_function(x, y);
10239     }
10240   }
10241
10242   ChangeDelay[x][y]--;
10243
10244   if (ChangeDelay[x][y] != 0)           /* continue element change */
10245   {
10246     if (change->can_change)
10247     {
10248       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10249
10250       if (IS_ANIMATED(graphic))
10251         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10252
10253       if (change->change_function)
10254         change->change_function(x, y);
10255     }
10256   }
10257   else                                  /* finish element change */
10258   {
10259     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10260     {
10261       page = ChangePage[x][y];
10262       ChangePage[x][y] = -1;
10263
10264       change = &ei->change_page[page];
10265     }
10266
10267     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10268     {
10269       ChangeDelay[x][y] = 1;            /* try change after next move step */
10270       ChangePage[x][y] = page;          /* remember page to use for change */
10271
10272       return;
10273     }
10274
10275     /* special case: set new level random seed before changing element */
10276     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10277       handle_action_before_change = TRUE;
10278
10279     if (change->has_action && handle_action_before_change)
10280       ExecuteCustomElementAction(x, y, element, page);
10281
10282     if (change->can_change)
10283     {
10284       if (ChangeElement(x, y, element, page))
10285       {
10286         if (change->post_change_function)
10287           change->post_change_function(x, y);
10288       }
10289     }
10290
10291     if (change->has_action && !handle_action_before_change)
10292       ExecuteCustomElementAction(x, y, element, page);
10293   }
10294 }
10295
10296 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10297                                               int trigger_element,
10298                                               int trigger_event,
10299                                               int trigger_player,
10300                                               int trigger_side,
10301                                               int trigger_page)
10302 {
10303   boolean change_done_any = FALSE;
10304   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10305   int i;
10306
10307   if (!(trigger_events[trigger_element][trigger_event]))
10308     return FALSE;
10309
10310   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10311
10312   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10313   {
10314     int element = EL_CUSTOM_START + i;
10315     boolean change_done = FALSE;
10316     int p;
10317
10318     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10319         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10320       continue;
10321
10322     for (p = 0; p < element_info[element].num_change_pages; p++)
10323     {
10324       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10325
10326       if (change->can_change_or_has_action &&
10327           change->has_event[trigger_event] &&
10328           change->trigger_side & trigger_side &&
10329           change->trigger_player & trigger_player &&
10330           change->trigger_page & trigger_page_bits &&
10331           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10332       {
10333         change->actual_trigger_element = trigger_element;
10334         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10335         change->actual_trigger_player_bits = trigger_player;
10336         change->actual_trigger_side = trigger_side;
10337         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10338         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10339
10340         if ((change->can_change && !change_done) || change->has_action)
10341         {
10342           int x, y;
10343
10344           SCAN_PLAYFIELD(x, y)
10345           {
10346             if (Feld[x][y] == element)
10347             {
10348               if (change->can_change && !change_done)
10349               {
10350                 /* if element already changed in this frame, not only prevent
10351                    another element change (checked in ChangeElement()), but
10352                    also prevent additional element actions for this element */
10353
10354                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10355                     !level.use_action_after_change_bug)
10356                   continue;
10357
10358                 ChangeDelay[x][y] = 1;
10359                 ChangeEvent[x][y] = trigger_event;
10360
10361                 HandleElementChange(x, y, p);
10362               }
10363               else if (change->has_action)
10364               {
10365                 /* if element already changed in this frame, not only prevent
10366                    another element change (checked in ChangeElement()), but
10367                    also prevent additional element actions for this element */
10368
10369                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10370                     !level.use_action_after_change_bug)
10371                   continue;
10372
10373                 ExecuteCustomElementAction(x, y, element, p);
10374                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10375               }
10376             }
10377           }
10378
10379           if (change->can_change)
10380           {
10381             change_done = TRUE;
10382             change_done_any = TRUE;
10383           }
10384         }
10385       }
10386     }
10387   }
10388
10389   RECURSION_LOOP_DETECTION_END();
10390
10391   return change_done_any;
10392 }
10393
10394 static boolean CheckElementChangeExt(int x, int y,
10395                                      int element,
10396                                      int trigger_element,
10397                                      int trigger_event,
10398                                      int trigger_player,
10399                                      int trigger_side)
10400 {
10401   boolean change_done = FALSE;
10402   int p;
10403
10404   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10405       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10406     return FALSE;
10407
10408   if (Feld[x][y] == EL_BLOCKED)
10409   {
10410     Blocked2Moving(x, y, &x, &y);
10411     element = Feld[x][y];
10412   }
10413
10414   /* check if element has already changed or is about to change after moving */
10415   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10416        Feld[x][y] != element) ||
10417
10418       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10419        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10420         ChangePage[x][y] != -1)))
10421     return FALSE;
10422
10423   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10424
10425   for (p = 0; p < element_info[element].num_change_pages; p++)
10426   {
10427     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10428
10429     /* check trigger element for all events where the element that is checked
10430        for changing interacts with a directly adjacent element -- this is
10431        different to element changes that affect other elements to change on the
10432        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10433     boolean check_trigger_element =
10434       (trigger_event == CE_TOUCHING_X ||
10435        trigger_event == CE_HITTING_X ||
10436        trigger_event == CE_HIT_BY_X ||
10437        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10438
10439     if (change->can_change_or_has_action &&
10440         change->has_event[trigger_event] &&
10441         change->trigger_side & trigger_side &&
10442         change->trigger_player & trigger_player &&
10443         (!check_trigger_element ||
10444          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10445     {
10446       change->actual_trigger_element = trigger_element;
10447       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10448       change->actual_trigger_player_bits = trigger_player;
10449       change->actual_trigger_side = trigger_side;
10450       change->actual_trigger_ce_value = CustomValue[x][y];
10451       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10452
10453       /* special case: trigger element not at (x,y) position for some events */
10454       if (check_trigger_element)
10455       {
10456         static struct
10457         {
10458           int dx, dy;
10459         } move_xy[] =
10460           {
10461             {  0,  0 },
10462             { -1,  0 },
10463             { +1,  0 },
10464             {  0,  0 },
10465             {  0, -1 },
10466             {  0,  0 }, { 0, 0 }, { 0, 0 },
10467             {  0, +1 }
10468           };
10469
10470         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10471         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10472
10473         change->actual_trigger_ce_value = CustomValue[xx][yy];
10474         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10475       }
10476
10477       if (change->can_change && !change_done)
10478       {
10479         ChangeDelay[x][y] = 1;
10480         ChangeEvent[x][y] = trigger_event;
10481
10482         HandleElementChange(x, y, p);
10483
10484         change_done = TRUE;
10485       }
10486       else if (change->has_action)
10487       {
10488         ExecuteCustomElementAction(x, y, element, p);
10489         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10490       }
10491     }
10492   }
10493
10494   RECURSION_LOOP_DETECTION_END();
10495
10496   return change_done;
10497 }
10498
10499 static void PlayPlayerSound(struct PlayerInfo *player)
10500 {
10501   int jx = player->jx, jy = player->jy;
10502   int sound_element = player->artwork_element;
10503   int last_action = player->last_action_waiting;
10504   int action = player->action_waiting;
10505
10506   if (player->is_waiting)
10507   {
10508     if (action != last_action)
10509       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10510     else
10511       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10512   }
10513   else
10514   {
10515     if (action != last_action)
10516       StopSound(element_info[sound_element].sound[last_action]);
10517
10518     if (last_action == ACTION_SLEEPING)
10519       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10520   }
10521 }
10522
10523 static void PlayAllPlayersSound()
10524 {
10525   int i;
10526
10527   for (i = 0; i < MAX_PLAYERS; i++)
10528     if (stored_player[i].active)
10529       PlayPlayerSound(&stored_player[i]);
10530 }
10531
10532 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10533 {
10534   boolean last_waiting = player->is_waiting;
10535   int move_dir = player->MovDir;
10536
10537   player->dir_waiting = move_dir;
10538   player->last_action_waiting = player->action_waiting;
10539
10540   if (is_waiting)
10541   {
10542     if (!last_waiting)          /* not waiting -> waiting */
10543     {
10544       player->is_waiting = TRUE;
10545
10546       player->frame_counter_bored =
10547         FrameCounter +
10548         game.player_boring_delay_fixed +
10549         GetSimpleRandom(game.player_boring_delay_random);
10550       player->frame_counter_sleeping =
10551         FrameCounter +
10552         game.player_sleeping_delay_fixed +
10553         GetSimpleRandom(game.player_sleeping_delay_random);
10554
10555       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10556     }
10557
10558     if (game.player_sleeping_delay_fixed +
10559         game.player_sleeping_delay_random > 0 &&
10560         player->anim_delay_counter == 0 &&
10561         player->post_delay_counter == 0 &&
10562         FrameCounter >= player->frame_counter_sleeping)
10563       player->is_sleeping = TRUE;
10564     else if (game.player_boring_delay_fixed +
10565              game.player_boring_delay_random > 0 &&
10566              FrameCounter >= player->frame_counter_bored)
10567       player->is_bored = TRUE;
10568
10569     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10570                               player->is_bored ? ACTION_BORING :
10571                               ACTION_WAITING);
10572
10573     if (player->is_sleeping && player->use_murphy)
10574     {
10575       /* special case for sleeping Murphy when leaning against non-free tile */
10576
10577       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10578           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10579            !IS_MOVING(player->jx - 1, player->jy)))
10580         move_dir = MV_LEFT;
10581       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10582                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10583                 !IS_MOVING(player->jx + 1, player->jy)))
10584         move_dir = MV_RIGHT;
10585       else
10586         player->is_sleeping = FALSE;
10587
10588       player->dir_waiting = move_dir;
10589     }
10590
10591     if (player->is_sleeping)
10592     {
10593       if (player->num_special_action_sleeping > 0)
10594       {
10595         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10596         {
10597           int last_special_action = player->special_action_sleeping;
10598           int num_special_action = player->num_special_action_sleeping;
10599           int special_action =
10600             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10601              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10602              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10603              last_special_action + 1 : ACTION_SLEEPING);
10604           int special_graphic =
10605             el_act_dir2img(player->artwork_element, special_action, move_dir);
10606
10607           player->anim_delay_counter =
10608             graphic_info[special_graphic].anim_delay_fixed +
10609             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10610           player->post_delay_counter =
10611             graphic_info[special_graphic].post_delay_fixed +
10612             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10613
10614           player->special_action_sleeping = special_action;
10615         }
10616
10617         if (player->anim_delay_counter > 0)
10618         {
10619           player->action_waiting = player->special_action_sleeping;
10620           player->anim_delay_counter--;
10621         }
10622         else if (player->post_delay_counter > 0)
10623         {
10624           player->post_delay_counter--;
10625         }
10626       }
10627     }
10628     else if (player->is_bored)
10629     {
10630       if (player->num_special_action_bored > 0)
10631       {
10632         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10633         {
10634           int special_action =
10635             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10636           int special_graphic =
10637             el_act_dir2img(player->artwork_element, special_action, move_dir);
10638
10639           player->anim_delay_counter =
10640             graphic_info[special_graphic].anim_delay_fixed +
10641             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10642           player->post_delay_counter =
10643             graphic_info[special_graphic].post_delay_fixed +
10644             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10645
10646           player->special_action_bored = special_action;
10647         }
10648
10649         if (player->anim_delay_counter > 0)
10650         {
10651           player->action_waiting = player->special_action_bored;
10652           player->anim_delay_counter--;
10653         }
10654         else if (player->post_delay_counter > 0)
10655         {
10656           player->post_delay_counter--;
10657         }
10658       }
10659     }
10660   }
10661   else if (last_waiting)        /* waiting -> not waiting */
10662   {
10663     player->is_waiting = FALSE;
10664     player->is_bored = FALSE;
10665     player->is_sleeping = FALSE;
10666
10667     player->frame_counter_bored = -1;
10668     player->frame_counter_sleeping = -1;
10669
10670     player->anim_delay_counter = 0;
10671     player->post_delay_counter = 0;
10672
10673     player->dir_waiting = player->MovDir;
10674     player->action_waiting = ACTION_DEFAULT;
10675
10676     player->special_action_bored = ACTION_DEFAULT;
10677     player->special_action_sleeping = ACTION_DEFAULT;
10678   }
10679 }
10680
10681 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10682 {
10683   if ((!player->is_moving  && player->was_moving) ||
10684       (player->MovPos == 0 && player->was_moving) ||
10685       (player->is_snapping && !player->was_snapping) ||
10686       (player->is_dropping && !player->was_dropping))
10687   {
10688     if (!CheckSaveEngineSnapshotToList())
10689       return;
10690
10691     player->was_moving = FALSE;
10692     player->was_snapping = TRUE;
10693     player->was_dropping = TRUE;
10694   }
10695   else
10696   {
10697     if (player->is_moving)
10698       player->was_moving = TRUE;
10699
10700     if (!player->is_snapping)
10701       player->was_snapping = FALSE;
10702
10703     if (!player->is_dropping)
10704       player->was_dropping = FALSE;
10705   }
10706 }
10707
10708 static void CheckSingleStepMode(struct PlayerInfo *player)
10709 {
10710   if (tape.single_step && tape.recording && !tape.pausing)
10711   {
10712     /* as it is called "single step mode", just return to pause mode when the
10713        player stopped moving after one tile (or never starts moving at all) */
10714     if (!player->is_moving && !player->is_pushing)
10715     {
10716       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10717       SnapField(player, 0, 0);                  /* stop snapping */
10718     }
10719   }
10720
10721   CheckSaveEngineSnapshot(player);
10722 }
10723
10724 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10725 {
10726   int left      = player_action & JOY_LEFT;
10727   int right     = player_action & JOY_RIGHT;
10728   int up        = player_action & JOY_UP;
10729   int down      = player_action & JOY_DOWN;
10730   int button1   = player_action & JOY_BUTTON_1;
10731   int button2   = player_action & JOY_BUTTON_2;
10732   int dx        = (left ? -1 : right ? 1 : 0);
10733   int dy        = (up   ? -1 : down  ? 1 : 0);
10734
10735   if (!player->active || tape.pausing)
10736     return 0;
10737
10738   if (player_action)
10739   {
10740     if (button1)
10741       SnapField(player, dx, dy);
10742     else
10743     {
10744       if (button2)
10745         DropElement(player);
10746
10747       MovePlayer(player, dx, dy);
10748     }
10749
10750     CheckSingleStepMode(player);
10751
10752     SetPlayerWaiting(player, FALSE);
10753
10754     return player_action;
10755   }
10756   else
10757   {
10758     /* no actions for this player (no input at player's configured device) */
10759
10760     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10761     SnapField(player, 0, 0);
10762     CheckGravityMovementWhenNotMoving(player);
10763
10764     if (player->MovPos == 0)
10765       SetPlayerWaiting(player, TRUE);
10766
10767     if (player->MovPos == 0)    /* needed for tape.playing */
10768       player->is_moving = FALSE;
10769
10770     player->is_dropping = FALSE;
10771     player->is_dropping_pressed = FALSE;
10772     player->drop_pressed_delay = 0;
10773
10774     CheckSingleStepMode(player);
10775
10776     return 0;
10777   }
10778 }
10779
10780 static void CheckLevelTime()
10781 {
10782   int i;
10783
10784   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10785   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10786   {
10787     if (level.native_em_level->lev->home == 0)  /* all players at home */
10788     {
10789       PlayerWins(local_player);
10790
10791       AllPlayersGone = TRUE;
10792
10793       level.native_em_level->lev->home = -1;
10794     }
10795
10796     if (level.native_em_level->ply[0]->alive == 0 &&
10797         level.native_em_level->ply[1]->alive == 0 &&
10798         level.native_em_level->ply[2]->alive == 0 &&
10799         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10800       AllPlayersGone = TRUE;
10801   }
10802   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10803   {
10804     if (game_sp.LevelSolved &&
10805         !game_sp.GameOver)                              /* game won */
10806     {
10807       PlayerWins(local_player);
10808
10809       game_sp.GameOver = TRUE;
10810
10811       AllPlayersGone = TRUE;
10812     }
10813
10814     if (game_sp.GameOver)                               /* game lost */
10815       AllPlayersGone = TRUE;
10816   }
10817
10818   if (TimeFrames >= FRAMES_PER_SECOND)
10819   {
10820     TimeFrames = 0;
10821     TapeTime++;
10822
10823     for (i = 0; i < MAX_PLAYERS; i++)
10824     {
10825       struct PlayerInfo *player = &stored_player[i];
10826
10827       if (SHIELD_ON(player))
10828       {
10829         player->shield_normal_time_left--;
10830
10831         if (player->shield_deadly_time_left > 0)
10832           player->shield_deadly_time_left--;
10833       }
10834     }
10835
10836     if (!local_player->LevelSolved && !level.use_step_counter)
10837     {
10838       TimePlayed++;
10839
10840       if (TimeLeft > 0)
10841       {
10842         TimeLeft--;
10843
10844         if (TimeLeft <= 10 && setup.time_limit)
10845           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10846
10847         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10848            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10849
10850         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10851
10852         if (!TimeLeft && setup.time_limit)
10853         {
10854           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10855             level.native_em_level->lev->killed_out_of_time = TRUE;
10856           else
10857             for (i = 0; i < MAX_PLAYERS; i++)
10858               KillPlayer(&stored_player[i]);
10859         }
10860       }
10861       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10862       {
10863         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10864       }
10865
10866       level.native_em_level->lev->time =
10867         (game.no_time_limit ? TimePlayed : TimeLeft);
10868     }
10869
10870     if (tape.recording || tape.playing)
10871       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10872   }
10873
10874   if (tape.recording || tape.playing)
10875     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10876
10877   UpdateAndDisplayGameControlValues();
10878 }
10879
10880 void AdvanceFrameAndPlayerCounters(int player_nr)
10881 {
10882   int i;
10883
10884   /* advance frame counters (global frame counter and time frame counter) */
10885   FrameCounter++;
10886   TimeFrames++;
10887
10888   /* advance player counters (counters for move delay, move animation etc.) */
10889   for (i = 0; i < MAX_PLAYERS; i++)
10890   {
10891     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10892     int move_delay_value = stored_player[i].move_delay_value;
10893     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10894
10895     if (!advance_player_counters)       /* not all players may be affected */
10896       continue;
10897
10898     if (move_frames == 0)       /* less than one move per game frame */
10899     {
10900       int stepsize = TILEX / move_delay_value;
10901       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10902       int count = (stored_player[i].is_moving ?
10903                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10904
10905       if (count % delay == 0)
10906         move_frames = 1;
10907     }
10908
10909     stored_player[i].Frame += move_frames;
10910
10911     if (stored_player[i].MovPos != 0)
10912       stored_player[i].StepFrame += move_frames;
10913
10914     if (stored_player[i].move_delay > 0)
10915       stored_player[i].move_delay--;
10916
10917     /* due to bugs in previous versions, counter must count up, not down */
10918     if (stored_player[i].push_delay != -1)
10919       stored_player[i].push_delay++;
10920
10921     if (stored_player[i].drop_delay > 0)
10922       stored_player[i].drop_delay--;
10923
10924     if (stored_player[i].is_dropping_pressed)
10925       stored_player[i].drop_pressed_delay++;
10926   }
10927 }
10928
10929 void StartGameActions(boolean init_network_game, boolean record_tape,
10930                       int random_seed)
10931 {
10932   unsigned int new_random_seed = InitRND(random_seed);
10933
10934   if (record_tape)
10935     TapeStartRecording(new_random_seed);
10936
10937 #if defined(NETWORK_AVALIABLE)
10938   if (init_network_game)
10939   {
10940     SendToServer_StartPlaying();
10941
10942     return;
10943   }
10944 #endif
10945
10946   InitGame();
10947 }
10948
10949 void GameActionsExt()
10950 {
10951 #if 0
10952   static unsigned int game_frame_delay = 0;
10953 #endif
10954   unsigned int game_frame_delay_value;
10955   byte *recorded_player_action;
10956   byte summarized_player_action = 0;
10957   byte tape_action[MAX_PLAYERS];
10958   int i;
10959
10960   /* detect endless loops, caused by custom element programming */
10961   if (recursion_loop_detected && recursion_loop_depth == 0)
10962   {
10963     char *message = getStringCat3("Internal Error! Element ",
10964                                   EL_NAME(recursion_loop_element),
10965                                   " caused endless loop! Quit the game?");
10966
10967     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10968           EL_NAME(recursion_loop_element));
10969
10970     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10971
10972     recursion_loop_detected = FALSE;    /* if game should be continued */
10973
10974     free(message);
10975
10976     return;
10977   }
10978
10979   if (game.restart_level)
10980     StartGameActions(options.network, setup.autorecord, level.random_seed);
10981
10982   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10983   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10984   {
10985     if (level.native_em_level->lev->home == 0)  /* all players at home */
10986     {
10987       PlayerWins(local_player);
10988
10989       AllPlayersGone = TRUE;
10990
10991       level.native_em_level->lev->home = -1;
10992     }
10993
10994     if (level.native_em_level->ply[0]->alive == 0 &&
10995         level.native_em_level->ply[1]->alive == 0 &&
10996         level.native_em_level->ply[2]->alive == 0 &&
10997         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10998       AllPlayersGone = TRUE;
10999   }
11000   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11001   {
11002     if (game_sp.LevelSolved &&
11003         !game_sp.GameOver)                              /* game won */
11004     {
11005       PlayerWins(local_player);
11006
11007       game_sp.GameOver = TRUE;
11008
11009       AllPlayersGone = TRUE;
11010     }
11011
11012     if (game_sp.GameOver)                               /* game lost */
11013       AllPlayersGone = TRUE;
11014   }
11015
11016   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11017     GameWon();
11018
11019   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11020     TapeStop();
11021
11022   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11023     return;
11024
11025   game_frame_delay_value =
11026     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11027
11028   if (tape.playing && tape.warp_forward && !tape.pausing)
11029     game_frame_delay_value = 0;
11030
11031   SetVideoFrameDelay(game_frame_delay_value);
11032
11033 #if 0
11034 #if 0
11035   /* ---------- main game synchronization point ---------- */
11036
11037   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11038
11039   printf("::: skip == %d\n", skip);
11040
11041 #else
11042   /* ---------- main game synchronization point ---------- */
11043
11044   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11045 #endif
11046 #endif
11047
11048   if (network_playing && !network_player_action_received)
11049   {
11050     /* try to get network player actions in time */
11051
11052 #if defined(NETWORK_AVALIABLE)
11053     /* last chance to get network player actions without main loop delay */
11054     HandleNetworking();
11055 #endif
11056
11057     /* game was quit by network peer */
11058     if (game_status != GAME_MODE_PLAYING)
11059       return;
11060
11061     if (!network_player_action_received)
11062       return;           /* failed to get network player actions in time */
11063
11064     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11065   }
11066
11067   if (tape.pausing)
11068     return;
11069
11070   /* at this point we know that we really continue executing the game */
11071
11072   network_player_action_received = FALSE;
11073
11074   /* when playing tape, read previously recorded player input from tape data */
11075   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11076
11077   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11078   if (tape.pausing)
11079     return;
11080
11081   if (tape.set_centered_player)
11082   {
11083     game.centered_player_nr_next = tape.centered_player_nr_next;
11084     game.set_centered_player = TRUE;
11085   }
11086
11087   for (i = 0; i < MAX_PLAYERS; i++)
11088   {
11089     summarized_player_action |= stored_player[i].action;
11090
11091     if (!network_playing && (game.team_mode || tape.playing))
11092       stored_player[i].effective_action = stored_player[i].action;
11093   }
11094
11095 #if defined(NETWORK_AVALIABLE)
11096   if (network_playing)
11097     SendToServer_MovePlayer(summarized_player_action);
11098 #endif
11099
11100   // summarize all actions at local players mapped input device position
11101   // (this allows using different input devices in single player mode)
11102   if (!options.network && !game.team_mode)
11103     stored_player[map_player_action[local_player->index_nr]].effective_action =
11104       summarized_player_action;
11105
11106   if (tape.recording &&
11107       setup.team_mode &&
11108       setup.input_on_focus &&
11109       game.centered_player_nr != -1)
11110   {
11111     for (i = 0; i < MAX_PLAYERS; i++)
11112       stored_player[i].effective_action =
11113         (i == game.centered_player_nr ? summarized_player_action : 0);
11114   }
11115
11116   if (recorded_player_action != NULL)
11117     for (i = 0; i < MAX_PLAYERS; i++)
11118       stored_player[i].effective_action = recorded_player_action[i];
11119
11120   for (i = 0; i < MAX_PLAYERS; i++)
11121   {
11122     tape_action[i] = stored_player[i].effective_action;
11123
11124     /* (this may happen in the RND game engine if a player was not present on
11125        the playfield on level start, but appeared later from a custom element */
11126     if (setup.team_mode &&
11127         tape.recording &&
11128         tape_action[i] &&
11129         !tape.player_participates[i])
11130       tape.player_participates[i] = TRUE;
11131   }
11132
11133   /* only record actions from input devices, but not programmed actions */
11134   if (tape.recording)
11135     TapeRecordAction(tape_action);
11136
11137 #if USE_NEW_PLAYER_ASSIGNMENTS
11138   // !!! also map player actions in single player mode !!!
11139   // if (game.team_mode)
11140   if (1)
11141   {
11142     byte mapped_action[MAX_PLAYERS];
11143
11144 #if DEBUG_PLAYER_ACTIONS
11145     printf(":::");
11146     for (i = 0; i < MAX_PLAYERS; i++)
11147       printf(" %d, ", stored_player[i].effective_action);
11148 #endif
11149
11150     for (i = 0; i < MAX_PLAYERS; i++)
11151       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11152
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154       stored_player[i].effective_action = mapped_action[i];
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     printf("\n");
11161 #endif
11162   }
11163 #if DEBUG_PLAYER_ACTIONS
11164   else
11165   {
11166     printf(":::");
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       printf(" %d, ", stored_player[i].effective_action);
11169     printf("\n");
11170   }
11171 #endif
11172 #endif
11173
11174   for (i = 0; i < MAX_PLAYERS; i++)
11175   {
11176     // allow engine snapshot in case of changed movement attempt
11177     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11178         (stored_player[i].effective_action & KEY_MOTION))
11179       game.snapshot.changed_action = TRUE;
11180
11181     // allow engine snapshot in case of snapping/dropping attempt
11182     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11183         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11184       game.snapshot.changed_action = TRUE;
11185
11186     game.snapshot.last_action[i] = stored_player[i].effective_action;
11187   }
11188
11189   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11190   {
11191     GameActions_EM_Main();
11192   }
11193   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11194   {
11195     GameActions_SP_Main();
11196   }
11197   else
11198   {
11199     GameActions_RND_Main();
11200   }
11201
11202   BlitScreenToBitmap(backbuffer);
11203
11204   CheckLevelTime();
11205
11206   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11207
11208   if (options.debug)                    /* calculate frames per second */
11209   {
11210     static unsigned int fps_counter = 0;
11211     static int fps_frames = 0;
11212     unsigned int fps_delay_ms = Counter() - fps_counter;
11213
11214     fps_frames++;
11215
11216     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11217     {
11218       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11219
11220       fps_frames = 0;
11221       fps_counter = Counter();
11222     }
11223
11224     redraw_mask |= REDRAW_FPS;
11225   }
11226 }
11227
11228 static void GameActions_CheckSaveEngineSnapshot()
11229 {
11230   if (!game.snapshot.save_snapshot)
11231     return;
11232
11233   // clear flag for saving snapshot _before_ saving snapshot
11234   game.snapshot.save_snapshot = FALSE;
11235
11236   SaveEngineSnapshotToList();
11237 }
11238
11239 void GameActions()
11240 {
11241   GameActionsExt();
11242
11243   GameActions_CheckSaveEngineSnapshot();
11244 }
11245
11246 void GameActions_EM_Main()
11247 {
11248   byte effective_action[MAX_PLAYERS];
11249   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11250   int i;
11251
11252   for (i = 0; i < MAX_PLAYERS; i++)
11253     effective_action[i] = stored_player[i].effective_action;
11254
11255   GameActions_EM(effective_action, warp_mode);
11256 }
11257
11258 void GameActions_SP_Main()
11259 {
11260   byte effective_action[MAX_PLAYERS];
11261   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11262   int i;
11263
11264   for (i = 0; i < MAX_PLAYERS; i++)
11265     effective_action[i] = stored_player[i].effective_action;
11266
11267   GameActions_SP(effective_action, warp_mode);
11268 }
11269
11270 void GameActions_RND_Main()
11271 {
11272   GameActions_RND();
11273 }
11274
11275 void GameActions_RND()
11276 {
11277   int magic_wall_x = 0, magic_wall_y = 0;
11278   int i, x, y, element, graphic, last_gfx_frame;
11279
11280   InitPlayfieldScanModeVars();
11281
11282   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11283   {
11284     SCAN_PLAYFIELD(x, y)
11285     {
11286       ChangeCount[x][y] = 0;
11287       ChangeEvent[x][y] = -1;
11288     }
11289   }
11290
11291   if (game.set_centered_player)
11292   {
11293     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11294
11295     /* switching to "all players" only possible if all players fit to screen */
11296     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11297     {
11298       game.centered_player_nr_next = game.centered_player_nr;
11299       game.set_centered_player = FALSE;
11300     }
11301
11302     /* do not switch focus to non-existing (or non-active) player */
11303     if (game.centered_player_nr_next >= 0 &&
11304         !stored_player[game.centered_player_nr_next].active)
11305     {
11306       game.centered_player_nr_next = game.centered_player_nr;
11307       game.set_centered_player = FALSE;
11308     }
11309   }
11310
11311   if (game.set_centered_player &&
11312       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11313   {
11314     int sx, sy;
11315
11316     if (game.centered_player_nr_next == -1)
11317     {
11318       setScreenCenteredToAllPlayers(&sx, &sy);
11319     }
11320     else
11321     {
11322       sx = stored_player[game.centered_player_nr_next].jx;
11323       sy = stored_player[game.centered_player_nr_next].jy;
11324     }
11325
11326     game.centered_player_nr = game.centered_player_nr_next;
11327     game.set_centered_player = FALSE;
11328
11329     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11330     DrawGameDoorValues();
11331   }
11332
11333   for (i = 0; i < MAX_PLAYERS; i++)
11334   {
11335     int actual_player_action = stored_player[i].effective_action;
11336
11337 #if 1
11338     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11339        - rnd_equinox_tetrachloride 048
11340        - rnd_equinox_tetrachloride_ii 096
11341        - rnd_emanuel_schmieg 002
11342        - doctor_sloan_ww 001, 020
11343     */
11344     if (stored_player[i].MovPos == 0)
11345       CheckGravityMovement(&stored_player[i]);
11346 #endif
11347
11348     /* overwrite programmed action with tape action */
11349     if (stored_player[i].programmed_action)
11350       actual_player_action = stored_player[i].programmed_action;
11351
11352     PlayerActions(&stored_player[i], actual_player_action);
11353
11354     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11355   }
11356
11357   ScrollScreen(NULL, SCROLL_GO_ON);
11358
11359   /* for backwards compatibility, the following code emulates a fixed bug that
11360      occured when pushing elements (causing elements that just made their last
11361      pushing step to already (if possible) make their first falling step in the
11362      same game frame, which is bad); this code is also needed to use the famous
11363      "spring push bug" which is used in older levels and might be wanted to be
11364      used also in newer levels, but in this case the buggy pushing code is only
11365      affecting the "spring" element and no other elements */
11366
11367   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11368   {
11369     for (i = 0; i < MAX_PLAYERS; i++)
11370     {
11371       struct PlayerInfo *player = &stored_player[i];
11372       int x = player->jx;
11373       int y = player->jy;
11374
11375       if (player->active && player->is_pushing && player->is_moving &&
11376           IS_MOVING(x, y) &&
11377           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11378            Feld[x][y] == EL_SPRING))
11379       {
11380         ContinueMoving(x, y);
11381
11382         /* continue moving after pushing (this is actually a bug) */
11383         if (!IS_MOVING(x, y))
11384           Stop[x][y] = FALSE;
11385       }
11386     }
11387   }
11388
11389   SCAN_PLAYFIELD(x, y)
11390   {
11391     ChangeCount[x][y] = 0;
11392     ChangeEvent[x][y] = -1;
11393
11394     /* this must be handled before main playfield loop */
11395     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11396     {
11397       MovDelay[x][y]--;
11398       if (MovDelay[x][y] <= 0)
11399         RemoveField(x, y);
11400     }
11401
11402     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11403     {
11404       MovDelay[x][y]--;
11405       if (MovDelay[x][y] <= 0)
11406       {
11407         RemoveField(x, y);
11408         TEST_DrawLevelField(x, y);
11409
11410         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11411       }
11412     }
11413
11414 #if DEBUG
11415     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11416     {
11417       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11418       printf("GameActions(): This should never happen!\n");
11419
11420       ChangePage[x][y] = -1;
11421     }
11422 #endif
11423
11424     Stop[x][y] = FALSE;
11425     if (WasJustMoving[x][y] > 0)
11426       WasJustMoving[x][y]--;
11427     if (WasJustFalling[x][y] > 0)
11428       WasJustFalling[x][y]--;
11429     if (CheckCollision[x][y] > 0)
11430       CheckCollision[x][y]--;
11431     if (CheckImpact[x][y] > 0)
11432       CheckImpact[x][y]--;
11433
11434     GfxFrame[x][y]++;
11435
11436     /* reset finished pushing action (not done in ContinueMoving() to allow
11437        continuous pushing animation for elements with zero push delay) */
11438     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11439     {
11440       ResetGfxAnimation(x, y);
11441       TEST_DrawLevelField(x, y);
11442     }
11443
11444 #if DEBUG
11445     if (IS_BLOCKED(x, y))
11446     {
11447       int oldx, oldy;
11448
11449       Blocked2Moving(x, y, &oldx, &oldy);
11450       if (!IS_MOVING(oldx, oldy))
11451       {
11452         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11453         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11454         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11455         printf("GameActions(): This should never happen!\n");
11456       }
11457     }
11458 #endif
11459   }
11460
11461   SCAN_PLAYFIELD(x, y)
11462   {
11463     element = Feld[x][y];
11464     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11465     last_gfx_frame = GfxFrame[x][y];
11466
11467     ResetGfxFrame(x, y);
11468
11469     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11470       DrawLevelGraphicAnimation(x, y, graphic);
11471
11472     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11473         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11474       ResetRandomAnimationValue(x, y);
11475
11476     SetRandomAnimationValue(x, y);
11477
11478     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11479
11480     if (IS_INACTIVE(element))
11481     {
11482       if (IS_ANIMATED(graphic))
11483         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11484
11485       continue;
11486     }
11487
11488     /* this may take place after moving, so 'element' may have changed */
11489     if (IS_CHANGING(x, y) &&
11490         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11491     {
11492       int page = element_info[element].event_page_nr[CE_DELAY];
11493
11494       HandleElementChange(x, y, page);
11495
11496       element = Feld[x][y];
11497       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11498     }
11499
11500     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11501     {
11502       StartMoving(x, y);
11503
11504       element = Feld[x][y];
11505       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11506
11507       if (IS_ANIMATED(graphic) &&
11508           !IS_MOVING(x, y) &&
11509           !Stop[x][y])
11510         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11511
11512       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11513         TEST_DrawTwinkleOnField(x, y);
11514     }
11515     else if (element == EL_ACID)
11516     {
11517       if (!Stop[x][y])
11518         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11519     }
11520     else if ((element == EL_EXIT_OPEN ||
11521               element == EL_EM_EXIT_OPEN ||
11522               element == EL_SP_EXIT_OPEN ||
11523               element == EL_STEEL_EXIT_OPEN ||
11524               element == EL_EM_STEEL_EXIT_OPEN ||
11525               element == EL_SP_TERMINAL ||
11526               element == EL_SP_TERMINAL_ACTIVE ||
11527               element == EL_EXTRA_TIME ||
11528               element == EL_SHIELD_NORMAL ||
11529               element == EL_SHIELD_DEADLY) &&
11530              IS_ANIMATED(graphic))
11531       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11532     else if (IS_MOVING(x, y))
11533       ContinueMoving(x, y);
11534     else if (IS_ACTIVE_BOMB(element))
11535       CheckDynamite(x, y);
11536     else if (element == EL_AMOEBA_GROWING)
11537       AmoebeWaechst(x, y);
11538     else if (element == EL_AMOEBA_SHRINKING)
11539       AmoebaDisappearing(x, y);
11540
11541 #if !USE_NEW_AMOEBA_CODE
11542     else if (IS_AMOEBALIVE(element))
11543       AmoebeAbleger(x, y);
11544 #endif
11545
11546     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11547       Life(x, y);
11548     else if (element == EL_EXIT_CLOSED)
11549       CheckExit(x, y);
11550     else if (element == EL_EM_EXIT_CLOSED)
11551       CheckExitEM(x, y);
11552     else if (element == EL_STEEL_EXIT_CLOSED)
11553       CheckExitSteel(x, y);
11554     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11555       CheckExitSteelEM(x, y);
11556     else if (element == EL_SP_EXIT_CLOSED)
11557       CheckExitSP(x, y);
11558     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11559              element == EL_EXPANDABLE_STEELWALL_GROWING)
11560       MauerWaechst(x, y);
11561     else if (element == EL_EXPANDABLE_WALL ||
11562              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11563              element == EL_EXPANDABLE_WALL_VERTICAL ||
11564              element == EL_EXPANDABLE_WALL_ANY ||
11565              element == EL_BD_EXPANDABLE_WALL)
11566       MauerAbleger(x, y);
11567     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11568              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11569              element == EL_EXPANDABLE_STEELWALL_ANY)
11570       MauerAblegerStahl(x, y);
11571     else if (element == EL_FLAMES)
11572       CheckForDragon(x, y);
11573     else if (element == EL_EXPLOSION)
11574       ; /* drawing of correct explosion animation is handled separately */
11575     else if (element == EL_ELEMENT_SNAPPING ||
11576              element == EL_DIAGONAL_SHRINKING ||
11577              element == EL_DIAGONAL_GROWING)
11578     {
11579       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11580
11581       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11582     }
11583     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11584       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11585
11586     if (IS_BELT_ACTIVE(element))
11587       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11588
11589     if (game.magic_wall_active)
11590     {
11591       int jx = local_player->jx, jy = local_player->jy;
11592
11593       /* play the element sound at the position nearest to the player */
11594       if ((element == EL_MAGIC_WALL_FULL ||
11595            element == EL_MAGIC_WALL_ACTIVE ||
11596            element == EL_MAGIC_WALL_EMPTYING ||
11597            element == EL_BD_MAGIC_WALL_FULL ||
11598            element == EL_BD_MAGIC_WALL_ACTIVE ||
11599            element == EL_BD_MAGIC_WALL_EMPTYING ||
11600            element == EL_DC_MAGIC_WALL_FULL ||
11601            element == EL_DC_MAGIC_WALL_ACTIVE ||
11602            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11603           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11604       {
11605         magic_wall_x = x;
11606         magic_wall_y = y;
11607       }
11608     }
11609   }
11610
11611 #if USE_NEW_AMOEBA_CODE
11612   /* new experimental amoeba growth stuff */
11613   if (!(FrameCounter % 8))
11614   {
11615     static unsigned int random = 1684108901;
11616
11617     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11618     {
11619       x = RND(lev_fieldx);
11620       y = RND(lev_fieldy);
11621       element = Feld[x][y];
11622
11623       if (!IS_PLAYER(x,y) &&
11624           (element == EL_EMPTY ||
11625            CAN_GROW_INTO(element) ||
11626            element == EL_QUICKSAND_EMPTY ||
11627            element == EL_QUICKSAND_FAST_EMPTY ||
11628            element == EL_ACID_SPLASH_LEFT ||
11629            element == EL_ACID_SPLASH_RIGHT))
11630       {
11631         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11632             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11633             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11634             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11635           Feld[x][y] = EL_AMOEBA_DROP;
11636       }
11637
11638       random = random * 129 + 1;
11639     }
11640   }
11641 #endif
11642
11643   game.explosions_delayed = FALSE;
11644
11645   SCAN_PLAYFIELD(x, y)
11646   {
11647     element = Feld[x][y];
11648
11649     if (ExplodeField[x][y])
11650       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11651     else if (element == EL_EXPLOSION)
11652       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11653
11654     ExplodeField[x][y] = EX_TYPE_NONE;
11655   }
11656
11657   game.explosions_delayed = TRUE;
11658
11659   if (game.magic_wall_active)
11660   {
11661     if (!(game.magic_wall_time_left % 4))
11662     {
11663       int element = Feld[magic_wall_x][magic_wall_y];
11664
11665       if (element == EL_BD_MAGIC_WALL_FULL ||
11666           element == EL_BD_MAGIC_WALL_ACTIVE ||
11667           element == EL_BD_MAGIC_WALL_EMPTYING)
11668         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11669       else if (element == EL_DC_MAGIC_WALL_FULL ||
11670                element == EL_DC_MAGIC_WALL_ACTIVE ||
11671                element == EL_DC_MAGIC_WALL_EMPTYING)
11672         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11673       else
11674         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11675     }
11676
11677     if (game.magic_wall_time_left > 0)
11678     {
11679       game.magic_wall_time_left--;
11680
11681       if (!game.magic_wall_time_left)
11682       {
11683         SCAN_PLAYFIELD(x, y)
11684         {
11685           element = Feld[x][y];
11686
11687           if (element == EL_MAGIC_WALL_ACTIVE ||
11688               element == EL_MAGIC_WALL_FULL)
11689           {
11690             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11691             TEST_DrawLevelField(x, y);
11692           }
11693           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11694                    element == EL_BD_MAGIC_WALL_FULL)
11695           {
11696             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11697             TEST_DrawLevelField(x, y);
11698           }
11699           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11700                    element == EL_DC_MAGIC_WALL_FULL)
11701           {
11702             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11703             TEST_DrawLevelField(x, y);
11704           }
11705         }
11706
11707         game.magic_wall_active = FALSE;
11708       }
11709     }
11710   }
11711
11712   if (game.light_time_left > 0)
11713   {
11714     game.light_time_left--;
11715
11716     if (game.light_time_left == 0)
11717       RedrawAllLightSwitchesAndInvisibleElements();
11718   }
11719
11720   if (game.timegate_time_left > 0)
11721   {
11722     game.timegate_time_left--;
11723
11724     if (game.timegate_time_left == 0)
11725       CloseAllOpenTimegates();
11726   }
11727
11728   if (game.lenses_time_left > 0)
11729   {
11730     game.lenses_time_left--;
11731
11732     if (game.lenses_time_left == 0)
11733       RedrawAllInvisibleElementsForLenses();
11734   }
11735
11736   if (game.magnify_time_left > 0)
11737   {
11738     game.magnify_time_left--;
11739
11740     if (game.magnify_time_left == 0)
11741       RedrawAllInvisibleElementsForMagnifier();
11742   }
11743
11744   for (i = 0; i < MAX_PLAYERS; i++)
11745   {
11746     struct PlayerInfo *player = &stored_player[i];
11747
11748     if (SHIELD_ON(player))
11749     {
11750       if (player->shield_deadly_time_left)
11751         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11752       else if (player->shield_normal_time_left)
11753         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11754     }
11755   }
11756
11757 #if USE_DELAYED_GFX_REDRAW
11758   SCAN_PLAYFIELD(x, y)
11759   {
11760     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11761     {
11762       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11763          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11764
11765       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11766         DrawLevelField(x, y);
11767
11768       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11769         DrawLevelFieldCrumbled(x, y);
11770
11771       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11772         DrawLevelFieldCrumbledNeighbours(x, y);
11773
11774       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11775         DrawTwinkleOnField(x, y);
11776     }
11777
11778     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11779   }
11780 #endif
11781
11782   DrawAllPlayers();
11783   PlayAllPlayersSound();
11784
11785   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11786   {
11787     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11788
11789     local_player->show_envelope = 0;
11790   }
11791
11792   /* use random number generator in every frame to make it less predictable */
11793   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11794     RND(1);
11795 }
11796
11797 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11798 {
11799   int min_x = x, min_y = y, max_x = x, max_y = y;
11800   int i;
11801
11802   for (i = 0; i < MAX_PLAYERS; i++)
11803   {
11804     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11805
11806     if (!stored_player[i].active || &stored_player[i] == player)
11807       continue;
11808
11809     min_x = MIN(min_x, jx);
11810     min_y = MIN(min_y, jy);
11811     max_x = MAX(max_x, jx);
11812     max_y = MAX(max_y, jy);
11813   }
11814
11815   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11816 }
11817
11818 static boolean AllPlayersInVisibleScreen()
11819 {
11820   int i;
11821
11822   for (i = 0; i < MAX_PLAYERS; i++)
11823   {
11824     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11825
11826     if (!stored_player[i].active)
11827       continue;
11828
11829     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11830       return FALSE;
11831   }
11832
11833   return TRUE;
11834 }
11835
11836 void ScrollLevel(int dx, int dy)
11837 {
11838   int scroll_offset = 2 * TILEX_VAR;
11839   int x, y;
11840
11841   BlitBitmap(drawto_field, drawto_field,
11842              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11843              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11844              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11845              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11846              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11847              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11848
11849   if (dx != 0)
11850   {
11851     x = (dx == 1 ? BX1 : BX2);
11852     for (y = BY1; y <= BY2; y++)
11853       DrawScreenField(x, y);
11854   }
11855
11856   if (dy != 0)
11857   {
11858     y = (dy == 1 ? BY1 : BY2);
11859     for (x = BX1; x <= BX2; x++)
11860       DrawScreenField(x, y);
11861   }
11862
11863   redraw_mask |= REDRAW_FIELD;
11864 }
11865
11866 static boolean canFallDown(struct PlayerInfo *player)
11867 {
11868   int jx = player->jx, jy = player->jy;
11869
11870   return (IN_LEV_FIELD(jx, jy + 1) &&
11871           (IS_FREE(jx, jy + 1) ||
11872            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11873           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11874           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11875 }
11876
11877 static boolean canPassField(int x, int y, int move_dir)
11878 {
11879   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11880   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11881   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11882   int nextx = x + dx;
11883   int nexty = y + dy;
11884   int element = Feld[x][y];
11885
11886   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11887           !CAN_MOVE(element) &&
11888           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11889           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11890           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11891 }
11892
11893 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11894 {
11895   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11896   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11897   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11898   int newx = x + dx;
11899   int newy = y + dy;
11900
11901   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11902           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11903           (IS_DIGGABLE(Feld[newx][newy]) ||
11904            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11905            canPassField(newx, newy, move_dir)));
11906 }
11907
11908 static void CheckGravityMovement(struct PlayerInfo *player)
11909 {
11910   if (player->gravity && !player->programmed_action)
11911   {
11912     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11913     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11914     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11915     int jx = player->jx, jy = player->jy;
11916     boolean player_is_moving_to_valid_field =
11917       (!player_is_snapping &&
11918        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11919         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11920     boolean player_can_fall_down = canFallDown(player);
11921
11922     if (player_can_fall_down &&
11923         !player_is_moving_to_valid_field)
11924       player->programmed_action = MV_DOWN;
11925   }
11926 }
11927
11928 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11929 {
11930   return CheckGravityMovement(player);
11931
11932   if (player->gravity && !player->programmed_action)
11933   {
11934     int jx = player->jx, jy = player->jy;
11935     boolean field_under_player_is_free =
11936       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11937     boolean player_is_standing_on_valid_field =
11938       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11939        (IS_WALKABLE(Feld[jx][jy]) &&
11940         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11941
11942     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11943       player->programmed_action = MV_DOWN;
11944   }
11945 }
11946
11947 /*
11948   MovePlayerOneStep()
11949   -----------------------------------------------------------------------------
11950   dx, dy:               direction (non-diagonal) to try to move the player to
11951   real_dx, real_dy:     direction as read from input device (can be diagonal)
11952 */
11953
11954 boolean MovePlayerOneStep(struct PlayerInfo *player,
11955                           int dx, int dy, int real_dx, int real_dy)
11956 {
11957   int jx = player->jx, jy = player->jy;
11958   int new_jx = jx + dx, new_jy = jy + dy;
11959   int can_move;
11960   boolean player_can_move = !player->cannot_move;
11961
11962   if (!player->active || (!dx && !dy))
11963     return MP_NO_ACTION;
11964
11965   player->MovDir = (dx < 0 ? MV_LEFT :
11966                     dx > 0 ? MV_RIGHT :
11967                     dy < 0 ? MV_UP :
11968                     dy > 0 ? MV_DOWN :  MV_NONE);
11969
11970   if (!IN_LEV_FIELD(new_jx, new_jy))
11971     return MP_NO_ACTION;
11972
11973   if (!player_can_move)
11974   {
11975     if (player->MovPos == 0)
11976     {
11977       player->is_moving = FALSE;
11978       player->is_digging = FALSE;
11979       player->is_collecting = FALSE;
11980       player->is_snapping = FALSE;
11981       player->is_pushing = FALSE;
11982     }
11983   }
11984
11985   if (!options.network && game.centered_player_nr == -1 &&
11986       !AllPlayersInSight(player, new_jx, new_jy))
11987     return MP_NO_ACTION;
11988
11989   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11990   if (can_move != MP_MOVING)
11991     return can_move;
11992
11993   /* check if DigField() has caused relocation of the player */
11994   if (player->jx != jx || player->jy != jy)
11995     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11996
11997   StorePlayer[jx][jy] = 0;
11998   player->last_jx = jx;
11999   player->last_jy = jy;
12000   player->jx = new_jx;
12001   player->jy = new_jy;
12002   StorePlayer[new_jx][new_jy] = player->element_nr;
12003
12004   if (player->move_delay_value_next != -1)
12005   {
12006     player->move_delay_value = player->move_delay_value_next;
12007     player->move_delay_value_next = -1;
12008   }
12009
12010   player->MovPos =
12011     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12012
12013   player->step_counter++;
12014
12015   PlayerVisit[jx][jy] = FrameCounter;
12016
12017   player->is_moving = TRUE;
12018
12019 #if 1
12020   /* should better be called in MovePlayer(), but this breaks some tapes */
12021   ScrollPlayer(player, SCROLL_INIT);
12022 #endif
12023
12024   return MP_MOVING;
12025 }
12026
12027 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12028 {
12029   int jx = player->jx, jy = player->jy;
12030   int old_jx = jx, old_jy = jy;
12031   int moved = MP_NO_ACTION;
12032
12033   if (!player->active)
12034     return FALSE;
12035
12036   if (!dx && !dy)
12037   {
12038     if (player->MovPos == 0)
12039     {
12040       player->is_moving = FALSE;
12041       player->is_digging = FALSE;
12042       player->is_collecting = FALSE;
12043       player->is_snapping = FALSE;
12044       player->is_pushing = FALSE;
12045     }
12046
12047     return FALSE;
12048   }
12049
12050   if (player->move_delay > 0)
12051     return FALSE;
12052
12053   player->move_delay = -1;              /* set to "uninitialized" value */
12054
12055   /* store if player is automatically moved to next field */
12056   player->is_auto_moving = (player->programmed_action != MV_NONE);
12057
12058   /* remove the last programmed player action */
12059   player->programmed_action = 0;
12060
12061   if (player->MovPos)
12062   {
12063     /* should only happen if pre-1.2 tape recordings are played */
12064     /* this is only for backward compatibility */
12065
12066     int original_move_delay_value = player->move_delay_value;
12067
12068 #if DEBUG
12069     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12070            tape.counter);
12071 #endif
12072
12073     /* scroll remaining steps with finest movement resolution */
12074     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12075
12076     while (player->MovPos)
12077     {
12078       ScrollPlayer(player, SCROLL_GO_ON);
12079       ScrollScreen(NULL, SCROLL_GO_ON);
12080
12081       AdvanceFrameAndPlayerCounters(player->index_nr);
12082
12083       DrawAllPlayers();
12084       BackToFront_WithFrameDelay(0);
12085     }
12086
12087     player->move_delay_value = original_move_delay_value;
12088   }
12089
12090   player->is_active = FALSE;
12091
12092   if (player->last_move_dir & MV_HORIZONTAL)
12093   {
12094     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12095       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12096   }
12097   else
12098   {
12099     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12100       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12101   }
12102
12103   if (!moved && !player->is_active)
12104   {
12105     player->is_moving = FALSE;
12106     player->is_digging = FALSE;
12107     player->is_collecting = FALSE;
12108     player->is_snapping = FALSE;
12109     player->is_pushing = FALSE;
12110   }
12111
12112   jx = player->jx;
12113   jy = player->jy;
12114
12115   if (moved & MP_MOVING && !ScreenMovPos &&
12116       (player->index_nr == game.centered_player_nr ||
12117        game.centered_player_nr == -1))
12118   {
12119     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12120     int offset = game.scroll_delay_value;
12121
12122     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12123     {
12124       /* actual player has left the screen -- scroll in that direction */
12125       if (jx != old_jx)         /* player has moved horizontally */
12126         scroll_x += (jx - old_jx);
12127       else                      /* player has moved vertically */
12128         scroll_y += (jy - old_jy);
12129     }
12130     else
12131     {
12132       if (jx != old_jx)         /* player has moved horizontally */
12133       {
12134         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12135             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12136           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12137
12138         /* don't scroll over playfield boundaries */
12139         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12140           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12141
12142         /* don't scroll more than one field at a time */
12143         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12144
12145         /* don't scroll against the player's moving direction */
12146         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12147             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12148           scroll_x = old_scroll_x;
12149       }
12150       else                      /* player has moved vertically */
12151       {
12152         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12153             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12154           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12155
12156         /* don't scroll over playfield boundaries */
12157         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12158           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12159
12160         /* don't scroll more than one field at a time */
12161         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12162
12163         /* don't scroll against the player's moving direction */
12164         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12165             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12166           scroll_y = old_scroll_y;
12167       }
12168     }
12169
12170     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12171     {
12172       if (!options.network && game.centered_player_nr == -1 &&
12173           !AllPlayersInVisibleScreen())
12174       {
12175         scroll_x = old_scroll_x;
12176         scroll_y = old_scroll_y;
12177       }
12178       else
12179       {
12180         ScrollScreen(player, SCROLL_INIT);
12181         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12182       }
12183     }
12184   }
12185
12186   player->StepFrame = 0;
12187
12188   if (moved & MP_MOVING)
12189   {
12190     if (old_jx != jx && old_jy == jy)
12191       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12192     else if (old_jx == jx && old_jy != jy)
12193       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12194
12195     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12196
12197     player->last_move_dir = player->MovDir;
12198     player->is_moving = TRUE;
12199     player->is_snapping = FALSE;
12200     player->is_switching = FALSE;
12201     player->is_dropping = FALSE;
12202     player->is_dropping_pressed = FALSE;
12203     player->drop_pressed_delay = 0;
12204
12205 #if 0
12206     /* should better be called here than above, but this breaks some tapes */
12207     ScrollPlayer(player, SCROLL_INIT);
12208 #endif
12209   }
12210   else
12211   {
12212     CheckGravityMovementWhenNotMoving(player);
12213
12214     player->is_moving = FALSE;
12215
12216     /* at this point, the player is allowed to move, but cannot move right now
12217        (e.g. because of something blocking the way) -- ensure that the player
12218        is also allowed to move in the next frame (in old versions before 3.1.1,
12219        the player was forced to wait again for eight frames before next try) */
12220
12221     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12222       player->move_delay = 0;   /* allow direct movement in the next frame */
12223   }
12224
12225   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12226     player->move_delay = player->move_delay_value;
12227
12228   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12229   {
12230     TestIfPlayerTouchesBadThing(jx, jy);
12231     TestIfPlayerTouchesCustomElement(jx, jy);
12232   }
12233
12234   if (!player->active)
12235     RemovePlayer(player);
12236
12237   return moved;
12238 }
12239
12240 void ScrollPlayer(struct PlayerInfo *player, int mode)
12241 {
12242   int jx = player->jx, jy = player->jy;
12243   int last_jx = player->last_jx, last_jy = player->last_jy;
12244   int move_stepsize = TILEX / player->move_delay_value;
12245
12246   if (!player->active)
12247     return;
12248
12249   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12250     return;
12251
12252   if (mode == SCROLL_INIT)
12253   {
12254     player->actual_frame_counter = FrameCounter;
12255     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12256
12257     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12258         Feld[last_jx][last_jy] == EL_EMPTY)
12259     {
12260       int last_field_block_delay = 0;   /* start with no blocking at all */
12261       int block_delay_adjustment = player->block_delay_adjustment;
12262
12263       /* if player blocks last field, add delay for exactly one move */
12264       if (player->block_last_field)
12265       {
12266         last_field_block_delay += player->move_delay_value;
12267
12268         /* when blocking enabled, prevent moving up despite gravity */
12269         if (player->gravity && player->MovDir == MV_UP)
12270           block_delay_adjustment = -1;
12271       }
12272
12273       /* add block delay adjustment (also possible when not blocking) */
12274       last_field_block_delay += block_delay_adjustment;
12275
12276       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12277       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12278     }
12279
12280     if (player->MovPos != 0)    /* player has not yet reached destination */
12281       return;
12282   }
12283   else if (!FrameReached(&player->actual_frame_counter, 1))
12284     return;
12285
12286   if (player->MovPos != 0)
12287   {
12288     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12289     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12290
12291     /* before DrawPlayer() to draw correct player graphic for this case */
12292     if (player->MovPos == 0)
12293       CheckGravityMovement(player);
12294   }
12295
12296   if (player->MovPos == 0)      /* player reached destination field */
12297   {
12298     if (player->move_delay_reset_counter > 0)
12299     {
12300       player->move_delay_reset_counter--;
12301
12302       if (player->move_delay_reset_counter == 0)
12303       {
12304         /* continue with normal speed after quickly moving through gate */
12305         HALVE_PLAYER_SPEED(player);
12306
12307         /* be able to make the next move without delay */
12308         player->move_delay = 0;
12309       }
12310     }
12311
12312     player->last_jx = jx;
12313     player->last_jy = jy;
12314
12315     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12316         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12317         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12318         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12319         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12320         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12321         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12322         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12323     {
12324       DrawPlayer(player);       /* needed here only to cleanup last field */
12325       RemovePlayer(player);
12326
12327       if (local_player->friends_still_needed == 0 ||
12328           IS_SP_ELEMENT(Feld[jx][jy]))
12329         PlayerWins(player);
12330     }
12331
12332     /* this breaks one level: "machine", level 000 */
12333     {
12334       int move_direction = player->MovDir;
12335       int enter_side = MV_DIR_OPPOSITE(move_direction);
12336       int leave_side = move_direction;
12337       int old_jx = last_jx;
12338       int old_jy = last_jy;
12339       int old_element = Feld[old_jx][old_jy];
12340       int new_element = Feld[jx][jy];
12341
12342       if (IS_CUSTOM_ELEMENT(old_element))
12343         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12344                                    CE_LEFT_BY_PLAYER,
12345                                    player->index_bit, leave_side);
12346
12347       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12348                                           CE_PLAYER_LEAVES_X,
12349                                           player->index_bit, leave_side);
12350
12351       if (IS_CUSTOM_ELEMENT(new_element))
12352         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12353                                    player->index_bit, enter_side);
12354
12355       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12356                                           CE_PLAYER_ENTERS_X,
12357                                           player->index_bit, enter_side);
12358
12359       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12360                                         CE_MOVE_OF_X, move_direction);
12361     }
12362
12363     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12364     {
12365       TestIfPlayerTouchesBadThing(jx, jy);
12366       TestIfPlayerTouchesCustomElement(jx, jy);
12367
12368       /* needed because pushed element has not yet reached its destination,
12369          so it would trigger a change event at its previous field location */
12370       if (!player->is_pushing)
12371         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12372
12373       if (!player->active)
12374         RemovePlayer(player);
12375     }
12376
12377     if (!local_player->LevelSolved && level.use_step_counter)
12378     {
12379       int i;
12380
12381       TimePlayed++;
12382
12383       if (TimeLeft > 0)
12384       {
12385         TimeLeft--;
12386
12387         if (TimeLeft <= 10 && setup.time_limit)
12388           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12389
12390         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12391
12392         DisplayGameControlValues();
12393
12394         if (!TimeLeft && setup.time_limit)
12395           for (i = 0; i < MAX_PLAYERS; i++)
12396             KillPlayer(&stored_player[i]);
12397       }
12398       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12399       {
12400         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12401
12402         DisplayGameControlValues();
12403       }
12404     }
12405
12406     if (tape.single_step && tape.recording && !tape.pausing &&
12407         !player->programmed_action)
12408       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12409
12410     if (!player->programmed_action)
12411       CheckSaveEngineSnapshot(player);
12412   }
12413 }
12414
12415 void ScrollScreen(struct PlayerInfo *player, int mode)
12416 {
12417   static unsigned int screen_frame_counter = 0;
12418
12419   if (mode == SCROLL_INIT)
12420   {
12421     /* set scrolling step size according to actual player's moving speed */
12422     ScrollStepSize = TILEX / player->move_delay_value;
12423
12424     screen_frame_counter = FrameCounter;
12425     ScreenMovDir = player->MovDir;
12426     ScreenMovPos = player->MovPos;
12427     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12428     return;
12429   }
12430   else if (!FrameReached(&screen_frame_counter, 1))
12431     return;
12432
12433   if (ScreenMovPos)
12434   {
12435     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12436     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12437     redraw_mask |= REDRAW_FIELD;
12438   }
12439   else
12440     ScreenMovDir = MV_NONE;
12441 }
12442
12443 void TestIfPlayerTouchesCustomElement(int x, int y)
12444 {
12445   static int xy[4][2] =
12446   {
12447     { 0, -1 },
12448     { -1, 0 },
12449     { +1, 0 },
12450     { 0, +1 }
12451   };
12452   static int trigger_sides[4][2] =
12453   {
12454     /* center side       border side */
12455     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12456     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12457     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12458     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12459   };
12460   static int touch_dir[4] =
12461   {
12462     MV_LEFT | MV_RIGHT,
12463     MV_UP   | MV_DOWN,
12464     MV_UP   | MV_DOWN,
12465     MV_LEFT | MV_RIGHT
12466   };
12467   int center_element = Feld[x][y];      /* should always be non-moving! */
12468   int i;
12469
12470   for (i = 0; i < NUM_DIRECTIONS; i++)
12471   {
12472     int xx = x + xy[i][0];
12473     int yy = y + xy[i][1];
12474     int center_side = trigger_sides[i][0];
12475     int border_side = trigger_sides[i][1];
12476     int border_element;
12477
12478     if (!IN_LEV_FIELD(xx, yy))
12479       continue;
12480
12481     if (IS_PLAYER(x, y))                /* player found at center element */
12482     {
12483       struct PlayerInfo *player = PLAYERINFO(x, y);
12484
12485       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12486         border_element = Feld[xx][yy];          /* may be moving! */
12487       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12488         border_element = Feld[xx][yy];
12489       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12490         border_element = MovingOrBlocked2Element(xx, yy);
12491       else
12492         continue;               /* center and border element do not touch */
12493
12494       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12495                                  player->index_bit, border_side);
12496       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12497                                           CE_PLAYER_TOUCHES_X,
12498                                           player->index_bit, border_side);
12499
12500       {
12501         /* use player element that is initially defined in the level playfield,
12502            not the player element that corresponds to the runtime player number
12503            (example: a level that contains EL_PLAYER_3 as the only player would
12504            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12505         int player_element = PLAYERINFO(x, y)->initial_element;
12506
12507         CheckElementChangeBySide(xx, yy, border_element, player_element,
12508                                  CE_TOUCHING_X, border_side);
12509       }
12510     }
12511     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12512     {
12513       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12514
12515       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12516       {
12517         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12518           continue;             /* center and border element do not touch */
12519       }
12520
12521       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12522                                  player->index_bit, center_side);
12523       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12524                                           CE_PLAYER_TOUCHES_X,
12525                                           player->index_bit, center_side);
12526
12527       {
12528         /* use player element that is initially defined in the level playfield,
12529            not the player element that corresponds to the runtime player number
12530            (example: a level that contains EL_PLAYER_3 as the only player would
12531            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12532         int player_element = PLAYERINFO(xx, yy)->initial_element;
12533
12534         CheckElementChangeBySide(x, y, center_element, player_element,
12535                                  CE_TOUCHING_X, center_side);
12536       }
12537
12538       break;
12539     }
12540   }
12541 }
12542
12543 void TestIfElementTouchesCustomElement(int x, int y)
12544 {
12545   static int xy[4][2] =
12546   {
12547     { 0, -1 },
12548     { -1, 0 },
12549     { +1, 0 },
12550     { 0, +1 }
12551   };
12552   static int trigger_sides[4][2] =
12553   {
12554     /* center side      border side */
12555     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12556     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12557     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12558     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12559   };
12560   static int touch_dir[4] =
12561   {
12562     MV_LEFT | MV_RIGHT,
12563     MV_UP   | MV_DOWN,
12564     MV_UP   | MV_DOWN,
12565     MV_LEFT | MV_RIGHT
12566   };
12567   boolean change_center_element = FALSE;
12568   int center_element = Feld[x][y];      /* should always be non-moving! */
12569   int border_element_old[NUM_DIRECTIONS];
12570   int i;
12571
12572   for (i = 0; i < NUM_DIRECTIONS; i++)
12573   {
12574     int xx = x + xy[i][0];
12575     int yy = y + xy[i][1];
12576     int border_element;
12577
12578     border_element_old[i] = -1;
12579
12580     if (!IN_LEV_FIELD(xx, yy))
12581       continue;
12582
12583     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12584       border_element = Feld[xx][yy];    /* may be moving! */
12585     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12586       border_element = Feld[xx][yy];
12587     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12588       border_element = MovingOrBlocked2Element(xx, yy);
12589     else
12590       continue;                 /* center and border element do not touch */
12591
12592     border_element_old[i] = border_element;
12593   }
12594
12595   for (i = 0; i < NUM_DIRECTIONS; i++)
12596   {
12597     int xx = x + xy[i][0];
12598     int yy = y + xy[i][1];
12599     int center_side = trigger_sides[i][0];
12600     int border_element = border_element_old[i];
12601
12602     if (border_element == -1)
12603       continue;
12604
12605     /* check for change of border element */
12606     CheckElementChangeBySide(xx, yy, border_element, center_element,
12607                              CE_TOUCHING_X, center_side);
12608
12609     /* (center element cannot be player, so we dont have to check this here) */
12610   }
12611
12612   for (i = 0; i < NUM_DIRECTIONS; i++)
12613   {
12614     int xx = x + xy[i][0];
12615     int yy = y + xy[i][1];
12616     int border_side = trigger_sides[i][1];
12617     int border_element = border_element_old[i];
12618
12619     if (border_element == -1)
12620       continue;
12621
12622     /* check for change of center element (but change it only once) */
12623     if (!change_center_element)
12624       change_center_element =
12625         CheckElementChangeBySide(x, y, center_element, border_element,
12626                                  CE_TOUCHING_X, border_side);
12627
12628     if (IS_PLAYER(xx, yy))
12629     {
12630       /* use player element that is initially defined in the level playfield,
12631          not the player element that corresponds to the runtime player number
12632          (example: a level that contains EL_PLAYER_3 as the only player would
12633          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12634       int player_element = PLAYERINFO(xx, yy)->initial_element;
12635
12636       CheckElementChangeBySide(x, y, center_element, player_element,
12637                                CE_TOUCHING_X, border_side);
12638     }
12639   }
12640 }
12641
12642 void TestIfElementHitsCustomElement(int x, int y, int direction)
12643 {
12644   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12645   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12646   int hitx = x + dx, hity = y + dy;
12647   int hitting_element = Feld[x][y];
12648   int touched_element;
12649
12650   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12651     return;
12652
12653   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12654                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12655
12656   if (IN_LEV_FIELD(hitx, hity))
12657   {
12658     int opposite_direction = MV_DIR_OPPOSITE(direction);
12659     int hitting_side = direction;
12660     int touched_side = opposite_direction;
12661     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12662                           MovDir[hitx][hity] != direction ||
12663                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12664
12665     object_hit = TRUE;
12666
12667     if (object_hit)
12668     {
12669       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12670                                CE_HITTING_X, touched_side);
12671
12672       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12673                                CE_HIT_BY_X, hitting_side);
12674
12675       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12676                                CE_HIT_BY_SOMETHING, opposite_direction);
12677
12678       if (IS_PLAYER(hitx, hity))
12679       {
12680         /* use player element that is initially defined in the level playfield,
12681            not the player element that corresponds to the runtime player number
12682            (example: a level that contains EL_PLAYER_3 as the only player would
12683            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12684         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12685
12686         CheckElementChangeBySide(x, y, hitting_element, player_element,
12687                                  CE_HITTING_X, touched_side);
12688       }
12689     }
12690   }
12691
12692   /* "hitting something" is also true when hitting the playfield border */
12693   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12694                            CE_HITTING_SOMETHING, direction);
12695 }
12696
12697 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12698 {
12699   int i, kill_x = -1, kill_y = -1;
12700
12701   int bad_element = -1;
12702   static int test_xy[4][2] =
12703   {
12704     { 0, -1 },
12705     { -1, 0 },
12706     { +1, 0 },
12707     { 0, +1 }
12708   };
12709   static int test_dir[4] =
12710   {
12711     MV_UP,
12712     MV_LEFT,
12713     MV_RIGHT,
12714     MV_DOWN
12715   };
12716
12717   for (i = 0; i < NUM_DIRECTIONS; i++)
12718   {
12719     int test_x, test_y, test_move_dir, test_element;
12720
12721     test_x = good_x + test_xy[i][0];
12722     test_y = good_y + test_xy[i][1];
12723
12724     if (!IN_LEV_FIELD(test_x, test_y))
12725       continue;
12726
12727     test_move_dir =
12728       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12729
12730     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12731
12732     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12733        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12734     */
12735     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12736         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12737     {
12738       kill_x = test_x;
12739       kill_y = test_y;
12740       bad_element = test_element;
12741
12742       break;
12743     }
12744   }
12745
12746   if (kill_x != -1 || kill_y != -1)
12747   {
12748     if (IS_PLAYER(good_x, good_y))
12749     {
12750       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12751
12752       if (player->shield_deadly_time_left > 0 &&
12753           !IS_INDESTRUCTIBLE(bad_element))
12754         Bang(kill_x, kill_y);
12755       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12756         KillPlayer(player);
12757     }
12758     else
12759       Bang(good_x, good_y);
12760   }
12761 }
12762
12763 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12764 {
12765   int i, kill_x = -1, kill_y = -1;
12766   int bad_element = Feld[bad_x][bad_y];
12767   static int test_xy[4][2] =
12768   {
12769     { 0, -1 },
12770     { -1, 0 },
12771     { +1, 0 },
12772     { 0, +1 }
12773   };
12774   static int touch_dir[4] =
12775   {
12776     MV_LEFT | MV_RIGHT,
12777     MV_UP   | MV_DOWN,
12778     MV_UP   | MV_DOWN,
12779     MV_LEFT | MV_RIGHT
12780   };
12781   static int test_dir[4] =
12782   {
12783     MV_UP,
12784     MV_LEFT,
12785     MV_RIGHT,
12786     MV_DOWN
12787   };
12788
12789   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12790     return;
12791
12792   for (i = 0; i < NUM_DIRECTIONS; i++)
12793   {
12794     int test_x, test_y, test_move_dir, test_element;
12795
12796     test_x = bad_x + test_xy[i][0];
12797     test_y = bad_y + test_xy[i][1];
12798
12799     if (!IN_LEV_FIELD(test_x, test_y))
12800       continue;
12801
12802     test_move_dir =
12803       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12804
12805     test_element = Feld[test_x][test_y];
12806
12807     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12808        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12809     */
12810     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12811         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12812     {
12813       /* good thing is player or penguin that does not move away */
12814       if (IS_PLAYER(test_x, test_y))
12815       {
12816         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12817
12818         if (bad_element == EL_ROBOT && player->is_moving)
12819           continue;     /* robot does not kill player if he is moving */
12820
12821         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12822         {
12823           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12824             continue;           /* center and border element do not touch */
12825         }
12826
12827         kill_x = test_x;
12828         kill_y = test_y;
12829
12830         break;
12831       }
12832       else if (test_element == EL_PENGUIN)
12833       {
12834         kill_x = test_x;
12835         kill_y = test_y;
12836
12837         break;
12838       }
12839     }
12840   }
12841
12842   if (kill_x != -1 || kill_y != -1)
12843   {
12844     if (IS_PLAYER(kill_x, kill_y))
12845     {
12846       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12847
12848       if (player->shield_deadly_time_left > 0 &&
12849           !IS_INDESTRUCTIBLE(bad_element))
12850         Bang(bad_x, bad_y);
12851       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12852         KillPlayer(player);
12853     }
12854     else
12855       Bang(kill_x, kill_y);
12856   }
12857 }
12858
12859 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12860 {
12861   int bad_element = Feld[bad_x][bad_y];
12862   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12863   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12864   int test_x = bad_x + dx, test_y = bad_y + dy;
12865   int test_move_dir, test_element;
12866   int kill_x = -1, kill_y = -1;
12867
12868   if (!IN_LEV_FIELD(test_x, test_y))
12869     return;
12870
12871   test_move_dir =
12872     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12873
12874   test_element = Feld[test_x][test_y];
12875
12876   if (test_move_dir != bad_move_dir)
12877   {
12878     /* good thing can be player or penguin that does not move away */
12879     if (IS_PLAYER(test_x, test_y))
12880     {
12881       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12882
12883       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12884          player as being hit when he is moving towards the bad thing, because
12885          the "get hit by" condition would be lost after the player stops) */
12886       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12887         return;         /* player moves away from bad thing */
12888
12889       kill_x = test_x;
12890       kill_y = test_y;
12891     }
12892     else if (test_element == EL_PENGUIN)
12893     {
12894       kill_x = test_x;
12895       kill_y = test_y;
12896     }
12897   }
12898
12899   if (kill_x != -1 || kill_y != -1)
12900   {
12901     if (IS_PLAYER(kill_x, kill_y))
12902     {
12903       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12904
12905       if (player->shield_deadly_time_left > 0 &&
12906           !IS_INDESTRUCTIBLE(bad_element))
12907         Bang(bad_x, bad_y);
12908       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12909         KillPlayer(player);
12910     }
12911     else
12912       Bang(kill_x, kill_y);
12913   }
12914 }
12915
12916 void TestIfPlayerTouchesBadThing(int x, int y)
12917 {
12918   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12919 }
12920
12921 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12922 {
12923   TestIfGoodThingHitsBadThing(x, y, move_dir);
12924 }
12925
12926 void TestIfBadThingTouchesPlayer(int x, int y)
12927 {
12928   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12929 }
12930
12931 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12932 {
12933   TestIfBadThingHitsGoodThing(x, y, move_dir);
12934 }
12935
12936 void TestIfFriendTouchesBadThing(int x, int y)
12937 {
12938   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12939 }
12940
12941 void TestIfBadThingTouchesFriend(int x, int y)
12942 {
12943   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12944 }
12945
12946 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12947 {
12948   int i, kill_x = bad_x, kill_y = bad_y;
12949   static int xy[4][2] =
12950   {
12951     { 0, -1 },
12952     { -1, 0 },
12953     { +1, 0 },
12954     { 0, +1 }
12955   };
12956
12957   for (i = 0; i < NUM_DIRECTIONS; i++)
12958   {
12959     int x, y, element;
12960
12961     x = bad_x + xy[i][0];
12962     y = bad_y + xy[i][1];
12963     if (!IN_LEV_FIELD(x, y))
12964       continue;
12965
12966     element = Feld[x][y];
12967     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12968         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12969     {
12970       kill_x = x;
12971       kill_y = y;
12972       break;
12973     }
12974   }
12975
12976   if (kill_x != bad_x || kill_y != bad_y)
12977     Bang(bad_x, bad_y);
12978 }
12979
12980 void KillPlayer(struct PlayerInfo *player)
12981 {
12982   int jx = player->jx, jy = player->jy;
12983
12984   if (!player->active)
12985     return;
12986
12987 #if 0
12988   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12989          player->killed, player->active, player->reanimated);
12990 #endif
12991
12992   /* the following code was introduced to prevent an infinite loop when calling
12993      -> Bang()
12994      -> CheckTriggeredElementChangeExt()
12995      -> ExecuteCustomElementAction()
12996      -> KillPlayer()
12997      -> (infinitely repeating the above sequence of function calls)
12998      which occurs when killing the player while having a CE with the setting
12999      "kill player X when explosion of <player X>"; the solution using a new
13000      field "player->killed" was chosen for backwards compatibility, although
13001      clever use of the fields "player->active" etc. would probably also work */
13002 #if 1
13003   if (player->killed)
13004     return;
13005 #endif
13006
13007   player->killed = TRUE;
13008
13009   /* remove accessible field at the player's position */
13010   Feld[jx][jy] = EL_EMPTY;
13011
13012   /* deactivate shield (else Bang()/Explode() would not work right) */
13013   player->shield_normal_time_left = 0;
13014   player->shield_deadly_time_left = 0;
13015
13016 #if 0
13017   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13018          player->killed, player->active, player->reanimated);
13019 #endif
13020
13021   Bang(jx, jy);
13022
13023 #if 0
13024   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13025          player->killed, player->active, player->reanimated);
13026 #endif
13027
13028   if (player->reanimated)       /* killed player may have been reanimated */
13029     player->killed = player->reanimated = FALSE;
13030   else
13031     BuryPlayer(player);
13032 }
13033
13034 static void KillPlayerUnlessEnemyProtected(int x, int y)
13035 {
13036   if (!PLAYER_ENEMY_PROTECTED(x, y))
13037     KillPlayer(PLAYERINFO(x, y));
13038 }
13039
13040 static void KillPlayerUnlessExplosionProtected(int x, int y)
13041 {
13042   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13043     KillPlayer(PLAYERINFO(x, y));
13044 }
13045
13046 void BuryPlayer(struct PlayerInfo *player)
13047 {
13048   int jx = player->jx, jy = player->jy;
13049
13050   if (!player->active)
13051     return;
13052
13053   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13054   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13055
13056   player->GameOver = TRUE;
13057   RemovePlayer(player);
13058 }
13059
13060 void RemovePlayer(struct PlayerInfo *player)
13061 {
13062   int jx = player->jx, jy = player->jy;
13063   int i, found = FALSE;
13064
13065   player->present = FALSE;
13066   player->active = FALSE;
13067
13068   if (!ExplodeField[jx][jy])
13069     StorePlayer[jx][jy] = 0;
13070
13071   if (player->is_moving)
13072     TEST_DrawLevelField(player->last_jx, player->last_jy);
13073
13074   for (i = 0; i < MAX_PLAYERS; i++)
13075     if (stored_player[i].active)
13076       found = TRUE;
13077
13078   if (!found)
13079     AllPlayersGone = TRUE;
13080
13081   ExitX = ZX = jx;
13082   ExitY = ZY = jy;
13083 }
13084
13085 static void setFieldForSnapping(int x, int y, int element, int direction)
13086 {
13087   struct ElementInfo *ei = &element_info[element];
13088   int direction_bit = MV_DIR_TO_BIT(direction);
13089   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13090   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13091                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13092
13093   Feld[x][y] = EL_ELEMENT_SNAPPING;
13094   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13095
13096   ResetGfxAnimation(x, y);
13097
13098   GfxElement[x][y] = element;
13099   GfxAction[x][y] = action;
13100   GfxDir[x][y] = direction;
13101   GfxFrame[x][y] = -1;
13102 }
13103
13104 /*
13105   =============================================================================
13106   checkDiagonalPushing()
13107   -----------------------------------------------------------------------------
13108   check if diagonal input device direction results in pushing of object
13109   (by checking if the alternative direction is walkable, diggable, ...)
13110   =============================================================================
13111 */
13112
13113 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13114                                     int x, int y, int real_dx, int real_dy)
13115 {
13116   int jx, jy, dx, dy, xx, yy;
13117
13118   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13119     return TRUE;
13120
13121   /* diagonal direction: check alternative direction */
13122   jx = player->jx;
13123   jy = player->jy;
13124   dx = x - jx;
13125   dy = y - jy;
13126   xx = jx + (dx == 0 ? real_dx : 0);
13127   yy = jy + (dy == 0 ? real_dy : 0);
13128
13129   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13130 }
13131
13132 /*
13133   =============================================================================
13134   DigField()
13135   -----------------------------------------------------------------------------
13136   x, y:                 field next to player (non-diagonal) to try to dig to
13137   real_dx, real_dy:     direction as read from input device (can be diagonal)
13138   =============================================================================
13139 */
13140
13141 static int DigField(struct PlayerInfo *player,
13142                     int oldx, int oldy, int x, int y,
13143                     int real_dx, int real_dy, int mode)
13144 {
13145   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13146   boolean player_was_pushing = player->is_pushing;
13147   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13148   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13149   int jx = oldx, jy = oldy;
13150   int dx = x - jx, dy = y - jy;
13151   int nextx = x + dx, nexty = y + dy;
13152   int move_direction = (dx == -1 ? MV_LEFT  :
13153                         dx == +1 ? MV_RIGHT :
13154                         dy == -1 ? MV_UP    :
13155                         dy == +1 ? MV_DOWN  : MV_NONE);
13156   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13157   int dig_side = MV_DIR_OPPOSITE(move_direction);
13158   int old_element = Feld[jx][jy];
13159   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13160   int collect_count;
13161
13162   if (is_player)                /* function can also be called by EL_PENGUIN */
13163   {
13164     if (player->MovPos == 0)
13165     {
13166       player->is_digging = FALSE;
13167       player->is_collecting = FALSE;
13168     }
13169
13170     if (player->MovPos == 0)    /* last pushing move finished */
13171       player->is_pushing = FALSE;
13172
13173     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13174     {
13175       player->is_switching = FALSE;
13176       player->push_delay = -1;
13177
13178       return MP_NO_ACTION;
13179     }
13180   }
13181
13182   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13183     old_element = Back[jx][jy];
13184
13185   /* in case of element dropped at player position, check background */
13186   else if (Back[jx][jy] != EL_EMPTY &&
13187            game.engine_version >= VERSION_IDENT(2,2,0,0))
13188     old_element = Back[jx][jy];
13189
13190   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13191     return MP_NO_ACTION;        /* field has no opening in this direction */
13192
13193   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13194     return MP_NO_ACTION;        /* field has no opening in this direction */
13195
13196   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13197   {
13198     SplashAcid(x, y);
13199
13200     Feld[jx][jy] = player->artwork_element;
13201     InitMovingField(jx, jy, MV_DOWN);
13202     Store[jx][jy] = EL_ACID;
13203     ContinueMoving(jx, jy);
13204     BuryPlayer(player);
13205
13206     return MP_DONT_RUN_INTO;
13207   }
13208
13209   if (player_can_move && DONT_RUN_INTO(element))
13210   {
13211     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13212
13213     return MP_DONT_RUN_INTO;
13214   }
13215
13216   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13217     return MP_NO_ACTION;
13218
13219   collect_count = element_info[element].collect_count_initial;
13220
13221   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13222     return MP_NO_ACTION;
13223
13224   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13225     player_can_move = player_can_move_or_snap;
13226
13227   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13228       game.engine_version >= VERSION_IDENT(2,2,0,0))
13229   {
13230     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13231                                player->index_bit, dig_side);
13232     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13233                                         player->index_bit, dig_side);
13234
13235     if (element == EL_DC_LANDMINE)
13236       Bang(x, y);
13237
13238     if (Feld[x][y] != element)          /* field changed by snapping */
13239       return MP_ACTION;
13240
13241     return MP_NO_ACTION;
13242   }
13243
13244   if (player->gravity && is_player && !player->is_auto_moving &&
13245       canFallDown(player) && move_direction != MV_DOWN &&
13246       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13247     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13248
13249   if (player_can_move &&
13250       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13251   {
13252     int sound_element = SND_ELEMENT(element);
13253     int sound_action = ACTION_WALKING;
13254
13255     if (IS_RND_GATE(element))
13256     {
13257       if (!player->key[RND_GATE_NR(element)])
13258         return MP_NO_ACTION;
13259     }
13260     else if (IS_RND_GATE_GRAY(element))
13261     {
13262       if (!player->key[RND_GATE_GRAY_NR(element)])
13263         return MP_NO_ACTION;
13264     }
13265     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13266     {
13267       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13268         return MP_NO_ACTION;
13269     }
13270     else if (element == EL_EXIT_OPEN ||
13271              element == EL_EM_EXIT_OPEN ||
13272              element == EL_EM_EXIT_OPENING ||
13273              element == EL_STEEL_EXIT_OPEN ||
13274              element == EL_EM_STEEL_EXIT_OPEN ||
13275              element == EL_EM_STEEL_EXIT_OPENING ||
13276              element == EL_SP_EXIT_OPEN ||
13277              element == EL_SP_EXIT_OPENING)
13278     {
13279       sound_action = ACTION_PASSING;    /* player is passing exit */
13280     }
13281     else if (element == EL_EMPTY)
13282     {
13283       sound_action = ACTION_MOVING;             /* nothing to walk on */
13284     }
13285
13286     /* play sound from background or player, whatever is available */
13287     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13288       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13289     else
13290       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13291   }
13292   else if (player_can_move &&
13293            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13294   {
13295     if (!ACCESS_FROM(element, opposite_direction))
13296       return MP_NO_ACTION;      /* field not accessible from this direction */
13297
13298     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13299       return MP_NO_ACTION;
13300
13301     if (IS_EM_GATE(element))
13302     {
13303       if (!player->key[EM_GATE_NR(element)])
13304         return MP_NO_ACTION;
13305     }
13306     else if (IS_EM_GATE_GRAY(element))
13307     {
13308       if (!player->key[EM_GATE_GRAY_NR(element)])
13309         return MP_NO_ACTION;
13310     }
13311     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13312     {
13313       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13314         return MP_NO_ACTION;
13315     }
13316     else if (IS_EMC_GATE(element))
13317     {
13318       if (!player->key[EMC_GATE_NR(element)])
13319         return MP_NO_ACTION;
13320     }
13321     else if (IS_EMC_GATE_GRAY(element))
13322     {
13323       if (!player->key[EMC_GATE_GRAY_NR(element)])
13324         return MP_NO_ACTION;
13325     }
13326     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13327     {
13328       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13329         return MP_NO_ACTION;
13330     }
13331     else if (element == EL_DC_GATE_WHITE ||
13332              element == EL_DC_GATE_WHITE_GRAY ||
13333              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13334     {
13335       if (player->num_white_keys == 0)
13336         return MP_NO_ACTION;
13337
13338       player->num_white_keys--;
13339     }
13340     else if (IS_SP_PORT(element))
13341     {
13342       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13343           element == EL_SP_GRAVITY_PORT_RIGHT ||
13344           element == EL_SP_GRAVITY_PORT_UP ||
13345           element == EL_SP_GRAVITY_PORT_DOWN)
13346         player->gravity = !player->gravity;
13347       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13348                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13349                element == EL_SP_GRAVITY_ON_PORT_UP ||
13350                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13351         player->gravity = TRUE;
13352       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13353                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13354                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13355                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13356         player->gravity = FALSE;
13357     }
13358
13359     /* automatically move to the next field with double speed */
13360     player->programmed_action = move_direction;
13361
13362     if (player->move_delay_reset_counter == 0)
13363     {
13364       player->move_delay_reset_counter = 2;     /* two double speed steps */
13365
13366       DOUBLE_PLAYER_SPEED(player);
13367     }
13368
13369     PlayLevelSoundAction(x, y, ACTION_PASSING);
13370   }
13371   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13372   {
13373     RemoveField(x, y);
13374
13375     if (mode != DF_SNAP)
13376     {
13377       GfxElement[x][y] = GFX_ELEMENT(element);
13378       player->is_digging = TRUE;
13379     }
13380
13381     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13382
13383     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13384                                         player->index_bit, dig_side);
13385
13386     if (mode == DF_SNAP)
13387     {
13388       if (level.block_snap_field)
13389         setFieldForSnapping(x, y, element, move_direction);
13390       else
13391         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13392
13393       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13394                                           player->index_bit, dig_side);
13395     }
13396   }
13397   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13398   {
13399     RemoveField(x, y);
13400
13401     if (is_player && mode != DF_SNAP)
13402     {
13403       GfxElement[x][y] = element;
13404       player->is_collecting = TRUE;
13405     }
13406
13407     if (element == EL_SPEED_PILL)
13408     {
13409       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13410     }
13411     else if (element == EL_EXTRA_TIME && level.time > 0)
13412     {
13413       TimeLeft += level.extra_time;
13414
13415       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13416
13417       DisplayGameControlValues();
13418     }
13419     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13420     {
13421       player->shield_normal_time_left += level.shield_normal_time;
13422       if (element == EL_SHIELD_DEADLY)
13423         player->shield_deadly_time_left += level.shield_deadly_time;
13424     }
13425     else if (element == EL_DYNAMITE ||
13426              element == EL_EM_DYNAMITE ||
13427              element == EL_SP_DISK_RED)
13428     {
13429       if (player->inventory_size < MAX_INVENTORY_SIZE)
13430         player->inventory_element[player->inventory_size++] = element;
13431
13432       DrawGameDoorValues();
13433     }
13434     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13435     {
13436       player->dynabomb_count++;
13437       player->dynabombs_left++;
13438     }
13439     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13440     {
13441       player->dynabomb_size++;
13442     }
13443     else if (element == EL_DYNABOMB_INCREASE_POWER)
13444     {
13445       player->dynabomb_xl = TRUE;
13446     }
13447     else if (IS_KEY(element))
13448     {
13449       player->key[KEY_NR(element)] = TRUE;
13450
13451       DrawGameDoorValues();
13452     }
13453     else if (element == EL_DC_KEY_WHITE)
13454     {
13455       player->num_white_keys++;
13456
13457       /* display white keys? */
13458       /* DrawGameDoorValues(); */
13459     }
13460     else if (IS_ENVELOPE(element))
13461     {
13462       player->show_envelope = element;
13463     }
13464     else if (element == EL_EMC_LENSES)
13465     {
13466       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13467
13468       RedrawAllInvisibleElementsForLenses();
13469     }
13470     else if (element == EL_EMC_MAGNIFIER)
13471     {
13472       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13473
13474       RedrawAllInvisibleElementsForMagnifier();
13475     }
13476     else if (IS_DROPPABLE(element) ||
13477              IS_THROWABLE(element))     /* can be collected and dropped */
13478     {
13479       int i;
13480
13481       if (collect_count == 0)
13482         player->inventory_infinite_element = element;
13483       else
13484         for (i = 0; i < collect_count; i++)
13485           if (player->inventory_size < MAX_INVENTORY_SIZE)
13486             player->inventory_element[player->inventory_size++] = element;
13487
13488       DrawGameDoorValues();
13489     }
13490     else if (collect_count > 0)
13491     {
13492       local_player->gems_still_needed -= collect_count;
13493       if (local_player->gems_still_needed < 0)
13494         local_player->gems_still_needed = 0;
13495
13496       game.snapshot.collected_item = TRUE;
13497
13498       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13499
13500       DisplayGameControlValues();
13501     }
13502
13503     RaiseScoreElement(element);
13504     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13505
13506     if (is_player)
13507       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13508                                           player->index_bit, dig_side);
13509
13510     if (mode == DF_SNAP)
13511     {
13512       if (level.block_snap_field)
13513         setFieldForSnapping(x, y, element, move_direction);
13514       else
13515         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13516
13517       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13518                                           player->index_bit, dig_side);
13519     }
13520   }
13521   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13522   {
13523     if (mode == DF_SNAP && element != EL_BD_ROCK)
13524       return MP_NO_ACTION;
13525
13526     if (CAN_FALL(element) && dy)
13527       return MP_NO_ACTION;
13528
13529     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13530         !(element == EL_SPRING && level.use_spring_bug))
13531       return MP_NO_ACTION;
13532
13533     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13534         ((move_direction & MV_VERTICAL &&
13535           ((element_info[element].move_pattern & MV_LEFT &&
13536             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13537            (element_info[element].move_pattern & MV_RIGHT &&
13538             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13539          (move_direction & MV_HORIZONTAL &&
13540           ((element_info[element].move_pattern & MV_UP &&
13541             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13542            (element_info[element].move_pattern & MV_DOWN &&
13543             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13544       return MP_NO_ACTION;
13545
13546     /* do not push elements already moving away faster than player */
13547     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13548         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13549       return MP_NO_ACTION;
13550
13551     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13552     {
13553       if (player->push_delay_value == -1 || !player_was_pushing)
13554         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13555     }
13556     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13557     {
13558       if (player->push_delay_value == -1)
13559         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13560     }
13561     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13562     {
13563       if (!player->is_pushing)
13564         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13565     }
13566
13567     player->is_pushing = TRUE;
13568     player->is_active = TRUE;
13569
13570     if (!(IN_LEV_FIELD(nextx, nexty) &&
13571           (IS_FREE(nextx, nexty) ||
13572            (IS_SB_ELEMENT(element) &&
13573             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13574            (IS_CUSTOM_ELEMENT(element) &&
13575             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13576       return MP_NO_ACTION;
13577
13578     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13579       return MP_NO_ACTION;
13580
13581     if (player->push_delay == -1)       /* new pushing; restart delay */
13582       player->push_delay = 0;
13583
13584     if (player->push_delay < player->push_delay_value &&
13585         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13586         element != EL_SPRING && element != EL_BALLOON)
13587     {
13588       /* make sure that there is no move delay before next try to push */
13589       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13590         player->move_delay = 0;
13591
13592       return MP_NO_ACTION;
13593     }
13594
13595     if (IS_CUSTOM_ELEMENT(element) &&
13596         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13597     {
13598       if (!DigFieldByCE(nextx, nexty, element))
13599         return MP_NO_ACTION;
13600     }
13601
13602     if (IS_SB_ELEMENT(element))
13603     {
13604       if (element == EL_SOKOBAN_FIELD_FULL)
13605       {
13606         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13607         local_player->sokobanfields_still_needed++;
13608       }
13609
13610       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13611       {
13612         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13613         local_player->sokobanfields_still_needed--;
13614       }
13615
13616       Feld[x][y] = EL_SOKOBAN_OBJECT;
13617
13618       if (Back[x][y] == Back[nextx][nexty])
13619         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13620       else if (Back[x][y] != 0)
13621         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13622                                     ACTION_EMPTYING);
13623       else
13624         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13625                                     ACTION_FILLING);
13626
13627       if (local_player->sokobanfields_still_needed == 0 &&
13628           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13629       {
13630         PlayerWins(player);
13631
13632         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13633       }
13634     }
13635     else
13636       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13637
13638     InitMovingField(x, y, move_direction);
13639     GfxAction[x][y] = ACTION_PUSHING;
13640
13641     if (mode == DF_SNAP)
13642       ContinueMoving(x, y);
13643     else
13644       MovPos[x][y] = (dx != 0 ? dx : dy);
13645
13646     Pushed[x][y] = TRUE;
13647     Pushed[nextx][nexty] = TRUE;
13648
13649     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13650       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13651     else
13652       player->push_delay_value = -1;    /* get new value later */
13653
13654     /* check for element change _after_ element has been pushed */
13655     if (game.use_change_when_pushing_bug)
13656     {
13657       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13658                                  player->index_bit, dig_side);
13659       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13660                                           player->index_bit, dig_side);
13661     }
13662   }
13663   else if (IS_SWITCHABLE(element))
13664   {
13665     if (PLAYER_SWITCHING(player, x, y))
13666     {
13667       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13668                                           player->index_bit, dig_side);
13669
13670       return MP_ACTION;
13671     }
13672
13673     player->is_switching = TRUE;
13674     player->switch_x = x;
13675     player->switch_y = y;
13676
13677     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13678
13679     if (element == EL_ROBOT_WHEEL)
13680     {
13681       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13682       ZX = x;
13683       ZY = y;
13684
13685       game.robot_wheel_active = TRUE;
13686
13687       TEST_DrawLevelField(x, y);
13688     }
13689     else if (element == EL_SP_TERMINAL)
13690     {
13691       int xx, yy;
13692
13693       SCAN_PLAYFIELD(xx, yy)
13694       {
13695         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13696         {
13697           Bang(xx, yy);
13698         }
13699         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13700         {
13701           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13702
13703           ResetGfxAnimation(xx, yy);
13704           TEST_DrawLevelField(xx, yy);
13705         }
13706       }
13707     }
13708     else if (IS_BELT_SWITCH(element))
13709     {
13710       ToggleBeltSwitch(x, y);
13711     }
13712     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13713              element == EL_SWITCHGATE_SWITCH_DOWN ||
13714              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13715              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13716     {
13717       ToggleSwitchgateSwitch(x, y);
13718     }
13719     else if (element == EL_LIGHT_SWITCH ||
13720              element == EL_LIGHT_SWITCH_ACTIVE)
13721     {
13722       ToggleLightSwitch(x, y);
13723     }
13724     else if (element == EL_TIMEGATE_SWITCH ||
13725              element == EL_DC_TIMEGATE_SWITCH)
13726     {
13727       ActivateTimegateSwitch(x, y);
13728     }
13729     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13730              element == EL_BALLOON_SWITCH_RIGHT ||
13731              element == EL_BALLOON_SWITCH_UP    ||
13732              element == EL_BALLOON_SWITCH_DOWN  ||
13733              element == EL_BALLOON_SWITCH_NONE  ||
13734              element == EL_BALLOON_SWITCH_ANY)
13735     {
13736       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13737                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13738                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13739                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13740                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13741                              move_direction);
13742     }
13743     else if (element == EL_LAMP)
13744     {
13745       Feld[x][y] = EL_LAMP_ACTIVE;
13746       local_player->lights_still_needed--;
13747
13748       ResetGfxAnimation(x, y);
13749       TEST_DrawLevelField(x, y);
13750     }
13751     else if (element == EL_TIME_ORB_FULL)
13752     {
13753       Feld[x][y] = EL_TIME_ORB_EMPTY;
13754
13755       if (level.time > 0 || level.use_time_orb_bug)
13756       {
13757         TimeLeft += level.time_orb_time;
13758         game.no_time_limit = FALSE;
13759
13760         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13761
13762         DisplayGameControlValues();
13763       }
13764
13765       ResetGfxAnimation(x, y);
13766       TEST_DrawLevelField(x, y);
13767     }
13768     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13769              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13770     {
13771       int xx, yy;
13772
13773       game.ball_state = !game.ball_state;
13774
13775       SCAN_PLAYFIELD(xx, yy)
13776       {
13777         int e = Feld[xx][yy];
13778
13779         if (game.ball_state)
13780         {
13781           if (e == EL_EMC_MAGIC_BALL)
13782             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13783           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13784             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13785         }
13786         else
13787         {
13788           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13789             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13790           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13791             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13792         }
13793       }
13794     }
13795
13796     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13797                                         player->index_bit, dig_side);
13798
13799     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13800                                         player->index_bit, dig_side);
13801
13802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13803                                         player->index_bit, dig_side);
13804
13805     return MP_ACTION;
13806   }
13807   else
13808   {
13809     if (!PLAYER_SWITCHING(player, x, y))
13810     {
13811       player->is_switching = TRUE;
13812       player->switch_x = x;
13813       player->switch_y = y;
13814
13815       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13816                                  player->index_bit, dig_side);
13817       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13818                                           player->index_bit, dig_side);
13819
13820       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13821                                  player->index_bit, dig_side);
13822       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13823                                           player->index_bit, dig_side);
13824     }
13825
13826     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13827                                player->index_bit, dig_side);
13828     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13829                                         player->index_bit, dig_side);
13830
13831     return MP_NO_ACTION;
13832   }
13833
13834   player->push_delay = -1;
13835
13836   if (is_player)                /* function can also be called by EL_PENGUIN */
13837   {
13838     if (Feld[x][y] != element)          /* really digged/collected something */
13839     {
13840       player->is_collecting = !player->is_digging;
13841       player->is_active = TRUE;
13842     }
13843   }
13844
13845   return MP_MOVING;
13846 }
13847
13848 static boolean DigFieldByCE(int x, int y, int digging_element)
13849 {
13850   int element = Feld[x][y];
13851
13852   if (!IS_FREE(x, y))
13853   {
13854     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13855                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13856                   ACTION_BREAKING);
13857
13858     /* no element can dig solid indestructible elements */
13859     if (IS_INDESTRUCTIBLE(element) &&
13860         !IS_DIGGABLE(element) &&
13861         !IS_COLLECTIBLE(element))
13862       return FALSE;
13863
13864     if (AmoebaNr[x][y] &&
13865         (element == EL_AMOEBA_FULL ||
13866          element == EL_BD_AMOEBA ||
13867          element == EL_AMOEBA_GROWING))
13868     {
13869       AmoebaCnt[AmoebaNr[x][y]]--;
13870       AmoebaCnt2[AmoebaNr[x][y]]--;
13871     }
13872
13873     if (IS_MOVING(x, y))
13874       RemoveMovingField(x, y);
13875     else
13876     {
13877       RemoveField(x, y);
13878       TEST_DrawLevelField(x, y);
13879     }
13880
13881     /* if digged element was about to explode, prevent the explosion */
13882     ExplodeField[x][y] = EX_TYPE_NONE;
13883
13884     PlayLevelSoundAction(x, y, action);
13885   }
13886
13887   Store[x][y] = EL_EMPTY;
13888
13889   /* this makes it possible to leave the removed element again */
13890   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13891     Store[x][y] = element;
13892
13893   return TRUE;
13894 }
13895
13896 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13897 {
13898   int jx = player->jx, jy = player->jy;
13899   int x = jx + dx, y = jy + dy;
13900   int snap_direction = (dx == -1 ? MV_LEFT  :
13901                         dx == +1 ? MV_RIGHT :
13902                         dy == -1 ? MV_UP    :
13903                         dy == +1 ? MV_DOWN  : MV_NONE);
13904   boolean can_continue_snapping = (level.continuous_snapping &&
13905                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13906
13907   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13908     return FALSE;
13909
13910   if (!player->active || !IN_LEV_FIELD(x, y))
13911     return FALSE;
13912
13913   if (dx && dy)
13914     return FALSE;
13915
13916   if (!dx && !dy)
13917   {
13918     if (player->MovPos == 0)
13919       player->is_pushing = FALSE;
13920
13921     player->is_snapping = FALSE;
13922
13923     if (player->MovPos == 0)
13924     {
13925       player->is_moving = FALSE;
13926       player->is_digging = FALSE;
13927       player->is_collecting = FALSE;
13928     }
13929
13930     return FALSE;
13931   }
13932
13933   /* prevent snapping with already pressed snap key when not allowed */
13934   if (player->is_snapping && !can_continue_snapping)
13935     return FALSE;
13936
13937   player->MovDir = snap_direction;
13938
13939   if (player->MovPos == 0)
13940   {
13941     player->is_moving = FALSE;
13942     player->is_digging = FALSE;
13943     player->is_collecting = FALSE;
13944   }
13945
13946   player->is_dropping = FALSE;
13947   player->is_dropping_pressed = FALSE;
13948   player->drop_pressed_delay = 0;
13949
13950   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13951     return FALSE;
13952
13953   player->is_snapping = TRUE;
13954   player->is_active = TRUE;
13955
13956   if (player->MovPos == 0)
13957   {
13958     player->is_moving = FALSE;
13959     player->is_digging = FALSE;
13960     player->is_collecting = FALSE;
13961   }
13962
13963   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13964     TEST_DrawLevelField(player->last_jx, player->last_jy);
13965
13966   TEST_DrawLevelField(x, y);
13967
13968   return TRUE;
13969 }
13970
13971 static boolean DropElement(struct PlayerInfo *player)
13972 {
13973   int old_element, new_element;
13974   int dropx = player->jx, dropy = player->jy;
13975   int drop_direction = player->MovDir;
13976   int drop_side = drop_direction;
13977   int drop_element = get_next_dropped_element(player);
13978
13979   player->is_dropping_pressed = TRUE;
13980
13981   /* do not drop an element on top of another element; when holding drop key
13982      pressed without moving, dropped element must move away before the next
13983      element can be dropped (this is especially important if the next element
13984      is dynamite, which can be placed on background for historical reasons) */
13985   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13986     return MP_ACTION;
13987
13988   if (IS_THROWABLE(drop_element))
13989   {
13990     dropx += GET_DX_FROM_DIR(drop_direction);
13991     dropy += GET_DY_FROM_DIR(drop_direction);
13992
13993     if (!IN_LEV_FIELD(dropx, dropy))
13994       return FALSE;
13995   }
13996
13997   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13998   new_element = drop_element;           /* default: no change when dropping */
13999
14000   /* check if player is active, not moving and ready to drop */
14001   if (!player->active || player->MovPos || player->drop_delay > 0)
14002     return FALSE;
14003
14004   /* check if player has anything that can be dropped */
14005   if (new_element == EL_UNDEFINED)
14006     return FALSE;
14007
14008   /* check if drop key was pressed long enough for EM style dynamite */
14009   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14010     return FALSE;
14011
14012   /* check if anything can be dropped at the current position */
14013   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14014     return FALSE;
14015
14016   /* collected custom elements can only be dropped on empty fields */
14017   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14018     return FALSE;
14019
14020   if (old_element != EL_EMPTY)
14021     Back[dropx][dropy] = old_element;   /* store old element on this field */
14022
14023   ResetGfxAnimation(dropx, dropy);
14024   ResetRandomAnimationValue(dropx, dropy);
14025
14026   if (player->inventory_size > 0 ||
14027       player->inventory_infinite_element != EL_UNDEFINED)
14028   {
14029     if (player->inventory_size > 0)
14030     {
14031       player->inventory_size--;
14032
14033       DrawGameDoorValues();
14034
14035       if (new_element == EL_DYNAMITE)
14036         new_element = EL_DYNAMITE_ACTIVE;
14037       else if (new_element == EL_EM_DYNAMITE)
14038         new_element = EL_EM_DYNAMITE_ACTIVE;
14039       else if (new_element == EL_SP_DISK_RED)
14040         new_element = EL_SP_DISK_RED_ACTIVE;
14041     }
14042
14043     Feld[dropx][dropy] = new_element;
14044
14045     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14046       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14047                           el2img(Feld[dropx][dropy]), 0);
14048
14049     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14050
14051     /* needed if previous element just changed to "empty" in the last frame */
14052     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14053
14054     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14055                                player->index_bit, drop_side);
14056     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14057                                         CE_PLAYER_DROPS_X,
14058                                         player->index_bit, drop_side);
14059
14060     TestIfElementTouchesCustomElement(dropx, dropy);
14061   }
14062   else          /* player is dropping a dyna bomb */
14063   {
14064     player->dynabombs_left--;
14065
14066     Feld[dropx][dropy] = new_element;
14067
14068     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14069       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14070                           el2img(Feld[dropx][dropy]), 0);
14071
14072     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14073   }
14074
14075   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14076     InitField_WithBug1(dropx, dropy, FALSE);
14077
14078   new_element = Feld[dropx][dropy];     /* element might have changed */
14079
14080   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14081       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14082   {
14083     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14084       MovDir[dropx][dropy] = drop_direction;
14085
14086     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14087
14088     /* do not cause impact style collision by dropping elements that can fall */
14089     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14090   }
14091
14092   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14093   player->is_dropping = TRUE;
14094
14095   player->drop_pressed_delay = 0;
14096   player->is_dropping_pressed = FALSE;
14097
14098   player->drop_x = dropx;
14099   player->drop_y = dropy;
14100
14101   return TRUE;
14102 }
14103
14104 /* ------------------------------------------------------------------------- */
14105 /* game sound playing functions                                              */
14106 /* ------------------------------------------------------------------------- */
14107
14108 static int *loop_sound_frame = NULL;
14109 static int *loop_sound_volume = NULL;
14110
14111 void InitPlayLevelSound()
14112 {
14113   int num_sounds = getSoundListSize();
14114
14115   checked_free(loop_sound_frame);
14116   checked_free(loop_sound_volume);
14117
14118   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14119   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14120 }
14121
14122 static void PlayLevelSound(int x, int y, int nr)
14123 {
14124   int sx = SCREENX(x), sy = SCREENY(y);
14125   int volume, stereo_position;
14126   int max_distance = 8;
14127   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14128
14129   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14130       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14131     return;
14132
14133   if (!IN_LEV_FIELD(x, y) ||
14134       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14135       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14136     return;
14137
14138   volume = SOUND_MAX_VOLUME;
14139
14140   if (!IN_SCR_FIELD(sx, sy))
14141   {
14142     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14143     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14144
14145     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14146   }
14147
14148   stereo_position = (SOUND_MAX_LEFT +
14149                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14150                      (SCR_FIELDX + 2 * max_distance));
14151
14152   if (IS_LOOP_SOUND(nr))
14153   {
14154     /* This assures that quieter loop sounds do not overwrite louder ones,
14155        while restarting sound volume comparison with each new game frame. */
14156
14157     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14158       return;
14159
14160     loop_sound_volume[nr] = volume;
14161     loop_sound_frame[nr] = FrameCounter;
14162   }
14163
14164   PlaySoundExt(nr, volume, stereo_position, type);
14165 }
14166
14167 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14168 {
14169   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14170                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14171                  y < LEVELY(BY1) ? LEVELY(BY1) :
14172                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14173                  sound_action);
14174 }
14175
14176 static void PlayLevelSoundAction(int x, int y, int action)
14177 {
14178   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14179 }
14180
14181 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14182 {
14183   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14184
14185   if (sound_effect != SND_UNDEFINED)
14186     PlayLevelSound(x, y, sound_effect);
14187 }
14188
14189 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14190                                               int action)
14191 {
14192   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14193
14194   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14195     PlayLevelSound(x, y, sound_effect);
14196 }
14197
14198 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14199 {
14200   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14201
14202   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14203     PlayLevelSound(x, y, sound_effect);
14204 }
14205
14206 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14207 {
14208   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14209
14210   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14211     StopSound(sound_effect);
14212 }
14213
14214 static void PlayLevelMusic()
14215 {
14216   if (levelset.music[level_nr] != MUS_UNDEFINED)
14217     PlayMusic(levelset.music[level_nr]);        /* from config file */
14218   else
14219     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14220 }
14221
14222 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14223 {
14224   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14225   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14226   int x = xx - 1 - offset;
14227   int y = yy - 1 - offset;
14228
14229   switch (sample)
14230   {
14231     case SAMPLE_blank:
14232       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14233       break;
14234
14235     case SAMPLE_roll:
14236       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14237       break;
14238
14239     case SAMPLE_stone:
14240       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14241       break;
14242
14243     case SAMPLE_nut:
14244       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14245       break;
14246
14247     case SAMPLE_crack:
14248       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14249       break;
14250
14251     case SAMPLE_bug:
14252       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14253       break;
14254
14255     case SAMPLE_tank:
14256       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14257       break;
14258
14259     case SAMPLE_android_clone:
14260       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14261       break;
14262
14263     case SAMPLE_android_move:
14264       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14265       break;
14266
14267     case SAMPLE_spring:
14268       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14269       break;
14270
14271     case SAMPLE_slurp:
14272       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14273       break;
14274
14275     case SAMPLE_eater:
14276       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14277       break;
14278
14279     case SAMPLE_eater_eat:
14280       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14281       break;
14282
14283     case SAMPLE_alien:
14284       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14285       break;
14286
14287     case SAMPLE_collect:
14288       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14289       break;
14290
14291     case SAMPLE_diamond:
14292       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14293       break;
14294
14295     case SAMPLE_squash:
14296       /* !!! CHECK THIS !!! */
14297 #if 1
14298       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14299 #else
14300       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14301 #endif
14302       break;
14303
14304     case SAMPLE_wonderfall:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14306       break;
14307
14308     case SAMPLE_drip:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14310       break;
14311
14312     case SAMPLE_push:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14314       break;
14315
14316     case SAMPLE_dirt:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14318       break;
14319
14320     case SAMPLE_acid:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14322       break;
14323
14324     case SAMPLE_ball:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14326       break;
14327
14328     case SAMPLE_grow:
14329       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14330       break;
14331
14332     case SAMPLE_wonder:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14334       break;
14335
14336     case SAMPLE_door:
14337       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14338       break;
14339
14340     case SAMPLE_exit_open:
14341       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14342       break;
14343
14344     case SAMPLE_exit_leave:
14345       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14346       break;
14347
14348     case SAMPLE_dynamite:
14349       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14350       break;
14351
14352     case SAMPLE_tick:
14353       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14354       break;
14355
14356     case SAMPLE_press:
14357       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14358       break;
14359
14360     case SAMPLE_wheel:
14361       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14362       break;
14363
14364     case SAMPLE_boom:
14365       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14366       break;
14367
14368     case SAMPLE_die:
14369       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14370       break;
14371
14372     case SAMPLE_time:
14373       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14374       break;
14375
14376     default:
14377       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14378       break;
14379   }
14380 }
14381
14382 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14383 {
14384   int element = map_element_SP_to_RND(element_sp);
14385   int action = map_action_SP_to_RND(action_sp);
14386   int offset = (setup.sp_show_border_elements ? 0 : 1);
14387   int x = xx - offset;
14388   int y = yy - offset;
14389
14390   PlayLevelSoundElementAction(x, y, element, action);
14391 }
14392
14393 void RaiseScore(int value)
14394 {
14395   local_player->score += value;
14396
14397   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14398
14399   DisplayGameControlValues();
14400 }
14401
14402 void RaiseScoreElement(int element)
14403 {
14404   switch (element)
14405   {
14406     case EL_EMERALD:
14407     case EL_BD_DIAMOND:
14408     case EL_EMERALD_YELLOW:
14409     case EL_EMERALD_RED:
14410     case EL_EMERALD_PURPLE:
14411     case EL_SP_INFOTRON:
14412       RaiseScore(level.score[SC_EMERALD]);
14413       break;
14414     case EL_DIAMOND:
14415       RaiseScore(level.score[SC_DIAMOND]);
14416       break;
14417     case EL_CRYSTAL:
14418       RaiseScore(level.score[SC_CRYSTAL]);
14419       break;
14420     case EL_PEARL:
14421       RaiseScore(level.score[SC_PEARL]);
14422       break;
14423     case EL_BUG:
14424     case EL_BD_BUTTERFLY:
14425     case EL_SP_ELECTRON:
14426       RaiseScore(level.score[SC_BUG]);
14427       break;
14428     case EL_SPACESHIP:
14429     case EL_BD_FIREFLY:
14430     case EL_SP_SNIKSNAK:
14431       RaiseScore(level.score[SC_SPACESHIP]);
14432       break;
14433     case EL_YAMYAM:
14434     case EL_DARK_YAMYAM:
14435       RaiseScore(level.score[SC_YAMYAM]);
14436       break;
14437     case EL_ROBOT:
14438       RaiseScore(level.score[SC_ROBOT]);
14439       break;
14440     case EL_PACMAN:
14441       RaiseScore(level.score[SC_PACMAN]);
14442       break;
14443     case EL_NUT:
14444       RaiseScore(level.score[SC_NUT]);
14445       break;
14446     case EL_DYNAMITE:
14447     case EL_EM_DYNAMITE:
14448     case EL_SP_DISK_RED:
14449     case EL_DYNABOMB_INCREASE_NUMBER:
14450     case EL_DYNABOMB_INCREASE_SIZE:
14451     case EL_DYNABOMB_INCREASE_POWER:
14452       RaiseScore(level.score[SC_DYNAMITE]);
14453       break;
14454     case EL_SHIELD_NORMAL:
14455     case EL_SHIELD_DEADLY:
14456       RaiseScore(level.score[SC_SHIELD]);
14457       break;
14458     case EL_EXTRA_TIME:
14459       RaiseScore(level.extra_time_score);
14460       break;
14461     case EL_KEY_1:
14462     case EL_KEY_2:
14463     case EL_KEY_3:
14464     case EL_KEY_4:
14465     case EL_EM_KEY_1:
14466     case EL_EM_KEY_2:
14467     case EL_EM_KEY_3:
14468     case EL_EM_KEY_4:
14469     case EL_EMC_KEY_5:
14470     case EL_EMC_KEY_6:
14471     case EL_EMC_KEY_7:
14472     case EL_EMC_KEY_8:
14473     case EL_DC_KEY_WHITE:
14474       RaiseScore(level.score[SC_KEY]);
14475       break;
14476     default:
14477       RaiseScore(element_info[element].collect_score);
14478       break;
14479   }
14480 }
14481
14482 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14483 {
14484   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14485   {
14486     /* closing door required in case of envelope style request dialogs */
14487     if (!skip_request)
14488       CloseDoor(DOOR_CLOSE_1);
14489
14490 #if defined(NETWORK_AVALIABLE)
14491     if (options.network)
14492       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14493     else
14494 #endif
14495     {
14496       if (quick_quit)
14497         FadeSkipNextFadeIn();
14498
14499       SetGameStatus(GAME_MODE_MAIN);
14500
14501       DrawMainMenu();
14502     }
14503   }
14504   else          /* continue playing the game */
14505   {
14506     if (tape.playing && tape.deactivate_display)
14507       TapeDeactivateDisplayOff(TRUE);
14508
14509     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14510
14511     if (tape.playing && tape.deactivate_display)
14512       TapeDeactivateDisplayOn();
14513   }
14514 }
14515
14516 void RequestQuitGame(boolean ask_if_really_quit)
14517 {
14518   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14519   boolean skip_request = AllPlayersGone || quick_quit;
14520
14521   RequestQuitGameExt(skip_request, quick_quit,
14522                      "Do you really want to quit the game?");
14523 }
14524
14525
14526 /* ------------------------------------------------------------------------- */
14527 /* random generator functions                                                */
14528 /* ------------------------------------------------------------------------- */
14529
14530 unsigned int InitEngineRandom_RND(int seed)
14531 {
14532   game.num_random_calls = 0;
14533
14534   return InitEngineRandom(seed);
14535 }
14536
14537 unsigned int RND(int max)
14538 {
14539   if (max > 0)
14540   {
14541     game.num_random_calls++;
14542
14543     return GetEngineRandom(max);
14544   }
14545
14546   return 0;
14547 }
14548
14549
14550 /* ------------------------------------------------------------------------- */
14551 /* game engine snapshot handling functions                                   */
14552 /* ------------------------------------------------------------------------- */
14553
14554 struct EngineSnapshotInfo
14555 {
14556   /* runtime values for custom element collect score */
14557   int collect_score[NUM_CUSTOM_ELEMENTS];
14558
14559   /* runtime values for group element choice position */
14560   int choice_pos[NUM_GROUP_ELEMENTS];
14561
14562   /* runtime values for belt position animations */
14563   int belt_graphic[4][NUM_BELT_PARTS];
14564   int belt_anim_mode[4][NUM_BELT_PARTS];
14565 };
14566
14567 static struct EngineSnapshotInfo engine_snapshot_rnd;
14568 static char *snapshot_level_identifier = NULL;
14569 static int snapshot_level_nr = -1;
14570
14571 static void SaveEngineSnapshotValues_RND()
14572 {
14573   static int belt_base_active_element[4] =
14574   {
14575     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14576     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14577     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14578     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14579   };
14580   int i, j;
14581
14582   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14583   {
14584     int element = EL_CUSTOM_START + i;
14585
14586     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14587   }
14588
14589   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14590   {
14591     int element = EL_GROUP_START + i;
14592
14593     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14594   }
14595
14596   for (i = 0; i < 4; i++)
14597   {
14598     for (j = 0; j < NUM_BELT_PARTS; j++)
14599     {
14600       int element = belt_base_active_element[i] + j;
14601       int graphic = el2img(element);
14602       int anim_mode = graphic_info[graphic].anim_mode;
14603
14604       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14605       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14606     }
14607   }
14608 }
14609
14610 static void LoadEngineSnapshotValues_RND()
14611 {
14612   unsigned int num_random_calls = game.num_random_calls;
14613   int i, j;
14614
14615   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14616   {
14617     int element = EL_CUSTOM_START + i;
14618
14619     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14620   }
14621
14622   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14623   {
14624     int element = EL_GROUP_START + i;
14625
14626     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14627   }
14628
14629   for (i = 0; i < 4; i++)
14630   {
14631     for (j = 0; j < NUM_BELT_PARTS; j++)
14632     {
14633       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14634       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14635
14636       graphic_info[graphic].anim_mode = anim_mode;
14637     }
14638   }
14639
14640   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14641   {
14642     InitRND(tape.random_seed);
14643     for (i = 0; i < num_random_calls; i++)
14644       RND(1);
14645   }
14646
14647   if (game.num_random_calls != num_random_calls)
14648   {
14649     Error(ERR_INFO, "number of random calls out of sync");
14650     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14651     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14652     Error(ERR_EXIT, "this should not happen -- please debug");
14653   }
14654 }
14655
14656 void FreeEngineSnapshotSingle()
14657 {
14658   FreeSnapshotSingle();
14659
14660   setString(&snapshot_level_identifier, NULL);
14661   snapshot_level_nr = -1;
14662 }
14663
14664 void FreeEngineSnapshotList()
14665 {
14666   FreeSnapshotList();
14667 }
14668
14669 ListNode *SaveEngineSnapshotBuffers()
14670 {
14671   ListNode *buffers = NULL;
14672
14673   /* copy some special values to a structure better suited for the snapshot */
14674
14675   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14676     SaveEngineSnapshotValues_RND();
14677   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14678     SaveEngineSnapshotValues_EM();
14679   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14680     SaveEngineSnapshotValues_SP(&buffers);
14681
14682   /* save values stored in special snapshot structure */
14683
14684   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14685     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14686   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14687     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14688   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14689     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14690
14691   /* save further RND engine values */
14692
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14696
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14701
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14707
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14711
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14715
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14737
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14740
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14744
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14747
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14753
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14756
14757 #if 0
14758   ListNode *node = engine_snapshot_list_rnd;
14759   int num_bytes = 0;
14760
14761   while (node != NULL)
14762   {
14763     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14764
14765     node = node->next;
14766   }
14767
14768   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14769 #endif
14770
14771   return buffers;
14772 }
14773
14774 void SaveEngineSnapshotSingle()
14775 {
14776   ListNode *buffers = SaveEngineSnapshotBuffers();
14777
14778   /* finally save all snapshot buffers to single snapshot */
14779   SaveSnapshotSingle(buffers);
14780
14781   /* save level identification information */
14782   setString(&snapshot_level_identifier, leveldir_current->identifier);
14783   snapshot_level_nr = level_nr;
14784 }
14785
14786 boolean CheckSaveEngineSnapshotToList()
14787 {
14788   boolean save_snapshot =
14789     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14790      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14791       game.snapshot.changed_action) ||
14792      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14793       game.snapshot.collected_item));
14794
14795   game.snapshot.changed_action = FALSE;
14796   game.snapshot.collected_item = FALSE;
14797   game.snapshot.save_snapshot = save_snapshot;
14798
14799   return save_snapshot;
14800 }
14801
14802 void SaveEngineSnapshotToList()
14803 {
14804   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14805       tape.quick_resume)
14806     return;
14807
14808   ListNode *buffers = SaveEngineSnapshotBuffers();
14809
14810   /* finally save all snapshot buffers to snapshot list */
14811   SaveSnapshotToList(buffers);
14812 }
14813
14814 void SaveEngineSnapshotToListInitial()
14815 {
14816   FreeEngineSnapshotList();
14817
14818   SaveEngineSnapshotToList();
14819 }
14820
14821 void LoadEngineSnapshotValues()
14822 {
14823   /* restore special values from snapshot structure */
14824
14825   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14826     LoadEngineSnapshotValues_RND();
14827   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14828     LoadEngineSnapshotValues_EM();
14829   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14830     LoadEngineSnapshotValues_SP();
14831 }
14832
14833 void LoadEngineSnapshotSingle()
14834 {
14835   LoadSnapshotSingle();
14836
14837   LoadEngineSnapshotValues();
14838 }
14839
14840 void LoadEngineSnapshot_Undo(int steps)
14841 {
14842   LoadSnapshotFromList_Older(steps);
14843
14844   LoadEngineSnapshotValues();
14845 }
14846
14847 void LoadEngineSnapshot_Redo(int steps)
14848 {
14849   LoadSnapshotFromList_Newer(steps);
14850
14851   LoadEngineSnapshotValues();
14852 }
14853
14854 boolean CheckEngineSnapshotSingle()
14855 {
14856   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14857           snapshot_level_nr == level_nr);
14858 }
14859
14860 boolean CheckEngineSnapshotList()
14861 {
14862   return CheckSnapshotList();
14863 }
14864
14865
14866 /* ---------- new game button stuff ---------------------------------------- */
14867
14868 static struct
14869 {
14870   int graphic;
14871   struct XY *pos;
14872   int gadget_id;
14873   char *infotext;
14874 } gamebutton_info[NUM_GAME_BUTTONS] =
14875 {
14876   {
14877     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14878     GAME_CTRL_ID_STOP,                  "stop game"
14879   },
14880   {
14881     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14882     GAME_CTRL_ID_PAUSE,                 "pause game"
14883   },
14884   {
14885     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14886     GAME_CTRL_ID_PLAY,                  "play game"
14887   },
14888   {
14889     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14890     GAME_CTRL_ID_UNDO,                  "undo step"
14891   },
14892   {
14893     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14894     GAME_CTRL_ID_REDO,                  "redo step"
14895   },
14896   {
14897     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14898     GAME_CTRL_ID_SAVE,                  "save game"
14899   },
14900   {
14901     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14902     GAME_CTRL_ID_PAUSE2,                "pause game"
14903   },
14904   {
14905     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14906     GAME_CTRL_ID_LOAD,                  "load game"
14907   },
14908   {
14909     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14910     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14911   },
14912   {
14913     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14914     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14915   },
14916   {
14917     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14918     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14919   }
14920 };
14921
14922 void CreateGameButtons()
14923 {
14924   int i;
14925
14926   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14927   {
14928     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14929     struct XY *pos = gamebutton_info[i].pos;
14930     struct GadgetInfo *gi;
14931     int button_type;
14932     boolean checked;
14933     unsigned int event_mask;
14934     int base_x = (tape.show_game_buttons ? VX : DX);
14935     int base_y = (tape.show_game_buttons ? VY : DY);
14936     int gd_x   = gfx->src_x;
14937     int gd_y   = gfx->src_y;
14938     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14939     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14940     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14941     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14942     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14943     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14944     int id = i;
14945
14946     if (gfx->bitmap == NULL)
14947     {
14948       game_gadget[id] = NULL;
14949
14950       continue;
14951     }
14952
14953     if (id == GAME_CTRL_ID_STOP ||
14954         id == GAME_CTRL_ID_PLAY ||
14955         id == GAME_CTRL_ID_SAVE ||
14956         id == GAME_CTRL_ID_LOAD)
14957     {
14958       button_type = GD_TYPE_NORMAL_BUTTON;
14959       checked = FALSE;
14960       event_mask = GD_EVENT_RELEASED;
14961     }
14962     else if (id == GAME_CTRL_ID_UNDO ||
14963              id == GAME_CTRL_ID_REDO)
14964     {
14965       button_type = GD_TYPE_NORMAL_BUTTON;
14966       checked = FALSE;
14967       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14968     }
14969     else
14970     {
14971       button_type = GD_TYPE_CHECK_BUTTON;
14972       checked =
14973         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14974          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14975          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14976       event_mask = GD_EVENT_PRESSED;
14977     }
14978
14979     gi = CreateGadget(GDI_CUSTOM_ID, id,
14980                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14981                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14982                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14983                       GDI_WIDTH, gfx->width,
14984                       GDI_HEIGHT, gfx->height,
14985                       GDI_TYPE, button_type,
14986                       GDI_STATE, GD_BUTTON_UNPRESSED,
14987                       GDI_CHECKED, checked,
14988                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14989                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14990                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14991                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14992                       GDI_DIRECT_DRAW, FALSE,
14993                       GDI_EVENT_MASK, event_mask,
14994                       GDI_CALLBACK_ACTION, HandleGameButtons,
14995                       GDI_END);
14996
14997     if (gi == NULL)
14998       Error(ERR_EXIT, "cannot create gadget");
14999
15000     game_gadget[id] = gi;
15001   }
15002 }
15003
15004 void FreeGameButtons()
15005 {
15006   int i;
15007
15008   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15009     FreeGadget(game_gadget[i]);
15010 }
15011
15012 static void UnmapGameButtonsAtSamePosition(int id)
15013 {
15014   int i;
15015
15016   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15017     if (i != id &&
15018         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15019         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15020       UnmapGadget(game_gadget[i]);
15021 }
15022
15023 static void UnmapGameButtonsAtSamePosition_All()
15024 {
15025   if (setup.show_snapshot_buttons)
15026   {
15027     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15028     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15029     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15030   }
15031   else
15032   {
15033     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15036   }
15037 }
15038
15039 static void MapGameButtonsAtSamePosition(int id)
15040 {
15041   int i;
15042
15043   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15044     if (i != id &&
15045         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15046         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15047       MapGadget(game_gadget[i]);
15048
15049   UnmapGameButtonsAtSamePosition_All();
15050 }
15051
15052 void MapUndoRedoButtons()
15053 {
15054   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15055   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15056
15057   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15058   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15059
15060   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15061 }
15062
15063 void UnmapUndoRedoButtons()
15064 {
15065   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15066   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15067
15068   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15069   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15070
15071   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15072 }
15073
15074 void MapGameButtons()
15075 {
15076   int i;
15077
15078   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15079     if (i != GAME_CTRL_ID_UNDO &&
15080         i != GAME_CTRL_ID_REDO)
15081       MapGadget(game_gadget[i]);
15082
15083   UnmapGameButtonsAtSamePosition_All();
15084
15085   RedrawGameButtons();
15086 }
15087
15088 void UnmapGameButtons()
15089 {
15090   int i;
15091
15092   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15093     UnmapGadget(game_gadget[i]);
15094 }
15095
15096 void RedrawGameButtons()
15097 {
15098   int i;
15099
15100   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15101     RedrawGadget(game_gadget[i]);
15102
15103   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15104   redraw_mask &= ~REDRAW_ALL;
15105 }
15106
15107 void GameUndoRedoExt()
15108 {
15109   ClearPlayerAction();
15110
15111   tape.pausing = TRUE;
15112
15113   RedrawPlayfield();
15114   UpdateAndDisplayGameControlValues();
15115
15116   DrawCompleteVideoDisplay();
15117   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15118   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15119   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15120
15121   BackToFront();
15122 }
15123
15124 void GameUndo(int steps)
15125 {
15126   if (!CheckEngineSnapshotList())
15127     return;
15128
15129   LoadEngineSnapshot_Undo(steps);
15130
15131   GameUndoRedoExt();
15132 }
15133
15134 void GameRedo(int steps)
15135 {
15136   if (!CheckEngineSnapshotList())
15137     return;
15138
15139   LoadEngineSnapshot_Redo(steps);
15140
15141   GameUndoRedoExt();
15142 }
15143
15144 static void HandleGameButtonsExt(int id, int button)
15145 {
15146   static boolean game_undo_executed = FALSE;
15147   int steps = BUTTON_STEPSIZE(button);
15148   boolean handle_game_buttons =
15149     (game_status == GAME_MODE_PLAYING ||
15150      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15151
15152   if (!handle_game_buttons)
15153     return;
15154
15155   switch (id)
15156   {
15157     case GAME_CTRL_ID_STOP:
15158       if (game_status == GAME_MODE_MAIN)
15159         break;
15160
15161       if (tape.playing)
15162         TapeStop();
15163       else
15164         RequestQuitGame(TRUE);
15165
15166       break;
15167
15168     case GAME_CTRL_ID_PAUSE:
15169     case GAME_CTRL_ID_PAUSE2:
15170       if (options.network && game_status == GAME_MODE_PLAYING)
15171       {
15172 #if defined(NETWORK_AVALIABLE)
15173         if (tape.pausing)
15174           SendToServer_ContinuePlaying();
15175         else
15176           SendToServer_PausePlaying();
15177 #endif
15178       }
15179       else
15180         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15181
15182       game_undo_executed = FALSE;
15183
15184       break;
15185
15186     case GAME_CTRL_ID_PLAY:
15187       if (game_status == GAME_MODE_MAIN)
15188       {
15189         StartGameActions(options.network, setup.autorecord, level.random_seed);
15190       }
15191       else if (tape.pausing)
15192       {
15193 #if defined(NETWORK_AVALIABLE)
15194         if (options.network)
15195           SendToServer_ContinuePlaying();
15196         else
15197 #endif
15198           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15199       }
15200       break;
15201
15202     case GAME_CTRL_ID_UNDO:
15203       // Important: When using "save snapshot when collecting an item" mode,
15204       // load last (current) snapshot for first "undo" after pressing "pause"
15205       // (else the last-but-one snapshot would be loaded, because the snapshot
15206       // pointer already points to the last snapshot when pressing "pause",
15207       // which is fine for "every step/move" mode, but not for "every collect")
15208       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15209           !game_undo_executed)
15210         steps--;
15211
15212       game_undo_executed = TRUE;
15213
15214       GameUndo(steps);
15215       break;
15216
15217     case GAME_CTRL_ID_REDO:
15218       GameRedo(steps);
15219       break;
15220
15221     case GAME_CTRL_ID_SAVE:
15222       TapeQuickSave();
15223       break;
15224
15225     case GAME_CTRL_ID_LOAD:
15226       TapeQuickLoad();
15227       break;
15228
15229     case SOUND_CTRL_ID_MUSIC:
15230       if (setup.sound_music)
15231       { 
15232         setup.sound_music = FALSE;
15233
15234         FadeMusic();
15235       }
15236       else if (audio.music_available)
15237       { 
15238         setup.sound = setup.sound_music = TRUE;
15239
15240         SetAudioMode(setup.sound);
15241
15242         PlayLevelMusic();
15243       }
15244       break;
15245
15246     case SOUND_CTRL_ID_LOOPS:
15247       if (setup.sound_loops)
15248         setup.sound_loops = FALSE;
15249       else if (audio.loops_available)
15250       {
15251         setup.sound = setup.sound_loops = TRUE;
15252
15253         SetAudioMode(setup.sound);
15254       }
15255       break;
15256
15257     case SOUND_CTRL_ID_SIMPLE:
15258       if (setup.sound_simple)
15259         setup.sound_simple = FALSE;
15260       else if (audio.sound_available)
15261       {
15262         setup.sound = setup.sound_simple = TRUE;
15263
15264         SetAudioMode(setup.sound);
15265       }
15266       break;
15267
15268     default:
15269       break;
15270   }
15271 }
15272
15273 static void HandleGameButtons(struct GadgetInfo *gi)
15274 {
15275   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15276 }
15277
15278 void HandleSoundButtonKeys(Key key)
15279 {
15280
15281   if (key == setup.shortcut.sound_simple)
15282     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15283   else if (key == setup.shortcut.sound_loops)
15284     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15285   else if (key == setup.shortcut.sound_music)
15286     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15287 }