rnd-20070407-4-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_ELEMENT_1                    69
204 #define GAME_PANEL_ELEMENT_2                    70
205 #define GAME_PANEL_ELEMENT_3                    71
206 #define GAME_PANEL_ELEMENT_4                    72
207 #define GAME_PANEL_ELEMENT_5                    73
208 #define GAME_PANEL_ELEMENT_6                    74
209 #define GAME_PANEL_ELEMENT_7                    75
210 #define GAME_PANEL_ELEMENT_8                    76
211 #define GAME_PANEL_CE_SCORE_1                   77
212 #define GAME_PANEL_CE_SCORE_2                   78
213 #define GAME_PANEL_CE_SCORE_3                   79
214 #define GAME_PANEL_CE_SCORE_4                   80
215 #define GAME_PANEL_CE_SCORE_5                   81
216 #define GAME_PANEL_CE_SCORE_6                   82
217 #define GAME_PANEL_CE_SCORE_7                   83
218 #define GAME_PANEL_CE_SCORE_8                   84
219 #define GAME_PANEL_CE_SCORE_1_ELEMENT           85
220 #define GAME_PANEL_CE_SCORE_2_ELEMENT           86
221 #define GAME_PANEL_CE_SCORE_3_ELEMENT           87
222 #define GAME_PANEL_CE_SCORE_4_ELEMENT           88
223 #define GAME_PANEL_CE_SCORE_5_ELEMENT           89
224 #define GAME_PANEL_CE_SCORE_6_ELEMENT           90
225 #define GAME_PANEL_CE_SCORE_7_ELEMENT           91
226 #define GAME_PANEL_CE_SCORE_8_ELEMENT           92
227 #define GAME_PANEL_PLAYER_NAME                  93
228 #define GAME_PANEL_LEVEL_NAME                   94
229 #define GAME_PANEL_LEVEL_AUTHOR                 95
230
231 #define NUM_GAME_PANEL_CONTROLS                 96
232
233 struct GamePanelOrderInfo
234 {
235   int nr;
236   int sort_priority;
237 };
238
239 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
240
241 struct GamePanelControlInfo
242 {
243   int nr;
244
245   struct TextPosInfo *pos;
246   int type;
247
248   int value, last_value;
249   int frame, last_frame;
250   int gfx_frame;
251   int gfx_random;
252 };
253
254 static struct GamePanelControlInfo game_panel_controls[] =
255 {
256   {
257     GAME_PANEL_LEVEL_NUMBER,
258     &game.panel.level_number,
259     TYPE_INTEGER,
260   },
261   {
262     GAME_PANEL_GEMS,
263     &game.panel.gems,
264     TYPE_INTEGER,
265   },
266   {
267     GAME_PANEL_INVENTORY_COUNT,
268     &game.panel.inventory_count,
269     TYPE_INTEGER,
270   },
271   {
272     GAME_PANEL_INVENTORY_FIRST_1,
273     &game.panel.inventory_first[0],
274     TYPE_ELEMENT,
275   },
276   {
277     GAME_PANEL_INVENTORY_FIRST_2,
278     &game.panel.inventory_first[1],
279     TYPE_ELEMENT,
280   },
281   {
282     GAME_PANEL_INVENTORY_FIRST_3,
283     &game.panel.inventory_first[2],
284     TYPE_ELEMENT,
285   },
286   {
287     GAME_PANEL_INVENTORY_FIRST_4,
288     &game.panel.inventory_first[3],
289     TYPE_ELEMENT,
290   },
291   {
292     GAME_PANEL_INVENTORY_FIRST_5,
293     &game.panel.inventory_first[4],
294     TYPE_ELEMENT,
295   },
296   {
297     GAME_PANEL_INVENTORY_FIRST_6,
298     &game.panel.inventory_first[5],
299     TYPE_ELEMENT,
300   },
301   {
302     GAME_PANEL_INVENTORY_FIRST_7,
303     &game.panel.inventory_first[6],
304     TYPE_ELEMENT,
305   },
306   {
307     GAME_PANEL_INVENTORY_FIRST_8,
308     &game.panel.inventory_first[7],
309     TYPE_ELEMENT,
310   },
311   {
312     GAME_PANEL_INVENTORY_LAST_1,
313     &game.panel.inventory_last[0],
314     TYPE_ELEMENT,
315   },
316   {
317     GAME_PANEL_INVENTORY_LAST_2,
318     &game.panel.inventory_last[1],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_LAST_3,
323     &game.panel.inventory_last[2],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_LAST_4,
328     &game.panel.inventory_last[3],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_LAST_5,
333     &game.panel.inventory_last[4],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_LAST_6,
338     &game.panel.inventory_last[5],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_LAST_7,
343     &game.panel.inventory_last[6],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_LAST_8,
348     &game.panel.inventory_last[7],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_KEY_1,
353     &game.panel.key[0],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_KEY_2,
358     &game.panel.key[1],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_KEY_3,
363     &game.panel.key[2],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_KEY_4,
368     &game.panel.key[3],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_KEY_5,
373     &game.panel.key[4],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_KEY_6,
378     &game.panel.key[5],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_KEY_7,
383     &game.panel.key[6],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_KEY_8,
388     &game.panel.key[7],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_KEY_WHITE,
393     &game.panel.key_white,
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_WHITE_COUNT,
398     &game.panel.key_white_count,
399     TYPE_INTEGER,
400   },
401   {
402     GAME_PANEL_SCORE,
403     &game.panel.score,
404     TYPE_INTEGER,
405   },
406   {
407     GAME_PANEL_TIME,
408     &game.panel.time,
409     TYPE_INTEGER,
410   },
411   {
412     GAME_PANEL_TIME_HH,
413     &game.panel.time_hh,
414     TYPE_INTEGER,
415   },
416   {
417     GAME_PANEL_TIME_MM,
418     &game.panel.time_mm,
419     TYPE_INTEGER,
420   },
421   {
422     GAME_PANEL_TIME_SS,
423     &game.panel.time_ss,
424     TYPE_INTEGER,
425   },
426   {
427     GAME_PANEL_SHIELD_NORMAL,
428     &game.panel.shield_normal,
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_SHIELD_NORMAL_TIME,
433     &game.panel.shield_normal_time,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_DEADLY,
438     &game.panel.shield_deadly,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_DEADLY_TIME,
443     &game.panel.shield_deadly_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_EXIT,
448     &game.panel.exit,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_EMC_MAGIC_BALL,
453     &game.panel.emc_magic_ball,
454     TYPE_ELEMENT,
455   },
456   {
457     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
458     &game.panel.emc_magic_ball_switch,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_LIGHT_SWITCH,
463     &game.panel.light_switch,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_LIGHT_SWITCH_TIME,
468     &game.panel.light_switch_time,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIMEGATE_SWITCH,
473     &game.panel.timegate_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_TIMEGATE_SWITCH_TIME,
478     &game.panel.timegate_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_SWITCHGATE_SWITCH,
483     &game.panel.switchgate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_EMC_LENSES,
488     &game.panel.emc_lenses,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_EMC_LENSES_TIME,
493     &game.panel.emc_lenses_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EMC_MAGNIFIER,
498     &game.panel.emc_magnifier,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGNIFIER_TIME,
503     &game.panel.emc_magnifier_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_BALLOON_SWITCH,
508     &game.panel.balloon_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_DYNABOMB_NUMBER,
513     &game.panel.dynabomb_number,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_DYNABOMB_SIZE,
518     &game.panel.dynabomb_size,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_DYNABOMB_POWER,
523     &game.panel.dynabomb_power,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_PENGUINS,
528     &game.panel.penguins,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SOKOBAN_OBJECTS,
533     &game.panel.sokoban_objects,
534     TYPE_INTEGER,
535   },
536   {
537     GAME_PANEL_SOKOBAN_FIELDS,
538     &game.panel.sokoban_fields,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_ROBOT_WHEEL,
543     &game.panel.robot_wheel,
544     TYPE_ELEMENT,
545   },
546   {
547     GAME_PANEL_CONVEYOR_BELT_1,
548     &game.panel.conveyor_belt[0],
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_CONVEYOR_BELT_2,
553     &game.panel.conveyor_belt[1],
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_3,
558     &game.panel.conveyor_belt[2],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_4,
563     &game.panel.conveyor_belt[3],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
568     &game.panel.conveyor_belt_switch[0],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
573     &game.panel.conveyor_belt_switch[1],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
578     &game.panel.conveyor_belt_switch[2],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
583     &game.panel.conveyor_belt_switch[3],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_MAGIC_WALL,
588     &game.panel.magic_wall,
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_MAGIC_WALL_TIME,
593     &game.panel.magic_wall_time,
594     TYPE_INTEGER,
595   },
596   {
597     GAME_PANEL_GRAVITY_STATE,
598     &game.panel.gravity_state,
599     TYPE_STRING,
600   },
601   {
602     GAME_PANEL_ELEMENT_1,
603     &game.panel.element[0],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_ELEMENT_2,
608     &game.panel.element[1],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_ELEMENT_3,
613     &game.panel.element[2],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_ELEMENT_4,
618     &game.panel.element[3],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_ELEMENT_5,
623     &game.panel.element[4],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_ELEMENT_6,
628     &game.panel.element[5],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_ELEMENT_7,
633     &game.panel.element[6],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_ELEMENT_8,
638     &game.panel.element[7],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_CE_SCORE_1,
643     &game.panel.ce_score[0],
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_CE_SCORE_2,
648     &game.panel.ce_score[1],
649     TYPE_INTEGER,
650   },
651   {
652     GAME_PANEL_CE_SCORE_3,
653     &game.panel.ce_score[2],
654     TYPE_INTEGER,
655   },
656   {
657     GAME_PANEL_CE_SCORE_4,
658     &game.panel.ce_score[3],
659     TYPE_INTEGER,
660   },
661   {
662     GAME_PANEL_CE_SCORE_5,
663     &game.panel.ce_score[4],
664     TYPE_INTEGER,
665   },
666   {
667     GAME_PANEL_CE_SCORE_6,
668     &game.panel.ce_score[5],
669     TYPE_INTEGER,
670   },
671   {
672     GAME_PANEL_CE_SCORE_7,
673     &game.panel.ce_score[6],
674     TYPE_INTEGER,
675   },
676   {
677     GAME_PANEL_CE_SCORE_8,
678     &game.panel.ce_score[7],
679     TYPE_INTEGER,
680   },
681   {
682     GAME_PANEL_CE_SCORE_1_ELEMENT,
683     &game.panel.ce_score_element[0],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_CE_SCORE_2_ELEMENT,
688     &game.panel.ce_score_element[1],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_CE_SCORE_3_ELEMENT,
693     &game.panel.ce_score_element[2],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_CE_SCORE_4_ELEMENT,
698     &game.panel.ce_score_element[3],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_CE_SCORE_5_ELEMENT,
703     &game.panel.ce_score_element[4],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_CE_SCORE_6_ELEMENT,
708     &game.panel.ce_score_element[5],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_CE_SCORE_7_ELEMENT,
713     &game.panel.ce_score_element[6],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_CE_SCORE_8_ELEMENT,
718     &game.panel.ce_score_element[7],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_PLAYER_NAME,
723     &game.panel.player_name,
724     TYPE_STRING,
725   },
726   {
727     GAME_PANEL_LEVEL_NAME,
728     &game.panel.level_name,
729     TYPE_STRING,
730   },
731   {
732     GAME_PANEL_LEVEL_AUTHOR,
733     &game.panel.level_author,
734     TYPE_STRING,
735   },
736
737   {
738     -1,
739     NULL,
740     -1,
741   }
742 };
743 #endif
744
745
746 /* values for delayed check of falling and moving elements and for collision */
747 #define CHECK_DELAY_MOVING      3
748 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
749 #define CHECK_DELAY_COLLISION   2
750 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
751
752 /* values for initial player move delay (initial delay counter value) */
753 #define INITIAL_MOVE_DELAY_OFF  -1
754 #define INITIAL_MOVE_DELAY_ON   0
755
756 /* values for player movement speed (which is in fact a delay value) */
757 #define MOVE_DELAY_MIN_SPEED    32
758 #define MOVE_DELAY_NORMAL_SPEED 8
759 #define MOVE_DELAY_HIGH_SPEED   4
760 #define MOVE_DELAY_MAX_SPEED    1
761
762 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
763 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
764
765 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
766 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
767
768 /* values for other actions */
769 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
770 #define MOVE_STEPSIZE_MIN       (1)
771 #define MOVE_STEPSIZE_MAX       (TILEX)
772
773 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
774 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
775
776 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
777
778 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
779                                  RND(element_info[e].push_delay_random))
780 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
781                                  RND(element_info[e].drop_delay_random))
782 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
783                                  RND(element_info[e].move_delay_random))
784 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
785                                     (element_info[e].move_delay_random))
786 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
787                                  RND(element_info[e].ce_value_random_initial))
788 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
789 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
790                                  RND((c)->delay_random * (c)->delay_frames))
791 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
792                                  RND((c)->delay_random))
793
794
795 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
796          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
797
798 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
799         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
800          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
801          (be) + (e) - EL_SELF)
802
803 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
804         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
805          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
806          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
807          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
808          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
809          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
810          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
811          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
812          (e))
813
814 #define CAN_GROW_INTO(e)                                                \
815         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
816
817 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
818                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
819                                         (condition)))
820
821 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
822                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
823                                         (CAN_MOVE_INTO_ACID(e) &&       \
824                                          Feld[x][y] == EL_ACID) ||      \
825                                         (condition)))
826
827 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
828                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
829                                         (CAN_MOVE_INTO_ACID(e) &&       \
830                                          Feld[x][y] == EL_ACID) ||      \
831                                         (condition)))
832
833 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
834                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
835                                         (condition) ||                  \
836                                         (CAN_MOVE_INTO_ACID(e) &&       \
837                                          Feld[x][y] == EL_ACID) ||      \
838                                         (DONT_COLLIDE_WITH(e) &&        \
839                                          IS_PLAYER(x, y) &&             \
840                                          !PLAYER_ENEMY_PROTECTED(x, y))))
841
842 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
843         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
844
845 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
846         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
847
848 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
849         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
850
851 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
852         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
853                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
854
855 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
856         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
857
858 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
859         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
860
861 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
862         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
863
864 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
865         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
866
867 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
868         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
869
870 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
871         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
872                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
873                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
874                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
875                                                  IS_FOOD_PENGUIN(Feld[x][y])))
876 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
877         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
878
879 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
880         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
881
882 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
883         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
884
885 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
886         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
887                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
888
889 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
890
891 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
892                 (!IS_PLAYER(x, y) &&                                    \
893                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
894
895 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
896         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
897
898 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
899 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
900
901 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
902 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
903 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
904 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
905
906 /* game button identifiers */
907 #define GAME_CTRL_ID_STOP               0
908 #define GAME_CTRL_ID_PAUSE              1
909 #define GAME_CTRL_ID_PLAY               2
910 #define SOUND_CTRL_ID_MUSIC             3
911 #define SOUND_CTRL_ID_LOOPS             4
912 #define SOUND_CTRL_ID_SIMPLE            5
913
914 #define NUM_GAME_BUTTONS                6
915
916
917 /* forward declaration for internal use */
918
919 static void CreateField(int, int, int);
920
921 static void ResetGfxAnimation(int, int);
922
923 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
924 static void AdvanceFrameAndPlayerCounters(int);
925
926 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
927 static boolean MovePlayer(struct PlayerInfo *, int, int);
928 static void ScrollPlayer(struct PlayerInfo *, int);
929 static void ScrollScreen(struct PlayerInfo *, int);
930
931 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
932
933 static void InitBeltMovement(void);
934 static void CloseAllOpenTimegates(void);
935 static void CheckGravityMovement(struct PlayerInfo *);
936 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
937 static void KillPlayerUnlessEnemyProtected(int, int);
938 static void KillPlayerUnlessExplosionProtected(int, int);
939
940 static void TestIfPlayerTouchesCustomElement(int, int);
941 static void TestIfElementTouchesCustomElement(int, int);
942 static void TestIfElementHitsCustomElement(int, int, int);
943 #if 0
944 static void TestIfElementSmashesCustomElement(int, int, int);
945 #endif
946
947 static void HandleElementChange(int, int, int);
948 static void ExecuteCustomElementAction(int, int, int, int);
949 static boolean ChangeElement(int, int, int, int);
950
951 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
952 #define CheckTriggeredElementChange(x, y, e, ev)                        \
953         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
954 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
955         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
956 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
957         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
958 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
959         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
960
961 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
962 #define CheckElementChange(x, y, e, te, ev)                             \
963         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
964 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
965         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
966 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
967         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
968
969 static void PlayLevelSound(int, int, int);
970 static void PlayLevelSoundNearest(int, int, int);
971 static void PlayLevelSoundAction(int, int, int);
972 static void PlayLevelSoundElementAction(int, int, int, int);
973 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
974 static void PlayLevelSoundActionIfLoop(int, int, int);
975 static void StopLevelSoundActionIfLoop(int, int, int);
976 static void PlayLevelMusic();
977
978 static void MapGameButtons();
979 static void HandleGameButtons(struct GadgetInfo *);
980
981 int AmoebeNachbarNr(int, int);
982 void AmoebeUmwandeln(int, int);
983 void ContinueMoving(int, int);
984 void Bang(int, int);
985 void InitMovDir(int, int);
986 void InitAmoebaNr(int, int);
987 int NewHiScore(void);
988
989 void TestIfGoodThingHitsBadThing(int, int, int);
990 void TestIfBadThingHitsGoodThing(int, int, int);
991 void TestIfPlayerTouchesBadThing(int, int);
992 void TestIfPlayerRunsIntoBadThing(int, int, int);
993 void TestIfBadThingTouchesPlayer(int, int);
994 void TestIfBadThingRunsIntoPlayer(int, int, int);
995 void TestIfFriendTouchesBadThing(int, int);
996 void TestIfBadThingTouchesFriend(int, int);
997 void TestIfBadThingTouchesOtherBadThing(int, int);
998
999 void KillPlayer(struct PlayerInfo *);
1000 void BuryPlayer(struct PlayerInfo *);
1001 void RemovePlayer(struct PlayerInfo *);
1002
1003 boolean SnapField(struct PlayerInfo *, int, int);
1004 boolean DropElement(struct PlayerInfo *);
1005
1006 static int getInvisibleActiveFromInvisibleElement(int);
1007 static int getInvisibleFromInvisibleActiveElement(int);
1008
1009 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1010
1011 /* for detection of endless loops, caused by custom element programming */
1012 /* (using maximal playfield width x 10 is just a rough approximation) */
1013 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1014
1015 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1016 {                                                                       \
1017   if (recursion_loop_detected)                                          \
1018     return (rc);                                                        \
1019                                                                         \
1020   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1021   {                                                                     \
1022     recursion_loop_detected = TRUE;                                     \
1023     recursion_loop_element = (e);                                       \
1024   }                                                                     \
1025                                                                         \
1026   recursion_loop_depth++;                                               \
1027 }
1028
1029 #define RECURSION_LOOP_DETECTION_END()                                  \
1030 {                                                                       \
1031   recursion_loop_depth--;                                               \
1032 }
1033
1034 static int recursion_loop_depth;
1035 static boolean recursion_loop_detected;
1036 static boolean recursion_loop_element;
1037
1038
1039 /* ------------------------------------------------------------------------- */
1040 /* definition of elements that automatically change to other elements after  */
1041 /* a specified time, eventually calling a function when changing             */
1042 /* ------------------------------------------------------------------------- */
1043
1044 /* forward declaration for changer functions */
1045 static void InitBuggyBase(int, int);
1046 static void WarnBuggyBase(int, int);
1047
1048 static void InitTrap(int, int);
1049 static void ActivateTrap(int, int);
1050 static void ChangeActiveTrap(int, int);
1051
1052 static void InitRobotWheel(int, int);
1053 static void RunRobotWheel(int, int);
1054 static void StopRobotWheel(int, int);
1055
1056 static void InitTimegateWheel(int, int);
1057 static void RunTimegateWheel(int, int);
1058
1059 static void InitMagicBallDelay(int, int);
1060 static void ActivateMagicBall(int, int);
1061
1062 struct ChangingElementInfo
1063 {
1064   int element;
1065   int target_element;
1066   int change_delay;
1067   void (*pre_change_function)(int x, int y);
1068   void (*change_function)(int x, int y);
1069   void (*post_change_function)(int x, int y);
1070 };
1071
1072 static struct ChangingElementInfo change_delay_list[] =
1073 {
1074   {
1075     EL_NUT_BREAKING,
1076     EL_EMERALD,
1077     6,
1078     NULL,
1079     NULL,
1080     NULL
1081   },
1082   {
1083     EL_PEARL_BREAKING,
1084     EL_EMPTY,
1085     8,
1086     NULL,
1087     NULL,
1088     NULL
1089   },
1090   {
1091     EL_EXIT_OPENING,
1092     EL_EXIT_OPEN,
1093     29,
1094     NULL,
1095     NULL,
1096     NULL
1097   },
1098   {
1099     EL_EXIT_CLOSING,
1100     EL_EXIT_CLOSED,
1101     29,
1102     NULL,
1103     NULL,
1104     NULL
1105   },
1106   {
1107     EL_STEEL_EXIT_OPENING,
1108     EL_STEEL_EXIT_OPEN,
1109     29,
1110     NULL,
1111     NULL,
1112     NULL
1113   },
1114   {
1115     EL_STEEL_EXIT_CLOSING,
1116     EL_STEEL_EXIT_CLOSED,
1117     29,
1118     NULL,
1119     NULL,
1120     NULL
1121   },
1122   {
1123     EL_EM_EXIT_OPENING,
1124     EL_EM_EXIT_OPEN,
1125     29,
1126     NULL,
1127     NULL,
1128     NULL
1129   },
1130   {
1131     EL_EM_EXIT_CLOSING,
1132 #if 1
1133     EL_EMPTY,
1134 #else
1135     EL_EM_EXIT_CLOSED,
1136 #endif
1137     29,
1138     NULL,
1139     NULL,
1140     NULL
1141   },
1142   {
1143     EL_EM_STEEL_EXIT_OPENING,
1144     EL_EM_STEEL_EXIT_OPEN,
1145     29,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_EM_STEEL_EXIT_CLOSING,
1152 #if 1
1153     EL_STEELWALL,
1154 #else
1155     EL_EM_STEEL_EXIT_CLOSED,
1156 #endif
1157     29,
1158     NULL,
1159     NULL,
1160     NULL
1161   },
1162   {
1163     EL_SP_EXIT_OPENING,
1164     EL_SP_EXIT_OPEN,
1165     29,
1166     NULL,
1167     NULL,
1168     NULL
1169   },
1170   {
1171     EL_SP_EXIT_CLOSING,
1172     EL_SP_EXIT_CLOSED,
1173     29,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_SWITCHGATE_OPENING,
1180     EL_SWITCHGATE_OPEN,
1181     29,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_SWITCHGATE_CLOSING,
1188     EL_SWITCHGATE_CLOSED,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_TIMEGATE_OPENING,
1196     EL_TIMEGATE_OPEN,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_TIMEGATE_CLOSING,
1204     EL_TIMEGATE_CLOSED,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210
1211   {
1212     EL_ACID_SPLASH_LEFT,
1213     EL_EMPTY,
1214     8,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_ACID_SPLASH_RIGHT,
1221     EL_EMPTY,
1222     8,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_SP_BUGGY_BASE,
1229     EL_SP_BUGGY_BASE_ACTIVATING,
1230     0,
1231     InitBuggyBase,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_SP_BUGGY_BASE_ACTIVATING,
1237     EL_SP_BUGGY_BASE_ACTIVE,
1238     0,
1239     InitBuggyBase,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_SP_BUGGY_BASE_ACTIVE,
1245     EL_SP_BUGGY_BASE,
1246     0,
1247     InitBuggyBase,
1248     WarnBuggyBase,
1249     NULL
1250   },
1251   {
1252     EL_TRAP,
1253     EL_TRAP_ACTIVE,
1254     0,
1255     InitTrap,
1256     NULL,
1257     ActivateTrap
1258   },
1259   {
1260     EL_TRAP_ACTIVE,
1261     EL_TRAP,
1262     31,
1263     NULL,
1264     ChangeActiveTrap,
1265     NULL
1266   },
1267   {
1268     EL_ROBOT_WHEEL_ACTIVE,
1269     EL_ROBOT_WHEEL,
1270     0,
1271     InitRobotWheel,
1272     RunRobotWheel,
1273     StopRobotWheel
1274   },
1275   {
1276     EL_TIMEGATE_SWITCH_ACTIVE,
1277     EL_TIMEGATE_SWITCH,
1278     0,
1279     InitTimegateWheel,
1280     RunTimegateWheel,
1281     NULL
1282   },
1283   {
1284     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1285     EL_DC_TIMEGATE_SWITCH,
1286     0,
1287     InitTimegateWheel,
1288     RunTimegateWheel,
1289     NULL
1290   },
1291   {
1292     EL_EMC_MAGIC_BALL_ACTIVE,
1293     EL_EMC_MAGIC_BALL_ACTIVE,
1294     0,
1295     InitMagicBallDelay,
1296     NULL,
1297     ActivateMagicBall
1298   },
1299   {
1300     EL_EMC_SPRING_BUMPER_ACTIVE,
1301     EL_EMC_SPRING_BUMPER,
1302     8,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_DIAGONAL_SHRINKING,
1309     EL_UNDEFINED,
1310     0,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_DIAGONAL_GROWING,
1317     EL_UNDEFINED,
1318     0,
1319     NULL,
1320     NULL,
1321     NULL,
1322   },
1323
1324   {
1325     EL_UNDEFINED,
1326     EL_UNDEFINED,
1327     -1,
1328     NULL,
1329     NULL,
1330     NULL
1331   }
1332 };
1333
1334 struct
1335 {
1336   int element;
1337   int push_delay_fixed, push_delay_random;
1338 }
1339 push_delay_list[] =
1340 {
1341   { EL_SPRING,                  0, 0 },
1342   { EL_BALLOON,                 0, 0 },
1343
1344   { EL_SOKOBAN_OBJECT,          2, 0 },
1345   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1346   { EL_SATELLITE,               2, 0 },
1347   { EL_SP_DISK_YELLOW,          2, 0 },
1348
1349   { EL_UNDEFINED,               0, 0 },
1350 };
1351
1352 struct
1353 {
1354   int element;
1355   int move_stepsize;
1356 }
1357 move_stepsize_list[] =
1358 {
1359   { EL_AMOEBA_DROP,             2 },
1360   { EL_AMOEBA_DROPPING,         2 },
1361   { EL_QUICKSAND_FILLING,       1 },
1362   { EL_QUICKSAND_EMPTYING,      1 },
1363   { EL_QUICKSAND_FAST_FILLING,  2 },
1364   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1365   { EL_MAGIC_WALL_FILLING,      2 },
1366   { EL_MAGIC_WALL_EMPTYING,     2 },
1367   { EL_BD_MAGIC_WALL_FILLING,   2 },
1368   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1369   { EL_DC_MAGIC_WALL_FILLING,   2 },
1370   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1371
1372   { EL_UNDEFINED,               0 },
1373 };
1374
1375 struct
1376 {
1377   int element;
1378   int count;
1379 }
1380 collect_count_list[] =
1381 {
1382   { EL_EMERALD,                 1 },
1383   { EL_BD_DIAMOND,              1 },
1384   { EL_EMERALD_YELLOW,          1 },
1385   { EL_EMERALD_RED,             1 },
1386   { EL_EMERALD_PURPLE,          1 },
1387   { EL_DIAMOND,                 3 },
1388   { EL_SP_INFOTRON,             1 },
1389   { EL_PEARL,                   5 },
1390   { EL_CRYSTAL,                 8 },
1391
1392   { EL_UNDEFINED,               0 },
1393 };
1394
1395 struct
1396 {
1397   int element;
1398   int direction;
1399 }
1400 access_direction_list[] =
1401 {
1402   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1403   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1404   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1405   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1406   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1407   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1408   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1409   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1410   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1411   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1412   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1413
1414   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1415   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1416   { EL_SP_PORT_UP,                                                   MV_DOWN },
1417   { EL_SP_PORT_DOWN,                                         MV_UP           },
1418   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1419   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1420   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1421   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1422   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1423   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1424   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1425   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1426   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1427   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1428   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1429   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1430   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1431   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1432   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1433
1434   { EL_UNDEFINED,                       MV_NONE                              }
1435 };
1436
1437 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1438
1439 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1440 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1441 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1442                                  IS_JUST_CHANGING(x, y))
1443
1444 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1445
1446 /* static variables for playfield scan mode (scanning forward or backward) */
1447 static int playfield_scan_start_x = 0;
1448 static int playfield_scan_start_y = 0;
1449 static int playfield_scan_delta_x = 1;
1450 static int playfield_scan_delta_y = 1;
1451
1452 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1453                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1454                                      (y) += playfield_scan_delta_y)     \
1455                                 for ((x) = playfield_scan_start_x;      \
1456                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1457                                      (x) += playfield_scan_delta_x)
1458
1459 #ifdef DEBUG
1460 void DEBUG_SetMaximumDynamite()
1461 {
1462   int i;
1463
1464   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1465     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1466       local_player->inventory_element[local_player->inventory_size++] =
1467         EL_DYNAMITE;
1468 }
1469 #endif
1470
1471 static void InitPlayfieldScanModeVars()
1472 {
1473   if (game.use_reverse_scan_direction)
1474   {
1475     playfield_scan_start_x = lev_fieldx - 1;
1476     playfield_scan_start_y = lev_fieldy - 1;
1477
1478     playfield_scan_delta_x = -1;
1479     playfield_scan_delta_y = -1;
1480   }
1481   else
1482   {
1483     playfield_scan_start_x = 0;
1484     playfield_scan_start_y = 0;
1485
1486     playfield_scan_delta_x = 1;
1487     playfield_scan_delta_y = 1;
1488   }
1489 }
1490
1491 static void InitPlayfieldScanMode(int mode)
1492 {
1493   game.use_reverse_scan_direction =
1494     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1495
1496   InitPlayfieldScanModeVars();
1497 }
1498
1499 static int get_move_delay_from_stepsize(int move_stepsize)
1500 {
1501   move_stepsize =
1502     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1503
1504   /* make sure that stepsize value is always a power of 2 */
1505   move_stepsize = (1 << log_2(move_stepsize));
1506
1507   return TILEX / move_stepsize;
1508 }
1509
1510 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1511                                boolean init_game)
1512 {
1513   int player_nr = player->index_nr;
1514   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1515   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1516
1517   /* do no immediately change move delay -- the player might just be moving */
1518   player->move_delay_value_next = move_delay;
1519
1520   /* information if player can move must be set separately */
1521   player->cannot_move = cannot_move;
1522
1523   if (init_game)
1524   {
1525     player->move_delay       = game.initial_move_delay[player_nr];
1526     player->move_delay_value = game.initial_move_delay_value[player_nr];
1527
1528     player->move_delay_value_next = -1;
1529
1530     player->move_delay_reset_counter = 0;
1531   }
1532 }
1533
1534 void GetPlayerConfig()
1535 {
1536   GameFrameDelay = setup.game_frame_delay;
1537
1538   if (!audio.sound_available)
1539     setup.sound_simple = FALSE;
1540
1541   if (!audio.loops_available)
1542     setup.sound_loops = FALSE;
1543
1544   if (!audio.music_available)
1545     setup.sound_music = FALSE;
1546
1547   if (!video.fullscreen_available)
1548     setup.fullscreen = FALSE;
1549
1550   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1551
1552   SetAudioMode(setup.sound);
1553   InitJoysticks();
1554 }
1555
1556 int GetElementFromGroupElement(int element)
1557 {
1558   if (IS_GROUP_ELEMENT(element))
1559   {
1560     struct ElementGroupInfo *group = element_info[element].group;
1561     int last_anim_random_frame = gfx.anim_random_frame;
1562     int element_pos;
1563
1564     if (group->choice_mode == ANIM_RANDOM)
1565       gfx.anim_random_frame = RND(group->num_elements_resolved);
1566
1567     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1568                                     group->choice_mode, 0,
1569                                     group->choice_pos);
1570
1571     if (group->choice_mode == ANIM_RANDOM)
1572       gfx.anim_random_frame = last_anim_random_frame;
1573
1574     group->choice_pos++;
1575
1576     element = group->element_resolved[element_pos];
1577   }
1578
1579   return element;
1580 }
1581
1582 static void InitPlayerField(int x, int y, int element, boolean init_game)
1583 {
1584   if (element == EL_SP_MURPHY)
1585   {
1586     if (init_game)
1587     {
1588       if (stored_player[0].present)
1589       {
1590         Feld[x][y] = EL_SP_MURPHY_CLONE;
1591
1592         return;
1593       }
1594       else
1595       {
1596         stored_player[0].use_murphy = TRUE;
1597
1598         if (!level.use_artwork_element[0])
1599           stored_player[0].artwork_element = EL_SP_MURPHY;
1600       }
1601
1602       Feld[x][y] = EL_PLAYER_1;
1603     }
1604   }
1605
1606   if (init_game)
1607   {
1608     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1609     int jx = player->jx, jy = player->jy;
1610
1611     player->present = TRUE;
1612
1613     player->block_last_field = (element == EL_SP_MURPHY ?
1614                                 level.sp_block_last_field :
1615                                 level.block_last_field);
1616
1617     /* ---------- initialize player's last field block delay --------------- */
1618
1619     /* always start with reliable default value (no adjustment needed) */
1620     player->block_delay_adjustment = 0;
1621
1622     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1623     if (player->block_last_field && element == EL_SP_MURPHY)
1624       player->block_delay_adjustment = 1;
1625
1626     /* special case 2: in game engines before 3.1.1, blocking was different */
1627     if (game.use_block_last_field_bug)
1628       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1629
1630     if (!options.network || player->connected)
1631     {
1632       player->active = TRUE;
1633
1634       /* remove potentially duplicate players */
1635       if (StorePlayer[jx][jy] == Feld[x][y])
1636         StorePlayer[jx][jy] = 0;
1637
1638       StorePlayer[x][y] = Feld[x][y];
1639
1640       if (options.debug)
1641       {
1642         printf("Player %d activated.\n", player->element_nr);
1643         printf("[Local player is %d and currently %s.]\n",
1644                local_player->element_nr,
1645                local_player->active ? "active" : "not active");
1646       }
1647     }
1648
1649     Feld[x][y] = EL_EMPTY;
1650
1651     player->jx = player->last_jx = x;
1652     player->jy = player->last_jy = y;
1653   }
1654 }
1655
1656 static void InitField(int x, int y, boolean init_game)
1657 {
1658   int element = Feld[x][y];
1659
1660   switch (element)
1661   {
1662     case EL_SP_MURPHY:
1663     case EL_PLAYER_1:
1664     case EL_PLAYER_2:
1665     case EL_PLAYER_3:
1666     case EL_PLAYER_4:
1667       InitPlayerField(x, y, element, init_game);
1668       break;
1669
1670     case EL_SOKOBAN_FIELD_PLAYER:
1671       element = Feld[x][y] = EL_PLAYER_1;
1672       InitField(x, y, init_game);
1673
1674       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1675       InitField(x, y, init_game);
1676       break;
1677
1678     case EL_SOKOBAN_FIELD_EMPTY:
1679       local_player->sokobanfields_still_needed++;
1680       break;
1681
1682     case EL_STONEBLOCK:
1683       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1684         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1685       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1686         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1687       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1688         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1689       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1690         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1691       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1692         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1693       break;
1694
1695     case EL_BUG:
1696     case EL_BUG_RIGHT:
1697     case EL_BUG_UP:
1698     case EL_BUG_LEFT:
1699     case EL_BUG_DOWN:
1700     case EL_SPACESHIP:
1701     case EL_SPACESHIP_RIGHT:
1702     case EL_SPACESHIP_UP:
1703     case EL_SPACESHIP_LEFT:
1704     case EL_SPACESHIP_DOWN:
1705     case EL_BD_BUTTERFLY:
1706     case EL_BD_BUTTERFLY_RIGHT:
1707     case EL_BD_BUTTERFLY_UP:
1708     case EL_BD_BUTTERFLY_LEFT:
1709     case EL_BD_BUTTERFLY_DOWN:
1710     case EL_BD_FIREFLY:
1711     case EL_BD_FIREFLY_RIGHT:
1712     case EL_BD_FIREFLY_UP:
1713     case EL_BD_FIREFLY_LEFT:
1714     case EL_BD_FIREFLY_DOWN:
1715     case EL_PACMAN_RIGHT:
1716     case EL_PACMAN_UP:
1717     case EL_PACMAN_LEFT:
1718     case EL_PACMAN_DOWN:
1719     case EL_YAMYAM:
1720     case EL_YAMYAM_LEFT:
1721     case EL_YAMYAM_RIGHT:
1722     case EL_YAMYAM_UP:
1723     case EL_YAMYAM_DOWN:
1724     case EL_DARK_YAMYAM:
1725     case EL_ROBOT:
1726     case EL_PACMAN:
1727     case EL_SP_SNIKSNAK:
1728     case EL_SP_ELECTRON:
1729     case EL_MOLE:
1730     case EL_MOLE_LEFT:
1731     case EL_MOLE_RIGHT:
1732     case EL_MOLE_UP:
1733     case EL_MOLE_DOWN:
1734       InitMovDir(x, y);
1735       break;
1736
1737     case EL_AMOEBA_FULL:
1738     case EL_BD_AMOEBA:
1739       InitAmoebaNr(x, y);
1740       break;
1741
1742     case EL_AMOEBA_DROP:
1743       if (y == lev_fieldy - 1)
1744       {
1745         Feld[x][y] = EL_AMOEBA_GROWING;
1746         Store[x][y] = EL_AMOEBA_WET;
1747       }
1748       break;
1749
1750     case EL_DYNAMITE_ACTIVE:
1751     case EL_SP_DISK_RED_ACTIVE:
1752     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1753     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1754     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1755     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1756       MovDelay[x][y] = 96;
1757       break;
1758
1759     case EL_EM_DYNAMITE_ACTIVE:
1760       MovDelay[x][y] = 32;
1761       break;
1762
1763     case EL_LAMP:
1764       local_player->lights_still_needed++;
1765       break;
1766
1767     case EL_PENGUIN:
1768       local_player->friends_still_needed++;
1769       break;
1770
1771     case EL_PIG:
1772     case EL_DRAGON:
1773       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1774       break;
1775
1776     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1777     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1778     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1779     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1780     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1781     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1782     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1783     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1784     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1785     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1786     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1787     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1788       if (init_game)
1789       {
1790         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1791         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1792         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1793
1794         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1795         {
1796           game.belt_dir[belt_nr] = belt_dir;
1797           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1798         }
1799         else    /* more than one switch -- set it like the first switch */
1800         {
1801           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1802         }
1803       }
1804       break;
1805
1806 #if !USE_BOTH_SWITCHGATE_SWITCHES
1807     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1808       if (init_game)
1809         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1810       break;
1811
1812     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1813       if (init_game)
1814         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1815       break;
1816 #endif
1817
1818     case EL_LIGHT_SWITCH_ACTIVE:
1819       if (init_game)
1820         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1821       break;
1822
1823     case EL_INVISIBLE_STEELWALL:
1824     case EL_INVISIBLE_WALL:
1825     case EL_INVISIBLE_SAND:
1826       if (game.light_time_left > 0 ||
1827           game.lenses_time_left > 0)
1828         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1829       break;
1830
1831     case EL_EMC_MAGIC_BALL:
1832       if (game.ball_state)
1833         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1834       break;
1835
1836     case EL_EMC_MAGIC_BALL_SWITCH:
1837       if (game.ball_state)
1838         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1839       break;
1840
1841     default:
1842       if (IS_CUSTOM_ELEMENT(element))
1843       {
1844         if (CAN_MOVE(element))
1845           InitMovDir(x, y);
1846
1847 #if USE_NEW_CUSTOM_VALUE
1848         if (!element_info[element].use_last_ce_value || init_game)
1849           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1850 #endif
1851       }
1852       else if (IS_GROUP_ELEMENT(element))
1853       {
1854         Feld[x][y] = GetElementFromGroupElement(element);
1855
1856         InitField(x, y, init_game);
1857       }
1858
1859       break;
1860   }
1861
1862   if (!init_game)
1863     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1864 }
1865
1866 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1867 {
1868   InitField(x, y, init_game);
1869
1870   /* not needed to call InitMovDir() -- already done by InitField()! */
1871   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1872       CAN_MOVE(Feld[x][y]))
1873     InitMovDir(x, y);
1874 }
1875
1876 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1877 {
1878   int old_element = Feld[x][y];
1879
1880   InitField(x, y, init_game);
1881
1882   /* not needed to call InitMovDir() -- already done by InitField()! */
1883   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1884       CAN_MOVE(old_element) &&
1885       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1886     InitMovDir(x, y);
1887
1888   /* this case is in fact a combination of not less than three bugs:
1889      first, it calls InitMovDir() for elements that can move, although this is
1890      already done by InitField(); then, it checks the element that was at this
1891      field _before_ the call to InitField() (which can change it); lastly, it
1892      was not called for "mole with direction" elements, which were treated as
1893      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1894   */
1895 }
1896
1897 #if 1
1898
1899 static int get_key_element_from_nr(int key_nr)
1900 {
1901   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1902                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1903                           EL_EM_KEY_1 : EL_KEY_1);
1904
1905   return key_base_element + key_nr;
1906 }
1907
1908 static int get_next_dropped_element(struct PlayerInfo *player)
1909 {
1910   return (player->inventory_size > 0 ?
1911           player->inventory_element[player->inventory_size - 1] :
1912           player->inventory_infinite_element != EL_UNDEFINED ?
1913           player->inventory_infinite_element :
1914           player->dynabombs_left > 0 ?
1915           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1916           EL_UNDEFINED);
1917 }
1918
1919 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1920 {
1921   /* pos >= 0: get element from bottom of the stack;
1922      pos <  0: get element from top of the stack */
1923
1924   if (pos < 0)
1925   {
1926     int min_inventory_size = -pos;
1927     int inventory_pos = player->inventory_size - min_inventory_size;
1928     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1929
1930     return (player->inventory_size >= min_inventory_size ?
1931             player->inventory_element[inventory_pos] :
1932             player->inventory_infinite_element != EL_UNDEFINED ?
1933             player->inventory_infinite_element :
1934             player->dynabombs_left >= min_dynabombs_left ?
1935             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1936             EL_UNDEFINED);
1937   }
1938   else
1939   {
1940     int min_dynabombs_left = pos + 1;
1941     int min_inventory_size = pos + 1 - player->dynabombs_left;
1942     int inventory_pos = pos - player->dynabombs_left;
1943
1944     return (player->inventory_infinite_element != EL_UNDEFINED ?
1945             player->inventory_infinite_element :
1946             player->dynabombs_left >= min_dynabombs_left ?
1947             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1948             player->inventory_size >= min_inventory_size ?
1949             player->inventory_element[inventory_pos] :
1950             EL_UNDEFINED);
1951   }
1952 }
1953
1954 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
1955 {
1956   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
1957   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
1958   int compare_result;
1959
1960   if (gpo1->sort_priority != gpo2->sort_priority)
1961     compare_result = gpo1->sort_priority - gpo2->sort_priority;
1962   else
1963     compare_result = gpo1->nr - gpo2->nr;
1964
1965   return compare_result;
1966 }
1967
1968 void InitGameControlValues()
1969 {
1970   int i;
1971
1972   for (i = 0; game_panel_controls[i].nr != -1; i++)
1973   {
1974     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1975     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
1976     struct TextPosInfo *pos = gpc->pos;
1977     int nr = gpc->nr;
1978     int type = gpc->type;
1979
1980     if (nr != i)
1981     {
1982       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1983       Error(ERR_EXIT, "this should not happen -- please debug");
1984     }
1985
1986     /* force update of game controls after initialization */
1987     gpc->value = gpc->last_value = -1;
1988     gpc->frame = gpc->last_frame = -1;
1989     gpc->gfx_frame = -1;
1990
1991     /* determine panel value width for later calculation of alignment */
1992     if (type == TYPE_INTEGER || type == TYPE_STRING)
1993     {
1994       pos->width = pos->size * getFontWidth(pos->font);
1995       pos->height = getFontHeight(pos->font);
1996     }
1997     else if (type == TYPE_ELEMENT)
1998     {
1999       pos->width = pos->size;
2000       pos->height = pos->size;
2001     }
2002
2003     /* fill structure for game panel draw order */
2004     gpo->nr = gpc->nr;
2005     gpo->sort_priority = pos->sort_priority;
2006   }
2007
2008   /* sort game panel controls according to sort_priority and control number */
2009   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2010         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2011 }
2012
2013 void UpdateGameControlValues()
2014 {
2015   int i, k;
2016   int time = (level.time == 0 ? TimePlayed : TimeLeft);
2017   int score = (local_player->LevelSolved ? local_player->score_final :
2018                local_player->score);
2019   int exit_closed = (local_player->gems_still_needed > 0 ||
2020                      local_player->sokobanfields_still_needed > 0 ||
2021                      local_player->lights_still_needed > 0);
2022
2023   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2024   game_panel_controls[GAME_PANEL_GEMS].value =
2025     local_player->gems_still_needed;
2026
2027   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2028   for (i = 0; i < MAX_NUM_KEYS; i++)
2029     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2030   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2031   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2032
2033   if (game.centered_player_nr == -1)
2034   {
2035     for (i = 0; i < MAX_PLAYERS; i++)
2036     {
2037       for (k = 0; k < MAX_NUM_KEYS; k++)
2038         if (stored_player[i].key[k])
2039           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2040             get_key_element_from_nr(k);
2041
2042       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2043         stored_player[i].inventory_size;
2044
2045       if (stored_player[i].num_white_keys > 0)
2046         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2047           EL_DC_KEY_WHITE;
2048
2049       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2050         stored_player[i].num_white_keys;
2051     }
2052   }
2053   else
2054   {
2055     int player_nr = game.centered_player_nr;
2056
2057     for (k = 0; k < MAX_NUM_KEYS; k++)
2058       if (stored_player[player_nr].key[k])
2059         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2060           get_key_element_from_nr(k);
2061
2062     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2063       stored_player[player_nr].inventory_size;
2064
2065     if (stored_player[player_nr].num_white_keys > 0)
2066       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2067
2068     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2069       stored_player[player_nr].num_white_keys;
2070   }
2071
2072   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2073   {
2074     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2075       get_inventory_element_from_pos(local_player, i);
2076     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2077       get_inventory_element_from_pos(local_player, -i - 1);
2078   }
2079
2080   game_panel_controls[GAME_PANEL_SCORE].value = score;
2081
2082   game_panel_controls[GAME_PANEL_TIME].value = time;
2083
2084   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2085   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2086   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2087
2088   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2089     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2090      EL_EMPTY);
2091   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2092     local_player->shield_normal_time_left;
2093   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2094     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2095      EL_EMPTY);
2096   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2097     local_player->shield_deadly_time_left;
2098
2099   game_panel_controls[GAME_PANEL_EXIT].value =
2100     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2101
2102   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2103     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2104   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2105     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2106      EL_EMC_MAGIC_BALL_SWITCH);
2107
2108   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2109     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2110   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2111     game.light_time_left;
2112
2113   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2114     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2115   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2116     game.timegate_time_left;
2117
2118   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2119     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2120
2121   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2122     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2123   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2124     game.lenses_time_left;
2125
2126   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2127     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2128   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2129     game.magnify_time_left;
2130
2131   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2132     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2133      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2134      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2135      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2136      EL_BALLOON_SWITCH_NONE);
2137
2138   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2139     local_player->dynabomb_count;
2140   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2141     local_player->dynabomb_size;
2142   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2143     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2144
2145   game_panel_controls[GAME_PANEL_PENGUINS].value =
2146     local_player->friends_still_needed;
2147
2148   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2149     local_player->sokobanfields_still_needed;
2150   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2151     local_player->sokobanfields_still_needed;
2152
2153   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2154     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2155
2156   for (i = 0; i < NUM_BELTS; i++)
2157   {
2158     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2159       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2160        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2161     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2162       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2163   }
2164
2165   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2166     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2167   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2168     game.magic_wall_time_left;
2169
2170 #if USE_PLAYER_GRAVITY
2171   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2172     local_player->gravity;
2173 #else
2174   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2175 #endif
2176
2177   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2178     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2179       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2180        game.panel.element[i].id : EL_UNDEFINED);
2181
2182   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2183     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2184       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2185        element_info[game.panel.ce_score[i].id].collect_score : 0);
2186
2187   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2188     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2189       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2190        element_info[game.panel.ce_score_element[i].id].collect_score :
2191        EL_UNDEFINED);
2192
2193   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2194   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2195   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2196
2197   for (i = 0; game_panel_controls[i].nr != -1; i++)
2198   {
2199     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2200
2201     if (gpc->type == TYPE_ELEMENT)
2202     {
2203       int last_anim_random_frame = gfx.anim_random_frame;
2204       int element = gpc->value;
2205       int graphic = el2panelimg(element);
2206
2207       if (gpc->value != gpc->last_value)
2208       {
2209         gpc->gfx_frame = 0;
2210         gpc->gfx_random = INIT_GFX_RANDOM();
2211       }
2212       else
2213       {
2214         gpc->gfx_frame++;
2215
2216         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2217             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2218           gpc->gfx_random = INIT_GFX_RANDOM();
2219       }
2220
2221       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2222         gfx.anim_random_frame = gpc->gfx_random;
2223
2224       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2225         gpc->gfx_frame = element_info[element].collect_score;
2226
2227       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2228                                             gpc->gfx_frame);
2229
2230       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2231         gfx.anim_random_frame = last_anim_random_frame;
2232     }
2233   }
2234 }
2235
2236 void DisplayGameControlValues()
2237 {
2238   boolean redraw_panel = FALSE;
2239   int i;
2240
2241   for (i = 0; game_panel_controls[i].nr != -1; i++)
2242   {
2243     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2244
2245     if (PANEL_DEACTIVATED(gpc->pos))
2246       continue;
2247
2248     if (gpc->value == gpc->last_value &&
2249         gpc->frame == gpc->last_frame)
2250       continue;
2251
2252     redraw_panel = TRUE;
2253   }
2254
2255   if (!redraw_panel)
2256     return;
2257
2258   /* copy default game door content to main double buffer */
2259   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2260              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2261
2262   /* redraw game control buttons */
2263 #if 1
2264   RedrawGameButtons();
2265 #else
2266   UnmapGameButtons();
2267   MapGameButtons();
2268 #endif
2269
2270   game_status = GAME_MODE_PSEUDO_PANEL;
2271
2272 #if 1
2273   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2274 #else
2275   for (i = 0; game_panel_controls[i].nr != -1; i++)
2276 #endif
2277   {
2278 #if 1
2279     int nr = game_panel_order[i].nr;
2280     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2281 #else
2282     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2283     int nr = gpc->nr;
2284 #endif
2285     struct TextPosInfo *pos = gpc->pos;
2286     int type = gpc->type;
2287     int value = gpc->value;
2288     int frame = gpc->frame;
2289 #if 0
2290     int last_value = gpc->last_value;
2291     int last_frame = gpc->last_frame;
2292 #endif
2293     int size = pos->size;
2294     int font = pos->font;
2295     boolean draw_masked = pos->draw_masked;
2296     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2297
2298     if (PANEL_DEACTIVATED(pos))
2299       continue;
2300
2301 #if 0
2302     if (value == last_value && frame == last_frame)
2303       continue;
2304 #endif
2305
2306     gpc->last_value = value;
2307     gpc->last_frame = frame;
2308
2309 #if 0
2310     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2311 #endif
2312
2313     if (type == TYPE_INTEGER)
2314     {
2315       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2316           nr == GAME_PANEL_TIME)
2317       {
2318         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2319
2320         if (use_dynamic_size)           /* use dynamic number of digits */
2321         {
2322           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2323           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2324           int size2 = size1 + 1;
2325           int font1 = pos->font;
2326           int font2 = pos->font_alt;
2327
2328           size = (value < value_change ? size1 : size2);
2329           font = (value < value_change ? font1 : font2);
2330
2331 #if 0
2332           /* clear background if value just changed its size (dynamic digits) */
2333           if ((last_value < value_change) != (value < value_change))
2334           {
2335             int width1 = size1 * getFontWidth(font1);
2336             int width2 = size2 * getFontWidth(font2);
2337             int max_width = MAX(width1, width2);
2338             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2339
2340             pos->width = max_width;
2341
2342             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2343                                        max_width, max_height);
2344           }
2345 #endif
2346         }
2347       }
2348
2349 #if 1
2350       /* correct text size if "digits" is zero or less */
2351       if (size <= 0)
2352         size = strlen(int2str(value, size));
2353
2354       /* dynamically correct text alignment */
2355       pos->width = size * getFontWidth(font);
2356 #endif
2357
2358       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2359                   int2str(value, size), font, mask_mode);
2360     }
2361     else if (type == TYPE_ELEMENT)
2362     {
2363       int element, graphic;
2364       Bitmap *src_bitmap;
2365       int src_x, src_y;
2366       int width, height;
2367       int dst_x = PANEL_XPOS(pos);
2368       int dst_y = PANEL_YPOS(pos);
2369
2370 #if 1
2371       if (value != EL_UNDEFINED && value != EL_EMPTY)
2372       {
2373         element = value;
2374         graphic = el2panelimg(value);
2375
2376         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2377                               &src_x, &src_y);
2378
2379         width  = graphic_info[graphic].width  * size / TILESIZE;
2380         height = graphic_info[graphic].height * size / TILESIZE;
2381
2382         if (draw_masked)
2383         {
2384           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2385                         dst_x - src_x, dst_y - src_y);
2386           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2387                            dst_x, dst_y);
2388         }
2389         else
2390         {
2391           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2392                      dst_x, dst_y);
2393         }
2394       }
2395 #else
2396       if (value == EL_UNDEFINED || value == EL_EMPTY)
2397       {
2398         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2399         graphic = el2panelimg(element);
2400
2401         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2402         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2403         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2404       }
2405       else
2406       {
2407         element = value;
2408         graphic = el2panelimg(value);
2409
2410         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2411       }
2412
2413       width  = graphic_info[graphic].width  * size / TILESIZE;
2414       height = graphic_info[graphic].height * size / TILESIZE;
2415
2416       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2417 #endif
2418     }
2419     else if (type == TYPE_STRING)
2420     {
2421       boolean active = (value != 0);
2422       char *state_normal = "off";
2423       char *state_active = "on";
2424       char *state = (active ? state_active : state_normal);
2425       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2426                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2427                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2428                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2429
2430       if (nr == GAME_PANEL_GRAVITY_STATE)
2431       {
2432         int font1 = pos->font;          /* (used for normal state) */
2433         int font2 = pos->font_alt;      /* (used for active state) */
2434 #if 0
2435         int size1 = strlen(state_normal);
2436         int size2 = strlen(state_active);
2437         int width1 = size1 * getFontWidth(font1);
2438         int width2 = size2 * getFontWidth(font2);
2439         int max_width = MAX(width1, width2);
2440         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2441
2442         pos->width = max_width;
2443
2444         /* clear background for values that may have changed its size */
2445         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2446                                    max_width, max_height);
2447 #endif
2448
2449         font = (active ? font2 : font1);
2450       }
2451
2452       if (s != NULL)
2453       {
2454         char *s_cut;
2455
2456 #if 1
2457         if (size <= 0)
2458         {
2459           /* don't truncate output if "chars" is zero or less */
2460           size = strlen(s);
2461
2462           /* dynamically correct text alignment */
2463           pos->width = size * getFontWidth(font);
2464         }
2465 #endif
2466
2467         s_cut = getStringCopyN(s, size);
2468
2469         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2470                     s_cut, font, mask_mode);
2471
2472         free(s_cut);
2473       }
2474     }
2475
2476     redraw_mask |= REDRAW_DOOR_1;
2477   }
2478
2479   game_status = GAME_MODE_PLAYING;
2480 }
2481
2482 void DrawGameValue_Emeralds(int value)
2483 {
2484   struct TextPosInfo *pos = &game.panel.gems;
2485 #if 1
2486   int font_nr = pos->font;
2487 #else
2488   int font_nr = FONT_TEXT_2;
2489 #endif
2490   int font_width = getFontWidth(font_nr);
2491   int chars = pos->size;
2492
2493 #if 1
2494   return;       /* !!! USE NEW STUFF !!! */
2495 #endif
2496
2497   if (PANEL_DEACTIVATED(pos))
2498     return;
2499
2500   pos->width = chars * font_width;
2501
2502   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2503 }
2504
2505 void DrawGameValue_Dynamite(int value)
2506 {
2507   struct TextPosInfo *pos = &game.panel.inventory_count;
2508 #if 1
2509   int font_nr = pos->font;
2510 #else
2511   int font_nr = FONT_TEXT_2;
2512 #endif
2513   int font_width = getFontWidth(font_nr);
2514   int chars = pos->size;
2515
2516 #if 1
2517   return;       /* !!! USE NEW STUFF !!! */
2518 #endif
2519
2520   if (PANEL_DEACTIVATED(pos))
2521     return;
2522
2523   pos->width = chars * font_width;
2524
2525   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2526 }
2527
2528 void DrawGameValue_Score(int value)
2529 {
2530   struct TextPosInfo *pos = &game.panel.score;
2531 #if 1
2532   int font_nr = pos->font;
2533 #else
2534   int font_nr = FONT_TEXT_2;
2535 #endif
2536   int font_width = getFontWidth(font_nr);
2537   int chars = pos->size;
2538
2539 #if 1
2540   return;       /* !!! USE NEW STUFF !!! */
2541 #endif
2542
2543   if (PANEL_DEACTIVATED(pos))
2544     return;
2545
2546   pos->width = chars * font_width;
2547
2548   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2549 }
2550
2551 void DrawGameValue_Time(int value)
2552 {
2553   struct TextPosInfo *pos = &game.panel.time;
2554   static int last_value = -1;
2555   int chars1 = 3;
2556   int chars2 = 4;
2557   int chars = pos->size;
2558 #if 1
2559   int font1_nr = pos->font;
2560   int font2_nr = pos->font_alt;
2561 #else
2562   int font1_nr = FONT_TEXT_2;
2563   int font2_nr = FONT_TEXT_1;
2564 #endif
2565   int font_nr = font1_nr;
2566   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2567
2568 #if 1
2569   return;       /* !!! USE NEW STUFF !!! */
2570 #endif
2571
2572   if (PANEL_DEACTIVATED(pos))
2573     return;
2574
2575   if (use_dynamic_chars)                /* use dynamic number of chars */
2576   {
2577     chars   = (value < 1000 ? chars1   : chars2);
2578     font_nr = (value < 1000 ? font1_nr : font2_nr);
2579   }
2580
2581   /* clear background if value just changed its size (dynamic chars only) */
2582   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2583   {
2584     int width1 = chars1 * getFontWidth(font1_nr);
2585     int width2 = chars2 * getFontWidth(font2_nr);
2586     int max_width = MAX(width1, width2);
2587     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2588
2589     pos->width = max_width;
2590
2591     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2592                                max_width, max_height);
2593   }
2594
2595   pos->width = chars * getFontWidth(font_nr);
2596
2597   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2598
2599   last_value = value;
2600 }
2601
2602 void DrawGameValue_Level(int value)
2603 {
2604   struct TextPosInfo *pos = &game.panel.level_number;
2605   int chars1 = 2;
2606   int chars2 = 3;
2607   int chars = pos->size;
2608 #if 1
2609   int font1_nr = pos->font;
2610   int font2_nr = pos->font_alt;
2611 #else
2612   int font1_nr = FONT_TEXT_2;
2613   int font2_nr = FONT_TEXT_1;
2614 #endif
2615   int font_nr = font1_nr;
2616   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2617
2618 #if 1
2619   return;       /* !!! USE NEW STUFF !!! */
2620 #endif
2621
2622   if (PANEL_DEACTIVATED(pos))
2623     return;
2624
2625   if (use_dynamic_chars)                /* use dynamic number of chars */
2626   {
2627     chars   = (level_nr < 100 ? chars1   : chars2);
2628     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2629   }
2630
2631   pos->width = chars * getFontWidth(font_nr);
2632
2633   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2634 }
2635
2636 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2637 {
2638 #if 0
2639   struct TextPosInfo *pos = &game.panel.keys;
2640 #endif
2641 #if 0
2642   int base_key_graphic = EL_KEY_1;
2643 #endif
2644   int i;
2645
2646 #if 1
2647   return;       /* !!! USE NEW STUFF !!! */
2648 #endif
2649
2650 #if 0
2651   if (PANEL_DEACTIVATED(pos))
2652     return;
2653 #endif
2654
2655 #if 0
2656   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2657     base_key_graphic = EL_EM_KEY_1;
2658 #endif
2659
2660 #if 0
2661   pos->width = 4 * MINI_TILEX;
2662 #endif
2663
2664 #if 1
2665   for (i = 0; i < MAX_NUM_KEYS; i++)
2666 #else
2667   /* currently only 4 of 8 possible keys are displayed */
2668   for (i = 0; i < STD_NUM_KEYS; i++)
2669 #endif
2670   {
2671 #if 1
2672     struct TextPosInfo *pos = &game.panel.key[i];
2673 #endif
2674     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2675     int src_y = DOOR_GFX_PAGEY1 + 123;
2676 #if 1
2677     int dst_x = PANEL_XPOS(pos);
2678     int dst_y = PANEL_YPOS(pos);
2679 #else
2680     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2681     int dst_y = PANEL_YPOS(pos);
2682 #endif
2683
2684 #if 1
2685     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2686                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2687                    EL_KEY_1) + i;
2688     int graphic = el2edimg(element);
2689 #endif
2690
2691 #if 1
2692     if (PANEL_DEACTIVATED(pos))
2693       continue;
2694 #endif
2695
2696 #if 0
2697     /* masked blit with tiles from half-size scaled bitmap does not work yet
2698        (no mask bitmap created for these sizes after loading and scaling) --
2699        solution: load without creating mask, scale, then create final mask */
2700
2701     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2702                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2703
2704     if (key[i])
2705     {
2706 #if 0
2707       int graphic = el2edimg(base_key_graphic + i);
2708 #endif
2709       Bitmap *src_bitmap;
2710       int src_x, src_y;
2711
2712       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2713
2714       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2715                     dst_x - src_x, dst_y - src_y);
2716       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2717                        dst_x, dst_y);
2718     }
2719 #else
2720 #if 1
2721     if (key[i])
2722       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2723     else
2724       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2725                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2726 #else
2727     if (key[i])
2728       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2729     else
2730       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2731                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2732 #endif
2733 #endif
2734   }
2735 }
2736
2737 #else
2738
2739 void DrawGameValue_Emeralds(int value)
2740 {
2741   int font_nr = FONT_TEXT_2;
2742   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2743
2744   if (PANEL_DEACTIVATED(game.panel.gems))
2745     return;
2746
2747   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2748 }
2749
2750 void DrawGameValue_Dynamite(int value)
2751 {
2752   int font_nr = FONT_TEXT_2;
2753   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2754
2755   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2756     return;
2757
2758   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2759 }
2760
2761 void DrawGameValue_Score(int value)
2762 {
2763   int font_nr = FONT_TEXT_2;
2764   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2765
2766   if (PANEL_DEACTIVATED(game.panel.score))
2767     return;
2768
2769   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2770 }
2771
2772 void DrawGameValue_Time(int value)
2773 {
2774   int font1_nr = FONT_TEXT_2;
2775 #if 1
2776   int font2_nr = FONT_TEXT_1;
2777 #else
2778   int font2_nr = FONT_LEVEL_NUMBER;
2779 #endif
2780   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2781   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2782
2783   if (PANEL_DEACTIVATED(game.panel.time))
2784     return;
2785
2786   /* clear background if value just changed its size */
2787   if (value == 999 || value == 1000)
2788     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2789
2790   if (value < 1000)
2791     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2792   else
2793     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2794 }
2795
2796 void DrawGameValue_Level(int value)
2797 {
2798   int font1_nr = FONT_TEXT_2;
2799 #if 1
2800   int font2_nr = FONT_TEXT_1;
2801 #else
2802   int font2_nr = FONT_LEVEL_NUMBER;
2803 #endif
2804
2805   if (PANEL_DEACTIVATED(game.panel.level))
2806     return;
2807
2808   if (level_nr < 100)
2809     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2810   else
2811     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2812 }
2813
2814 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2815 {
2816   int base_key_graphic = EL_KEY_1;
2817   int i;
2818
2819   if (PANEL_DEACTIVATED(game.panel.keys))
2820     return;
2821
2822   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2823     base_key_graphic = EL_EM_KEY_1;
2824
2825   /* currently only 4 of 8 possible keys are displayed */
2826   for (i = 0; i < STD_NUM_KEYS; i++)
2827   {
2828     int x = XX_KEYS + i * MINI_TILEX;
2829     int y = YY_KEYS;
2830
2831     if (key[i])
2832       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2833     else
2834       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2835                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2836   }
2837 }
2838
2839 #endif
2840
2841 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2842                        int key_bits)
2843 {
2844   int key[MAX_NUM_KEYS];
2845   int i;
2846
2847   /* prevent EM engine from updating time/score values parallel to GameWon() */
2848   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2849       local_player->LevelSolved)
2850     return;
2851
2852   for (i = 0; i < MAX_NUM_KEYS; i++)
2853     key[i] = key_bits & (1 << i);
2854
2855   DrawGameValue_Level(level_nr);
2856
2857   DrawGameValue_Emeralds(emeralds);
2858   DrawGameValue_Dynamite(dynamite);
2859   DrawGameValue_Score(score);
2860   DrawGameValue_Time(time);
2861
2862   DrawGameValue_Keys(key);
2863 }
2864
2865 void UpdateGameDoorValues()
2866 {
2867   UpdateGameControlValues();
2868 }
2869
2870 void DrawGameDoorValues()
2871 {
2872   DisplayGameControlValues();
2873 }
2874
2875 void DrawGameDoorValues_OLD()
2876 {
2877   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2878   int dynamite_value = 0;
2879   int score_value = (local_player->LevelSolved ? local_player->score_final :
2880                      local_player->score);
2881   int gems_value = local_player->gems_still_needed;
2882   int key_bits = 0;
2883   int i, j;
2884
2885   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2886   {
2887     DrawGameDoorValues_EM();
2888
2889     return;
2890   }
2891
2892   if (game.centered_player_nr == -1)
2893   {
2894     for (i = 0; i < MAX_PLAYERS; i++)
2895     {
2896       for (j = 0; j < MAX_NUM_KEYS; j++)
2897         if (stored_player[i].key[j])
2898           key_bits |= (1 << j);
2899
2900       dynamite_value += stored_player[i].inventory_size;
2901     }
2902   }
2903   else
2904   {
2905     int player_nr = game.centered_player_nr;
2906
2907     for (i = 0; i < MAX_NUM_KEYS; i++)
2908       if (stored_player[player_nr].key[i])
2909         key_bits |= (1 << i);
2910
2911     dynamite_value = stored_player[player_nr].inventory_size;
2912   }
2913
2914   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2915                     key_bits);
2916 }
2917
2918
2919 /*
2920   =============================================================================
2921   InitGameEngine()
2922   -----------------------------------------------------------------------------
2923   initialize game engine due to level / tape version number
2924   =============================================================================
2925 */
2926
2927 static void InitGameEngine()
2928 {
2929   int i, j, k, l, x, y;
2930
2931   /* set game engine from tape file when re-playing, else from level file */
2932   game.engine_version = (tape.playing ? tape.engine_version :
2933                          level.game_version);
2934
2935   /* ---------------------------------------------------------------------- */
2936   /* set flags for bugs and changes according to active game engine version */
2937   /* ---------------------------------------------------------------------- */
2938
2939   /*
2940     Summary of bugfix/change:
2941     Fixed handling for custom elements that change when pushed by the player.
2942
2943     Fixed/changed in version:
2944     3.1.0
2945
2946     Description:
2947     Before 3.1.0, custom elements that "change when pushing" changed directly
2948     after the player started pushing them (until then handled in "DigField()").
2949     Since 3.1.0, these custom elements are not changed until the "pushing"
2950     move of the element is finished (now handled in "ContinueMoving()").
2951
2952     Affected levels/tapes:
2953     The first condition is generally needed for all levels/tapes before version
2954     3.1.0, which might use the old behaviour before it was changed; known tapes
2955     that are affected are some tapes from the level set "Walpurgis Gardens" by
2956     Jamie Cullen.
2957     The second condition is an exception from the above case and is needed for
2958     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2959     above (including some development versions of 3.1.0), but before it was
2960     known that this change would break tapes like the above and was fixed in
2961     3.1.1, so that the changed behaviour was active although the engine version
2962     while recording maybe was before 3.1.0. There is at least one tape that is
2963     affected by this exception, which is the tape for the one-level set "Bug
2964     Machine" by Juergen Bonhagen.
2965   */
2966
2967   game.use_change_when_pushing_bug =
2968     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2969      !(tape.playing &&
2970        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2971        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2972
2973   /*
2974     Summary of bugfix/change:
2975     Fixed handling for blocking the field the player leaves when moving.
2976
2977     Fixed/changed in version:
2978     3.1.1
2979
2980     Description:
2981     Before 3.1.1, when "block last field when moving" was enabled, the field
2982     the player is leaving when moving was blocked for the time of the move,
2983     and was directly unblocked afterwards. This resulted in the last field
2984     being blocked for exactly one less than the number of frames of one player
2985     move. Additionally, even when blocking was disabled, the last field was
2986     blocked for exactly one frame.
2987     Since 3.1.1, due to changes in player movement handling, the last field
2988     is not blocked at all when blocking is disabled. When blocking is enabled,
2989     the last field is blocked for exactly the number of frames of one player
2990     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2991     last field is blocked for exactly one more than the number of frames of
2992     one player move.
2993
2994     Affected levels/tapes:
2995     (!!! yet to be determined -- probably many !!!)
2996   */
2997
2998   game.use_block_last_field_bug =
2999     (game.engine_version < VERSION_IDENT(3,1,1,0));
3000
3001   /*
3002     Summary of bugfix/change:
3003     Changed behaviour of CE changes with multiple changes per single frame.
3004
3005     Fixed/changed in version:
3006     3.2.0-6
3007
3008     Description:
3009     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3010     This resulted in race conditions where CEs seem to behave strange in some
3011     situations (where triggered CE changes were just skipped because there was
3012     already a CE change on that tile in the playfield in that engine frame).
3013     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3014     (The number of changes per frame must be limited in any case, because else
3015     it is easily possible to define CE changes that would result in an infinite
3016     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3017     should be set large enough so that it would only be reached in cases where
3018     the corresponding CE change conditions run into a loop. Therefore, it seems
3019     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3020     maximal number of change pages for custom elements.)
3021
3022     Affected levels/tapes:
3023     Probably many.
3024   */
3025
3026 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3027   game.max_num_changes_per_frame = 1;
3028 #else
3029   game.max_num_changes_per_frame =
3030     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3031 #endif
3032
3033   /* ---------------------------------------------------------------------- */
3034
3035   /* default scan direction: scan playfield from top/left to bottom/right */
3036   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3037
3038   /* dynamically adjust element properties according to game engine version */
3039   InitElementPropertiesEngine(game.engine_version);
3040
3041 #if 0
3042   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3043   printf("          tape version == %06d [%s] [file: %06d]\n",
3044          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3045          tape.file_version);
3046   printf("       => game.engine_version == %06d\n", game.engine_version);
3047 #endif
3048
3049   /* ---------- initialize player's initial move delay --------------------- */
3050
3051   /* dynamically adjust player properties according to level information */
3052   for (i = 0; i < MAX_PLAYERS; i++)
3053     game.initial_move_delay_value[i] =
3054       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3055
3056   /* dynamically adjust player properties according to game engine version */
3057   for (i = 0; i < MAX_PLAYERS; i++)
3058     game.initial_move_delay[i] =
3059       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3060        game.initial_move_delay_value[i] : 0);
3061
3062   /* ---------- initialize player's initial push delay --------------------- */
3063
3064   /* dynamically adjust player properties according to game engine version */
3065   game.initial_push_delay_value =
3066     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3067
3068   /* ---------- initialize changing elements ------------------------------- */
3069
3070   /* initialize changing elements information */
3071   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3072   {
3073     struct ElementInfo *ei = &element_info[i];
3074
3075     /* this pointer might have been changed in the level editor */
3076     ei->change = &ei->change_page[0];
3077
3078     if (!IS_CUSTOM_ELEMENT(i))
3079     {
3080       ei->change->target_element = EL_EMPTY_SPACE;
3081       ei->change->delay_fixed = 0;
3082       ei->change->delay_random = 0;
3083       ei->change->delay_frames = 1;
3084     }
3085
3086     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3087     {
3088       ei->has_change_event[j] = FALSE;
3089
3090       ei->event_page_nr[j] = 0;
3091       ei->event_page[j] = &ei->change_page[0];
3092     }
3093   }
3094
3095   /* add changing elements from pre-defined list */
3096   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3097   {
3098     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3099     struct ElementInfo *ei = &element_info[ch_delay->element];
3100
3101     ei->change->target_element       = ch_delay->target_element;
3102     ei->change->delay_fixed          = ch_delay->change_delay;
3103
3104     ei->change->pre_change_function  = ch_delay->pre_change_function;
3105     ei->change->change_function      = ch_delay->change_function;
3106     ei->change->post_change_function = ch_delay->post_change_function;
3107
3108     ei->change->can_change = TRUE;
3109     ei->change->can_change_or_has_action = TRUE;
3110
3111     ei->has_change_event[CE_DELAY] = TRUE;
3112
3113     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3114     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3115   }
3116
3117   /* ---------- initialize internal run-time variables ------------- */
3118
3119   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3122
3123     for (j = 0; j < ei->num_change_pages; j++)
3124     {
3125       ei->change_page[j].can_change_or_has_action =
3126         (ei->change_page[j].can_change |
3127          ei->change_page[j].has_action);
3128     }
3129   }
3130
3131   /* add change events from custom element configuration */
3132   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3133   {
3134     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3135
3136     for (j = 0; j < ei->num_change_pages; j++)
3137     {
3138       if (!ei->change_page[j].can_change_or_has_action)
3139         continue;
3140
3141       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3142       {
3143         /* only add event page for the first page found with this event */
3144         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3145         {
3146           ei->has_change_event[k] = TRUE;
3147
3148           ei->event_page_nr[k] = j;
3149           ei->event_page[k] = &ei->change_page[j];
3150         }
3151       }
3152     }
3153   }
3154
3155   /* ---------- initialize run-time trigger player and element ------------- */
3156
3157   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3158   {
3159     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3160
3161     for (j = 0; j < ei->num_change_pages; j++)
3162     {
3163       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3164       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3165       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3166       ei->change_page[j].actual_trigger_ce_value = 0;
3167       ei->change_page[j].actual_trigger_ce_score = 0;
3168     }
3169   }
3170
3171   /* ---------- initialize trigger events ---------------------------------- */
3172
3173   /* initialize trigger events information */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3176       trigger_events[i][j] = FALSE;
3177
3178   /* add trigger events from element change event properties */
3179   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3180   {
3181     struct ElementInfo *ei = &element_info[i];
3182
3183     for (j = 0; j < ei->num_change_pages; j++)
3184     {
3185       if (!ei->change_page[j].can_change_or_has_action)
3186         continue;
3187
3188       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3189       {
3190         int trigger_element = ei->change_page[j].trigger_element;
3191
3192         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3193         {
3194           if (ei->change_page[j].has_event[k])
3195           {
3196             if (IS_GROUP_ELEMENT(trigger_element))
3197             {
3198               struct ElementGroupInfo *group =
3199                 element_info[trigger_element].group;
3200
3201               for (l = 0; l < group->num_elements_resolved; l++)
3202                 trigger_events[group->element_resolved[l]][k] = TRUE;
3203             }
3204             else if (trigger_element == EL_ANY_ELEMENT)
3205               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3206                 trigger_events[l][k] = TRUE;
3207             else
3208               trigger_events[trigger_element][k] = TRUE;
3209           }
3210         }
3211       }
3212     }
3213   }
3214
3215   /* ---------- initialize push delay -------------------------------------- */
3216
3217   /* initialize push delay values to default */
3218   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3219   {
3220     if (!IS_CUSTOM_ELEMENT(i))
3221     {
3222       /* set default push delay values (corrected since version 3.0.7-1) */
3223       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3224       {
3225         element_info[i].push_delay_fixed = 2;
3226         element_info[i].push_delay_random = 8;
3227       }
3228       else
3229       {
3230         element_info[i].push_delay_fixed = 8;
3231         element_info[i].push_delay_random = 8;
3232       }
3233     }
3234   }
3235
3236   /* set push delay value for certain elements from pre-defined list */
3237   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3238   {
3239     int e = push_delay_list[i].element;
3240
3241     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3242     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3243   }
3244
3245   /* set push delay value for Supaplex elements for newer engine versions */
3246   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3247   {
3248     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3249     {
3250       if (IS_SP_ELEMENT(i))
3251       {
3252         /* set SP push delay to just enough to push under a falling zonk */
3253         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3254
3255         element_info[i].push_delay_fixed  = delay;
3256         element_info[i].push_delay_random = 0;
3257       }
3258     }
3259   }
3260
3261   /* ---------- initialize move stepsize ----------------------------------- */
3262
3263   /* initialize move stepsize values to default */
3264   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3265     if (!IS_CUSTOM_ELEMENT(i))
3266       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3267
3268   /* set move stepsize value for certain elements from pre-defined list */
3269   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3270   {
3271     int e = move_stepsize_list[i].element;
3272
3273     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3274   }
3275
3276   /* ---------- initialize collect score ----------------------------------- */
3277
3278   /* initialize collect score values for custom elements from initial value */
3279   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3280     if (IS_CUSTOM_ELEMENT(i))
3281       element_info[i].collect_score = element_info[i].collect_score_initial;
3282
3283   /* ---------- initialize collect count ----------------------------------- */
3284
3285   /* initialize collect count values for non-custom elements */
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287     if (!IS_CUSTOM_ELEMENT(i))
3288       element_info[i].collect_count_initial = 0;
3289
3290   /* add collect count values for all elements from pre-defined list */
3291   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3292     element_info[collect_count_list[i].element].collect_count_initial =
3293       collect_count_list[i].count;
3294
3295   /* ---------- initialize access direction -------------------------------- */
3296
3297   /* initialize access direction values to default (access from every side) */
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299     if (!IS_CUSTOM_ELEMENT(i))
3300       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3301
3302   /* set access direction value for certain elements from pre-defined list */
3303   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3304     element_info[access_direction_list[i].element].access_direction =
3305       access_direction_list[i].direction;
3306
3307   /* ---------- initialize explosion content ------------------------------- */
3308   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309   {
3310     if (IS_CUSTOM_ELEMENT(i))
3311       continue;
3312
3313     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3314     {
3315       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3316
3317       element_info[i].content.e[x][y] =
3318         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3319          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3320          i == EL_PLAYER_3 ? EL_EMERALD :
3321          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3322          i == EL_MOLE ? EL_EMERALD_RED :
3323          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3324          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3325          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3326          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3327          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3328          i == EL_WALL_EMERALD ? EL_EMERALD :
3329          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3330          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3331          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3332          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3333          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3334          i == EL_WALL_PEARL ? EL_PEARL :
3335          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3336          EL_EMPTY);
3337     }
3338   }
3339
3340   /* ---------- initialize recursion detection ------------------------------ */
3341   recursion_loop_depth = 0;
3342   recursion_loop_detected = FALSE;
3343   recursion_loop_element = EL_UNDEFINED;
3344
3345   /* ---------- initialize graphics engine ---------------------------------- */
3346   game.scroll_delay_value =
3347     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3348      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3349   game.scroll_delay_value =
3350     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3351 }
3352
3353 int get_num_special_action(int element, int action_first, int action_last)
3354 {
3355   int num_special_action = 0;
3356   int i, j;
3357
3358   for (i = action_first; i <= action_last; i++)
3359   {
3360     boolean found = FALSE;
3361
3362     for (j = 0; j < NUM_DIRECTIONS; j++)
3363       if (el_act_dir2img(element, i, j) !=
3364           el_act_dir2img(element, ACTION_DEFAULT, j))
3365         found = TRUE;
3366
3367     if (found)
3368       num_special_action++;
3369     else
3370       break;
3371   }
3372
3373   return num_special_action;
3374 }
3375
3376
3377 /*
3378   =============================================================================
3379   InitGame()
3380   -----------------------------------------------------------------------------
3381   initialize and start new game
3382   =============================================================================
3383 */
3384
3385 void InitGame()
3386 {
3387   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3388   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3389   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3390 #if 0
3391   boolean do_fading = (game_status == GAME_MODE_MAIN);
3392 #endif
3393   int i, j, x, y;
3394
3395   game_status = GAME_MODE_PLAYING;
3396
3397   InitGameEngine();
3398   InitGameControlValues();
3399
3400   /* don't play tapes over network */
3401   network_playing = (options.network && !tape.playing);
3402
3403   for (i = 0; i < MAX_PLAYERS; i++)
3404   {
3405     struct PlayerInfo *player = &stored_player[i];
3406
3407     player->index_nr = i;
3408     player->index_bit = (1 << i);
3409     player->element_nr = EL_PLAYER_1 + i;
3410
3411     player->present = FALSE;
3412     player->active = FALSE;
3413     player->killed = FALSE;
3414
3415     player->action = 0;
3416     player->effective_action = 0;
3417     player->programmed_action = 0;
3418
3419     player->score = 0;
3420     player->score_final = 0;
3421
3422     player->gems_still_needed = level.gems_needed;
3423     player->sokobanfields_still_needed = 0;
3424     player->lights_still_needed = 0;
3425     player->friends_still_needed = 0;
3426
3427     for (j = 0; j < MAX_NUM_KEYS; j++)
3428       player->key[j] = FALSE;
3429
3430     player->num_white_keys = 0;
3431
3432     player->dynabomb_count = 0;
3433     player->dynabomb_size = 1;
3434     player->dynabombs_left = 0;
3435     player->dynabomb_xl = FALSE;
3436
3437     player->MovDir = MV_NONE;
3438     player->MovPos = 0;
3439     player->GfxPos = 0;
3440     player->GfxDir = MV_NONE;
3441     player->GfxAction = ACTION_DEFAULT;
3442     player->Frame = 0;
3443     player->StepFrame = 0;
3444
3445     player->use_murphy = FALSE;
3446     player->artwork_element =
3447       (level.use_artwork_element[i] ? level.artwork_element[i] :
3448        player->element_nr);
3449
3450     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3451     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3452
3453     player->gravity = level.initial_player_gravity[i];
3454
3455     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3456
3457     player->actual_frame_counter = 0;
3458
3459     player->step_counter = 0;
3460
3461     player->last_move_dir = MV_NONE;
3462
3463     player->is_active = FALSE;
3464
3465     player->is_waiting = FALSE;
3466     player->is_moving = FALSE;
3467     player->is_auto_moving = FALSE;
3468     player->is_digging = FALSE;
3469     player->is_snapping = FALSE;
3470     player->is_collecting = FALSE;
3471     player->is_pushing = FALSE;
3472     player->is_switching = FALSE;
3473     player->is_dropping = FALSE;
3474     player->is_dropping_pressed = FALSE;
3475
3476     player->is_bored = FALSE;
3477     player->is_sleeping = FALSE;
3478
3479     player->frame_counter_bored = -1;
3480     player->frame_counter_sleeping = -1;
3481
3482     player->anim_delay_counter = 0;
3483     player->post_delay_counter = 0;
3484
3485     player->dir_waiting = MV_NONE;
3486     player->action_waiting = ACTION_DEFAULT;
3487     player->last_action_waiting = ACTION_DEFAULT;
3488     player->special_action_bored = ACTION_DEFAULT;
3489     player->special_action_sleeping = ACTION_DEFAULT;
3490
3491     player->switch_x = -1;
3492     player->switch_y = -1;
3493
3494     player->drop_x = -1;
3495     player->drop_y = -1;
3496
3497     player->show_envelope = 0;
3498
3499     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3500
3501     player->push_delay       = -1;      /* initialized when pushing starts */
3502     player->push_delay_value = game.initial_push_delay_value;
3503
3504     player->drop_delay = 0;
3505     player->drop_pressed_delay = 0;
3506
3507     player->last_jx = -1;
3508     player->last_jy = -1;
3509     player->jx = -1;
3510     player->jy = -1;
3511
3512     player->shield_normal_time_left = 0;
3513     player->shield_deadly_time_left = 0;
3514
3515     player->inventory_infinite_element = EL_UNDEFINED;
3516     player->inventory_size = 0;
3517
3518     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3519     SnapField(player, 0, 0);
3520
3521     player->LevelSolved = FALSE;
3522     player->GameOver = FALSE;
3523
3524     player->LevelSolved_GameWon = FALSE;
3525     player->LevelSolved_GameEnd = FALSE;
3526     player->LevelSolved_PanelOff = FALSE;
3527     player->LevelSolved_SaveTape = FALSE;
3528     player->LevelSolved_SaveScore = FALSE;
3529   }
3530
3531   network_player_action_received = FALSE;
3532
3533 #if defined(NETWORK_AVALIABLE)
3534   /* initial null action */
3535   if (network_playing)
3536     SendToServer_MovePlayer(MV_NONE);
3537 #endif
3538
3539   ZX = ZY = -1;
3540   ExitX = ExitY = -1;
3541
3542   FrameCounter = 0;
3543   TimeFrames = 0;
3544   TimePlayed = 0;
3545   TimeLeft = level.time;
3546   TapeTime = 0;
3547
3548   ScreenMovDir = MV_NONE;
3549   ScreenMovPos = 0;
3550   ScreenGfxPos = 0;
3551
3552   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3553
3554   AllPlayersGone = FALSE;
3555
3556   game.yamyam_content_nr = 0;
3557   game.robot_wheel_active = FALSE;
3558   game.magic_wall_active = FALSE;
3559   game.magic_wall_time_left = 0;
3560   game.light_time_left = 0;
3561   game.timegate_time_left = 0;
3562   game.switchgate_pos = 0;
3563   game.wind_direction = level.wind_direction_initial;
3564
3565 #if !USE_PLAYER_GRAVITY
3566   game.gravity = FALSE;
3567   game.explosions_delayed = TRUE;
3568 #endif
3569
3570   game.lenses_time_left = 0;
3571   game.magnify_time_left = 0;
3572
3573   game.ball_state = level.ball_state_initial;
3574   game.ball_content_nr = 0;
3575
3576   game.envelope_active = FALSE;
3577
3578   /* set focus to local player for network games, else to all players */
3579   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3580   game.centered_player_nr_next = game.centered_player_nr;
3581   game.set_centered_player = FALSE;
3582
3583   if (network_playing && tape.recording)
3584   {
3585     /* store client dependent player focus when recording network games */
3586     tape.centered_player_nr_next = game.centered_player_nr_next;
3587     tape.set_centered_player = TRUE;
3588   }
3589
3590   for (i = 0; i < NUM_BELTS; i++)
3591   {
3592     game.belt_dir[i] = MV_NONE;
3593     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3594   }
3595
3596   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3597     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3598
3599   SCAN_PLAYFIELD(x, y)
3600   {
3601     Feld[x][y] = level.field[x][y];
3602     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3603     ChangeDelay[x][y] = 0;
3604     ChangePage[x][y] = -1;
3605 #if USE_NEW_CUSTOM_VALUE
3606     CustomValue[x][y] = 0;              /* initialized in InitField() */
3607 #endif
3608     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3609     AmoebaNr[x][y] = 0;
3610     WasJustMoving[x][y] = 0;
3611     WasJustFalling[x][y] = 0;
3612     CheckCollision[x][y] = 0;
3613     CheckImpact[x][y] = 0;
3614     Stop[x][y] = FALSE;
3615     Pushed[x][y] = FALSE;
3616
3617     ChangeCount[x][y] = 0;
3618     ChangeEvent[x][y] = -1;
3619
3620     ExplodePhase[x][y] = 0;
3621     ExplodeDelay[x][y] = 0;
3622     ExplodeField[x][y] = EX_TYPE_NONE;
3623
3624     RunnerVisit[x][y] = 0;
3625     PlayerVisit[x][y] = 0;
3626
3627     GfxFrame[x][y] = 0;
3628     GfxRandom[x][y] = INIT_GFX_RANDOM();
3629     GfxElement[x][y] = EL_UNDEFINED;
3630     GfxAction[x][y] = ACTION_DEFAULT;
3631     GfxDir[x][y] = MV_NONE;
3632   }
3633
3634   SCAN_PLAYFIELD(x, y)
3635   {
3636     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3637       emulate_bd = FALSE;
3638     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3639       emulate_sb = FALSE;
3640     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3641       emulate_sp = FALSE;
3642
3643     InitField(x, y, TRUE);
3644
3645     ResetGfxAnimation(x, y);
3646   }
3647
3648   InitBeltMovement();
3649
3650   for (i = 0; i < MAX_PLAYERS; i++)
3651   {
3652     struct PlayerInfo *player = &stored_player[i];
3653
3654     /* set number of special actions for bored and sleeping animation */
3655     player->num_special_action_bored =
3656       get_num_special_action(player->artwork_element,
3657                              ACTION_BORING_1, ACTION_BORING_LAST);
3658     player->num_special_action_sleeping =
3659       get_num_special_action(player->artwork_element,
3660                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3661   }
3662
3663   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3664                     emulate_sb ? EMU_SOKOBAN :
3665                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3666
3667 #if USE_NEW_ALL_SLIPPERY
3668   /* initialize type of slippery elements */
3669   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3670   {
3671     if (!IS_CUSTOM_ELEMENT(i))
3672     {
3673       /* default: elements slip down either to the left or right randomly */
3674       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3675
3676       /* SP style elements prefer to slip down on the left side */
3677       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3678         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3679
3680       /* BD style elements prefer to slip down on the left side */
3681       if (game.emulation == EMU_BOULDERDASH)
3682         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3683     }
3684   }
3685 #endif
3686
3687   /* initialize explosion and ignition delay */
3688   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3689   {
3690     if (!IS_CUSTOM_ELEMENT(i))
3691     {
3692       int num_phase = 8;
3693       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3694                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3695                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3696       int last_phase = (num_phase + 1) * delay;
3697       int half_phase = (num_phase / 2) * delay;
3698
3699       element_info[i].explosion_delay = last_phase - 1;
3700       element_info[i].ignition_delay = half_phase;
3701
3702       if (i == EL_BLACK_ORB)
3703         element_info[i].ignition_delay = 1;
3704     }
3705
3706 #if 0
3707     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3708       element_info[i].explosion_delay = 1;
3709
3710     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3711       element_info[i].ignition_delay = 1;
3712 #endif
3713   }
3714
3715   /* correct non-moving belts to start moving left */
3716   for (i = 0; i < NUM_BELTS; i++)
3717     if (game.belt_dir[i] == MV_NONE)
3718       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3719
3720   /* check if any connected player was not found in playfield */
3721   for (i = 0; i < MAX_PLAYERS; i++)
3722   {
3723     struct PlayerInfo *player = &stored_player[i];
3724
3725     if (player->connected && !player->present)
3726     {
3727       for (j = 0; j < MAX_PLAYERS; j++)
3728       {
3729         struct PlayerInfo *some_player = &stored_player[j];
3730         int jx = some_player->jx, jy = some_player->jy;
3731
3732         /* assign first free player found that is present in the playfield */
3733         if (some_player->present && !some_player->connected)
3734         {
3735           player->present = TRUE;
3736           player->active = TRUE;
3737
3738           some_player->present = FALSE;
3739           some_player->active = FALSE;
3740
3741           player->artwork_element = some_player->artwork_element;
3742
3743           player->block_last_field       = some_player->block_last_field;
3744           player->block_delay_adjustment = some_player->block_delay_adjustment;
3745
3746           StorePlayer[jx][jy] = player->element_nr;
3747           player->jx = player->last_jx = jx;
3748           player->jy = player->last_jy = jy;
3749
3750           break;
3751         }
3752       }
3753     }
3754   }
3755
3756   if (tape.playing)
3757   {
3758     /* when playing a tape, eliminate all players who do not participate */
3759
3760     for (i = 0; i < MAX_PLAYERS; i++)
3761     {
3762       if (stored_player[i].active && !tape.player_participates[i])
3763       {
3764         struct PlayerInfo *player = &stored_player[i];
3765         int jx = player->jx, jy = player->jy;
3766
3767         player->active = FALSE;
3768         StorePlayer[jx][jy] = 0;
3769         Feld[jx][jy] = EL_EMPTY;
3770       }
3771     }
3772   }
3773   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3774   {
3775     /* when in single player mode, eliminate all but the first active player */
3776
3777     for (i = 0; i < MAX_PLAYERS; i++)
3778     {
3779       if (stored_player[i].active)
3780       {
3781         for (j = i + 1; j < MAX_PLAYERS; j++)
3782         {
3783           if (stored_player[j].active)
3784           {
3785             struct PlayerInfo *player = &stored_player[j];
3786             int jx = player->jx, jy = player->jy;
3787
3788             player->active = FALSE;
3789             player->present = FALSE;
3790
3791             StorePlayer[jx][jy] = 0;
3792             Feld[jx][jy] = EL_EMPTY;
3793           }
3794         }
3795       }
3796     }
3797   }
3798
3799   /* when recording the game, store which players take part in the game */
3800   if (tape.recording)
3801   {
3802     for (i = 0; i < MAX_PLAYERS; i++)
3803       if (stored_player[i].active)
3804         tape.player_participates[i] = TRUE;
3805   }
3806
3807   if (options.debug)
3808   {
3809     for (i = 0; i < MAX_PLAYERS; i++)
3810     {
3811       struct PlayerInfo *player = &stored_player[i];
3812
3813       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3814              i+1,
3815              player->present,
3816              player->connected,
3817              player->active);
3818       if (local_player == player)
3819         printf("Player  %d is local player.\n", i+1);
3820     }
3821   }
3822
3823   if (BorderElement == EL_EMPTY)
3824   {
3825     SBX_Left = 0;
3826     SBX_Right = lev_fieldx - SCR_FIELDX;
3827     SBY_Upper = 0;
3828     SBY_Lower = lev_fieldy - SCR_FIELDY;
3829   }
3830   else
3831   {
3832     SBX_Left = -1;
3833     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3834     SBY_Upper = -1;
3835     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3836   }
3837
3838   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3839     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3840
3841   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3842     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3843
3844   /* if local player not found, look for custom element that might create
3845      the player (make some assumptions about the right custom element) */
3846   if (!local_player->present)
3847   {
3848     int start_x = 0, start_y = 0;
3849     int found_rating = 0;
3850     int found_element = EL_UNDEFINED;
3851     int player_nr = local_player->index_nr;
3852
3853     SCAN_PLAYFIELD(x, y)
3854     {
3855       int element = Feld[x][y];
3856       int content;
3857       int xx, yy;
3858       boolean is_player;
3859
3860       if (level.use_start_element[player_nr] &&
3861           level.start_element[player_nr] == element &&
3862           found_rating < 4)
3863       {
3864         start_x = x;
3865         start_y = y;
3866
3867         found_rating = 4;
3868         found_element = element;
3869       }
3870
3871       if (!IS_CUSTOM_ELEMENT(element))
3872         continue;
3873
3874       if (CAN_CHANGE(element))
3875       {
3876         for (i = 0; i < element_info[element].num_change_pages; i++)
3877         {
3878           /* check for player created from custom element as single target */
3879           content = element_info[element].change_page[i].target_element;
3880           is_player = ELEM_IS_PLAYER(content);
3881
3882           if (is_player && (found_rating < 3 ||
3883                             (found_rating == 3 && element < found_element)))
3884           {
3885             start_x = x;
3886             start_y = y;
3887
3888             found_rating = 3;
3889             found_element = element;
3890           }
3891         }
3892       }
3893
3894       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3895       {
3896         /* check for player created from custom element as explosion content */
3897         content = element_info[element].content.e[xx][yy];
3898         is_player = ELEM_IS_PLAYER(content);
3899
3900         if (is_player && (found_rating < 2 ||
3901                           (found_rating == 2 && element < found_element)))
3902         {
3903           start_x = x + xx - 1;
3904           start_y = y + yy - 1;
3905
3906           found_rating = 2;
3907           found_element = element;
3908         }
3909
3910         if (!CAN_CHANGE(element))
3911           continue;
3912
3913         for (i = 0; i < element_info[element].num_change_pages; i++)
3914         {
3915           /* check for player created from custom element as extended target */
3916           content =
3917             element_info[element].change_page[i].target_content.e[xx][yy];
3918
3919           is_player = ELEM_IS_PLAYER(content);
3920
3921           if (is_player && (found_rating < 1 ||
3922                             (found_rating == 1 && element < found_element)))
3923           {
3924             start_x = x + xx - 1;
3925             start_y = y + yy - 1;
3926
3927             found_rating = 1;
3928             found_element = element;
3929           }
3930         }
3931       }
3932     }
3933
3934     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3935                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3936                 start_x - MIDPOSX);
3937
3938     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3939                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3940                 start_y - MIDPOSY);
3941   }
3942   else
3943   {
3944     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3945                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3946                 local_player->jx - MIDPOSX);
3947
3948     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3949                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3950                 local_player->jy - MIDPOSY);
3951   }
3952
3953   /* do not use PLAYING mask for fading out from main screen */
3954   game_status = GAME_MODE_MAIN;
3955
3956   StopAnimation();
3957
3958   if (!game.restart_level)
3959     CloseDoor(DOOR_CLOSE_1);
3960
3961 #if 1
3962   if (level_editor_test_game)
3963     FadeSkipNextFadeIn();
3964   else
3965     FadeSetEnterScreen();
3966 #else
3967   if (level_editor_test_game)
3968     fading = fading_none;
3969   else
3970     fading = menu.destination;
3971 #endif
3972
3973 #if 1
3974   FadeOut(REDRAW_FIELD);
3975 #else
3976   if (do_fading)
3977     FadeOut(REDRAW_FIELD);
3978 #endif
3979
3980   game_status = GAME_MODE_PLAYING;
3981
3982   /* !!! FIX THIS (START) !!! */
3983   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3984   {
3985     InitGameEngine_EM();
3986
3987     /* blit playfield from scroll buffer to normal back buffer for fading in */
3988     BlitScreenToBitmap_EM(backbuffer);
3989   }
3990   else
3991   {
3992     DrawLevel();
3993     DrawAllPlayers();
3994
3995     /* after drawing the level, correct some elements */
3996     if (game.timegate_time_left == 0)
3997       CloseAllOpenTimegates();
3998
3999     /* blit playfield from scroll buffer to normal back buffer for fading in */
4000     if (setup.soft_scrolling)
4001       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4002
4003     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4004   }
4005   /* !!! FIX THIS (END) !!! */
4006
4007 #if 1
4008   FadeIn(REDRAW_FIELD);
4009 #else
4010   if (do_fading)
4011     FadeIn(REDRAW_FIELD);
4012
4013   BackToFront();
4014 #endif
4015
4016   if (!game.restart_level)
4017   {
4018     /* copy default game door content to main double buffer */
4019     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4020                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4021   }
4022
4023   SetPanelBackground();
4024   SetDrawBackgroundMask(REDRAW_DOOR_1);
4025
4026   UpdateGameDoorValues();
4027   DrawGameDoorValues();
4028
4029   if (!game.restart_level)
4030   {
4031     UnmapGameButtons();
4032     UnmapTapeButtons();
4033     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4034     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4035     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4036     MapGameButtons();
4037     MapTapeButtons();
4038
4039     /* copy actual game door content to door double buffer for OpenDoor() */
4040     BlitBitmap(drawto, bitmap_db_door,
4041                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4042
4043     OpenDoor(DOOR_OPEN_ALL);
4044
4045     PlaySound(SND_GAME_STARTING);
4046
4047     if (setup.sound_music)
4048       PlayLevelMusic();
4049
4050     KeyboardAutoRepeatOffUnlessAutoplay();
4051
4052     if (options.debug)
4053     {
4054       for (i = 0; i < MAX_PLAYERS; i++)
4055         printf("Player %d %sactive.\n",
4056                i + 1, (stored_player[i].active ? "" : "not "));
4057     }
4058   }
4059
4060 #if 1
4061   UnmapAllGadgets();
4062
4063   MapGameButtons();
4064   MapTapeButtons();
4065 #endif
4066
4067   game.restart_level = FALSE;
4068 }
4069
4070 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4071 {
4072   /* this is used for non-R'n'D game engines to update certain engine values */
4073
4074   /* needed to determine if sounds are played within the visible screen area */
4075   scroll_x = actual_scroll_x;
4076   scroll_y = actual_scroll_y;
4077 }
4078
4079 void InitMovDir(int x, int y)
4080 {
4081   int i, element = Feld[x][y];
4082   static int xy[4][2] =
4083   {
4084     {  0, +1 },
4085     { +1,  0 },
4086     {  0, -1 },
4087     { -1,  0 }
4088   };
4089   static int direction[3][4] =
4090   {
4091     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4092     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4093     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4094   };
4095
4096   switch (element)
4097   {
4098     case EL_BUG_RIGHT:
4099     case EL_BUG_UP:
4100     case EL_BUG_LEFT:
4101     case EL_BUG_DOWN:
4102       Feld[x][y] = EL_BUG;
4103       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4104       break;
4105
4106     case EL_SPACESHIP_RIGHT:
4107     case EL_SPACESHIP_UP:
4108     case EL_SPACESHIP_LEFT:
4109     case EL_SPACESHIP_DOWN:
4110       Feld[x][y] = EL_SPACESHIP;
4111       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4112       break;
4113
4114     case EL_BD_BUTTERFLY_RIGHT:
4115     case EL_BD_BUTTERFLY_UP:
4116     case EL_BD_BUTTERFLY_LEFT:
4117     case EL_BD_BUTTERFLY_DOWN:
4118       Feld[x][y] = EL_BD_BUTTERFLY;
4119       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4120       break;
4121
4122     case EL_BD_FIREFLY_RIGHT:
4123     case EL_BD_FIREFLY_UP:
4124     case EL_BD_FIREFLY_LEFT:
4125     case EL_BD_FIREFLY_DOWN:
4126       Feld[x][y] = EL_BD_FIREFLY;
4127       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4128       break;
4129
4130     case EL_PACMAN_RIGHT:
4131     case EL_PACMAN_UP:
4132     case EL_PACMAN_LEFT:
4133     case EL_PACMAN_DOWN:
4134       Feld[x][y] = EL_PACMAN;
4135       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4136       break;
4137
4138     case EL_YAMYAM_LEFT:
4139     case EL_YAMYAM_RIGHT:
4140     case EL_YAMYAM_UP:
4141     case EL_YAMYAM_DOWN:
4142       Feld[x][y] = EL_YAMYAM;
4143       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4144       break;
4145
4146     case EL_SP_SNIKSNAK:
4147       MovDir[x][y] = MV_UP;
4148       break;
4149
4150     case EL_SP_ELECTRON:
4151       MovDir[x][y] = MV_LEFT;
4152       break;
4153
4154     case EL_MOLE_LEFT:
4155     case EL_MOLE_RIGHT:
4156     case EL_MOLE_UP:
4157     case EL_MOLE_DOWN:
4158       Feld[x][y] = EL_MOLE;
4159       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4160       break;
4161
4162     default:
4163       if (IS_CUSTOM_ELEMENT(element))
4164       {
4165         struct ElementInfo *ei = &element_info[element];
4166         int move_direction_initial = ei->move_direction_initial;
4167         int move_pattern = ei->move_pattern;
4168
4169         if (move_direction_initial == MV_START_PREVIOUS)
4170         {
4171           if (MovDir[x][y] != MV_NONE)
4172             return;
4173
4174           move_direction_initial = MV_START_AUTOMATIC;
4175         }
4176
4177         if (move_direction_initial == MV_START_RANDOM)
4178           MovDir[x][y] = 1 << RND(4);
4179         else if (move_direction_initial & MV_ANY_DIRECTION)
4180           MovDir[x][y] = move_direction_initial;
4181         else if (move_pattern == MV_ALL_DIRECTIONS ||
4182                  move_pattern == MV_TURNING_LEFT ||
4183                  move_pattern == MV_TURNING_RIGHT ||
4184                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4185                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4186                  move_pattern == MV_TURNING_RANDOM)
4187           MovDir[x][y] = 1 << RND(4);
4188         else if (move_pattern == MV_HORIZONTAL)
4189           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4190         else if (move_pattern == MV_VERTICAL)
4191           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4192         else if (move_pattern & MV_ANY_DIRECTION)
4193           MovDir[x][y] = element_info[element].move_pattern;
4194         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4195                  move_pattern == MV_ALONG_RIGHT_SIDE)
4196         {
4197           /* use random direction as default start direction */
4198           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4199             MovDir[x][y] = 1 << RND(4);
4200
4201           for (i = 0; i < NUM_DIRECTIONS; i++)
4202           {
4203             int x1 = x + xy[i][0];
4204             int y1 = y + xy[i][1];
4205
4206             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4207             {
4208               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4209                 MovDir[x][y] = direction[0][i];
4210               else
4211                 MovDir[x][y] = direction[1][i];
4212
4213               break;
4214             }
4215           }
4216         }                
4217       }
4218       else
4219       {
4220         MovDir[x][y] = 1 << RND(4);
4221
4222         if (element != EL_BUG &&
4223             element != EL_SPACESHIP &&
4224             element != EL_BD_BUTTERFLY &&
4225             element != EL_BD_FIREFLY)
4226           break;
4227
4228         for (i = 0; i < NUM_DIRECTIONS; i++)
4229         {
4230           int x1 = x + xy[i][0];
4231           int y1 = y + xy[i][1];
4232
4233           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4234           {
4235             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4236             {
4237               MovDir[x][y] = direction[0][i];
4238               break;
4239             }
4240             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4241                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4242             {
4243               MovDir[x][y] = direction[1][i];
4244               break;
4245             }
4246           }
4247         }
4248       }
4249       break;
4250   }
4251
4252   GfxDir[x][y] = MovDir[x][y];
4253 }
4254
4255 void InitAmoebaNr(int x, int y)
4256 {
4257   int i;
4258   int group_nr = AmoebeNachbarNr(x, y);
4259
4260   if (group_nr == 0)
4261   {
4262     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4263     {
4264       if (AmoebaCnt[i] == 0)
4265       {
4266         group_nr = i;
4267         break;
4268       }
4269     }
4270   }
4271
4272   AmoebaNr[x][y] = group_nr;
4273   AmoebaCnt[group_nr]++;
4274   AmoebaCnt2[group_nr]++;
4275 }
4276
4277 static void PlayerWins(struct PlayerInfo *player)
4278 {
4279   player->LevelSolved = TRUE;
4280   player->GameOver = TRUE;
4281
4282   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4283                          level.native_em_level->lev->score : player->score);
4284 }
4285
4286 void GameWon()
4287 {
4288   static int time, time_final;
4289   static int score, score_final;
4290   static int game_over_delay_1 = 0;
4291   static int game_over_delay_2 = 0;
4292   int game_over_delay_value_1 = 50;
4293   int game_over_delay_value_2 = 50;
4294
4295   if (!local_player->LevelSolved_GameWon)
4296   {
4297     int i;
4298
4299     /* do not start end game actions before the player stops moving (to exit) */
4300     if (local_player->MovPos)
4301       return;
4302
4303     local_player->LevelSolved_GameWon = TRUE;
4304     local_player->LevelSolved_SaveTape = tape.recording;
4305     local_player->LevelSolved_SaveScore = !tape.playing;
4306
4307     if (tape.auto_play)         /* tape might already be stopped here */
4308       tape.auto_play_level_solved = TRUE;
4309
4310 #if 1
4311     TapeStop();
4312 #endif
4313
4314     game_over_delay_1 = game_over_delay_value_1;
4315     game_over_delay_2 = game_over_delay_value_2;
4316
4317     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4318     score = score_final = local_player->score_final;
4319
4320     if (TimeLeft > 0)
4321     {
4322       time_final = 0;
4323       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4324     }
4325     else if (level.time == 0 && TimePlayed < 999)
4326     {
4327       time_final = 999;
4328       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4329     }
4330
4331     local_player->score_final = score_final;
4332
4333     if (level_editor_test_game)
4334     {
4335       time = time_final;
4336       score = score_final;
4337
4338 #if 1
4339       game_panel_controls[GAME_PANEL_TIME].value = time;
4340       game_panel_controls[GAME_PANEL_SCORE].value = score;
4341
4342       DisplayGameControlValues();
4343 #else
4344       DrawGameValue_Time(time);
4345       DrawGameValue_Score(score);
4346 #endif
4347     }
4348
4349     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4350     {
4351       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4352       {
4353         /* close exit door after last player */
4354         if ((AllPlayersGone &&
4355              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4356               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4357               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4358             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4359             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4360         {
4361           int element = Feld[ExitX][ExitY];
4362
4363 #if 0
4364           if (element == EL_EM_EXIT_OPEN ||
4365               element == EL_EM_STEEL_EXIT_OPEN)
4366           {
4367             Bang(ExitX, ExitY);
4368           }
4369           else
4370 #endif
4371           {
4372             Feld[ExitX][ExitY] =
4373               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4374                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4375                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4376                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4377                EL_EM_STEEL_EXIT_CLOSING);
4378
4379             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4380           }
4381         }
4382
4383         /* player disappears */
4384         DrawLevelField(ExitX, ExitY);
4385       }
4386
4387       for (i = 0; i < MAX_PLAYERS; i++)
4388       {
4389         struct PlayerInfo *player = &stored_player[i];
4390
4391         if (player->present)
4392         {
4393           RemovePlayer(player);
4394
4395           /* player disappears */
4396           DrawLevelField(player->jx, player->jy);
4397         }
4398       }
4399     }
4400
4401     PlaySound(SND_GAME_WINNING);
4402   }
4403
4404   if (game_over_delay_1 > 0)
4405   {
4406     game_over_delay_1--;
4407
4408     return;
4409   }
4410
4411   if (time != time_final)
4412   {
4413     int time_to_go = ABS(time_final - time);
4414     int time_count_dir = (time < time_final ? +1 : -1);
4415     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4416
4417     time  += time_count_steps * time_count_dir;
4418     score += time_count_steps * level.score[SC_TIME_BONUS];
4419
4420 #if 1
4421     game_panel_controls[GAME_PANEL_TIME].value = time;
4422     game_panel_controls[GAME_PANEL_SCORE].value = score;
4423
4424     DisplayGameControlValues();
4425 #else
4426     DrawGameValue_Time(time);
4427     DrawGameValue_Score(score);
4428 #endif
4429
4430     if (time == time_final)
4431       StopSound(SND_GAME_LEVELTIME_BONUS);
4432     else if (setup.sound_loops)
4433       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4434     else
4435       PlaySound(SND_GAME_LEVELTIME_BONUS);
4436
4437     return;
4438   }
4439
4440   local_player->LevelSolved_PanelOff = TRUE;
4441
4442   if (game_over_delay_2 > 0)
4443   {
4444     game_over_delay_2--;
4445
4446     return;
4447   }
4448
4449 #if 1
4450   GameEnd();
4451 #endif
4452 }
4453
4454 void GameEnd()
4455 {
4456   int hi_pos;
4457   boolean raise_level = FALSE;
4458
4459   local_player->LevelSolved_GameEnd = TRUE;
4460
4461   CloseDoor(DOOR_CLOSE_1);
4462
4463   if (local_player->LevelSolved_SaveTape)
4464   {
4465 #if 0
4466     TapeStop();
4467 #endif
4468
4469 #if 1
4470     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4471 #else
4472     SaveTape(tape.level_nr);            /* ask to save tape */
4473 #endif
4474   }
4475
4476   if (level_editor_test_game)
4477   {
4478     game_status = GAME_MODE_MAIN;
4479
4480 #if 1
4481     DrawAndFadeInMainMenu(REDRAW_FIELD);
4482 #else
4483     DrawMainMenu();
4484 #endif
4485
4486     return;
4487   }
4488
4489   if (!local_player->LevelSolved_SaveScore)
4490   {
4491 #if 1
4492     FadeOut(REDRAW_FIELD);
4493 #endif
4494
4495     game_status = GAME_MODE_MAIN;
4496
4497     DrawAndFadeInMainMenu(REDRAW_FIELD);
4498
4499     return;
4500   }
4501
4502   if (level_nr == leveldir_current->handicap_level)
4503   {
4504     leveldir_current->handicap_level++;
4505     SaveLevelSetup_SeriesInfo();
4506   }
4507
4508   if (level_nr < leveldir_current->last_level)
4509     raise_level = TRUE;                 /* advance to next level */
4510
4511   if ((hi_pos = NewHiScore()) >= 0) 
4512   {
4513     game_status = GAME_MODE_SCORES;
4514
4515     DrawHallOfFame(hi_pos);
4516
4517     if (raise_level)
4518     {
4519       level_nr++;
4520       TapeErase();
4521     }
4522   }
4523   else
4524   {
4525 #if 1
4526     FadeOut(REDRAW_FIELD);
4527 #endif
4528
4529     game_status = GAME_MODE_MAIN;
4530
4531     if (raise_level)
4532     {
4533       level_nr++;
4534       TapeErase();
4535     }
4536
4537     DrawAndFadeInMainMenu(REDRAW_FIELD);
4538   }
4539 }
4540
4541 int NewHiScore()
4542 {
4543   int k, l;
4544   int position = -1;
4545
4546   LoadScore(level_nr);
4547
4548   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4549       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4550     return -1;
4551
4552   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4553   {
4554     if (local_player->score_final > highscore[k].Score)
4555     {
4556       /* player has made it to the hall of fame */
4557
4558       if (k < MAX_SCORE_ENTRIES - 1)
4559       {
4560         int m = MAX_SCORE_ENTRIES - 1;
4561
4562 #ifdef ONE_PER_NAME
4563         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4564           if (strEqual(setup.player_name, highscore[l].Name))
4565             m = l;
4566         if (m == k)     /* player's new highscore overwrites his old one */
4567           goto put_into_list;
4568 #endif
4569
4570         for (l = m; l > k; l--)
4571         {
4572           strcpy(highscore[l].Name, highscore[l - 1].Name);
4573           highscore[l].Score = highscore[l - 1].Score;
4574         }
4575       }
4576
4577 #ifdef ONE_PER_NAME
4578       put_into_list:
4579 #endif
4580       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4581       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4582       highscore[k].Score = local_player->score_final; 
4583       position = k;
4584       break;
4585     }
4586
4587 #ifdef ONE_PER_NAME
4588     else if (!strncmp(setup.player_name, highscore[k].Name,
4589                       MAX_PLAYER_NAME_LEN))
4590       break;    /* player already there with a higher score */
4591 #endif
4592
4593   }
4594
4595   if (position >= 0) 
4596     SaveScore(level_nr);
4597
4598   return position;
4599 }
4600
4601 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4602 {
4603   int element = Feld[x][y];
4604   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4605   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4606   int horiz_move = (dx != 0);
4607   int sign = (horiz_move ? dx : dy);
4608   int step = sign * element_info[element].move_stepsize;
4609
4610   /* special values for move stepsize for spring and things on conveyor belt */
4611   if (horiz_move)
4612   {
4613     if (CAN_FALL(element) &&
4614         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4615       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4616     else if (element == EL_SPRING)
4617       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4618   }
4619
4620   return step;
4621 }
4622
4623 inline static int getElementMoveStepsize(int x, int y)
4624 {
4625   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4626 }
4627
4628 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4629 {
4630   if (player->GfxAction != action || player->GfxDir != dir)
4631   {
4632 #if 0
4633     printf("Player frame reset! (%d => %d, %d => %d)\n",
4634            player->GfxAction, action, player->GfxDir, dir);
4635 #endif
4636
4637     player->GfxAction = action;
4638     player->GfxDir = dir;
4639     player->Frame = 0;
4640     player->StepFrame = 0;
4641   }
4642 }
4643
4644 #if USE_GFX_RESET_GFX_ANIMATION
4645 static void ResetGfxFrame(int x, int y, boolean redraw)
4646 {
4647   int element = Feld[x][y];
4648   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4649   int last_gfx_frame = GfxFrame[x][y];
4650
4651   if (graphic_info[graphic].anim_global_sync)
4652     GfxFrame[x][y] = FrameCounter;
4653   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4654     GfxFrame[x][y] = CustomValue[x][y];
4655   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4656     GfxFrame[x][y] = element_info[element].collect_score;
4657   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4658     GfxFrame[x][y] = ChangeDelay[x][y];
4659
4660   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4661     DrawLevelGraphicAnimation(x, y, graphic);
4662 }
4663 #endif
4664
4665 static void ResetGfxAnimation(int x, int y)
4666 {
4667   GfxAction[x][y] = ACTION_DEFAULT;
4668   GfxDir[x][y] = MovDir[x][y];
4669   GfxFrame[x][y] = 0;
4670
4671 #if USE_GFX_RESET_GFX_ANIMATION
4672   ResetGfxFrame(x, y, FALSE);
4673 #endif
4674 }
4675
4676 static void ResetRandomAnimationValue(int x, int y)
4677 {
4678   GfxRandom[x][y] = INIT_GFX_RANDOM();
4679 }
4680
4681 void InitMovingField(int x, int y, int direction)
4682 {
4683   int element = Feld[x][y];
4684   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4685   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4686   int newx = x + dx;
4687   int newy = y + dy;
4688   boolean is_moving_before, is_moving_after;
4689 #if 0
4690   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4691 #endif
4692
4693   /* check if element was/is moving or being moved before/after mode change */
4694 #if 1
4695 #if 1
4696   is_moving_before = (WasJustMoving[x][y] != 0);
4697 #else
4698   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4699   is_moving_before = WasJustMoving[x][y];
4700 #endif
4701 #else
4702   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4703 #endif
4704   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4705
4706   /* reset animation only for moving elements which change direction of moving
4707      or which just started or stopped moving
4708      (else CEs with property "can move" / "not moving" are reset each frame) */
4709 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4710 #if 1
4711   if (is_moving_before != is_moving_after ||
4712       direction != MovDir[x][y])
4713     ResetGfxAnimation(x, y);
4714 #else
4715   if ((is_moving_before || is_moving_after) && !continues_moving)
4716     ResetGfxAnimation(x, y);
4717 #endif
4718 #else
4719   if (!continues_moving)
4720     ResetGfxAnimation(x, y);
4721 #endif
4722
4723   MovDir[x][y] = direction;
4724   GfxDir[x][y] = direction;
4725
4726 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4727   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4728                      direction == MV_DOWN && CAN_FALL(element) ?
4729                      ACTION_FALLING : ACTION_MOVING);
4730 #else
4731   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4732                      ACTION_FALLING : ACTION_MOVING);
4733 #endif
4734
4735   /* this is needed for CEs with property "can move" / "not moving" */
4736
4737   if (is_moving_after)
4738   {
4739     if (Feld[newx][newy] == EL_EMPTY)
4740       Feld[newx][newy] = EL_BLOCKED;
4741
4742     MovDir[newx][newy] = MovDir[x][y];
4743
4744 #if USE_NEW_CUSTOM_VALUE
4745     CustomValue[newx][newy] = CustomValue[x][y];
4746 #endif
4747
4748     GfxFrame[newx][newy] = GfxFrame[x][y];
4749     GfxRandom[newx][newy] = GfxRandom[x][y];
4750     GfxAction[newx][newy] = GfxAction[x][y];
4751     GfxDir[newx][newy] = GfxDir[x][y];
4752   }
4753 }
4754
4755 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4756 {
4757   int direction = MovDir[x][y];
4758   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4759   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4760
4761   *goes_to_x = newx;
4762   *goes_to_y = newy;
4763 }
4764
4765 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4766 {
4767   int oldx = x, oldy = y;
4768   int direction = MovDir[x][y];
4769
4770   if (direction == MV_LEFT)
4771     oldx++;
4772   else if (direction == MV_RIGHT)
4773     oldx--;
4774   else if (direction == MV_UP)
4775     oldy++;
4776   else if (direction == MV_DOWN)
4777     oldy--;
4778
4779   *comes_from_x = oldx;
4780   *comes_from_y = oldy;
4781 }
4782
4783 int MovingOrBlocked2Element(int x, int y)
4784 {
4785   int element = Feld[x][y];
4786
4787   if (element == EL_BLOCKED)
4788   {
4789     int oldx, oldy;
4790
4791     Blocked2Moving(x, y, &oldx, &oldy);
4792     return Feld[oldx][oldy];
4793   }
4794   else
4795     return element;
4796 }
4797
4798 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4799 {
4800   /* like MovingOrBlocked2Element(), but if element is moving
4801      and (x,y) is the field the moving element is just leaving,
4802      return EL_BLOCKED instead of the element value */
4803   int element = Feld[x][y];
4804
4805   if (IS_MOVING(x, y))
4806   {
4807     if (element == EL_BLOCKED)
4808     {
4809       int oldx, oldy;
4810
4811       Blocked2Moving(x, y, &oldx, &oldy);
4812       return Feld[oldx][oldy];
4813     }
4814     else
4815       return EL_BLOCKED;
4816   }
4817   else
4818     return element;
4819 }
4820
4821 static void RemoveField(int x, int y)
4822 {
4823   Feld[x][y] = EL_EMPTY;
4824
4825   MovPos[x][y] = 0;
4826   MovDir[x][y] = 0;
4827   MovDelay[x][y] = 0;
4828
4829 #if USE_NEW_CUSTOM_VALUE
4830   CustomValue[x][y] = 0;
4831 #endif
4832
4833   AmoebaNr[x][y] = 0;
4834   ChangeDelay[x][y] = 0;
4835   ChangePage[x][y] = -1;
4836   Pushed[x][y] = FALSE;
4837
4838 #if 0
4839   ExplodeField[x][y] = EX_TYPE_NONE;
4840 #endif
4841
4842   GfxElement[x][y] = EL_UNDEFINED;
4843   GfxAction[x][y] = ACTION_DEFAULT;
4844   GfxDir[x][y] = MV_NONE;
4845 }
4846
4847 void RemoveMovingField(int x, int y)
4848 {
4849   int oldx = x, oldy = y, newx = x, newy = y;
4850   int element = Feld[x][y];
4851   int next_element = EL_UNDEFINED;
4852
4853   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4854     return;
4855
4856   if (IS_MOVING(x, y))
4857   {
4858     Moving2Blocked(x, y, &newx, &newy);
4859
4860     if (Feld[newx][newy] != EL_BLOCKED)
4861     {
4862       /* element is moving, but target field is not free (blocked), but
4863          already occupied by something different (example: acid pool);
4864          in this case, only remove the moving field, but not the target */
4865
4866       RemoveField(oldx, oldy);
4867
4868       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4869
4870       DrawLevelField(oldx, oldy);
4871
4872       return;
4873     }
4874   }
4875   else if (element == EL_BLOCKED)
4876   {
4877     Blocked2Moving(x, y, &oldx, &oldy);
4878     if (!IS_MOVING(oldx, oldy))
4879       return;
4880   }
4881
4882   if (element == EL_BLOCKED &&
4883       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4884        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4885        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4886        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4887        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4888        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4889     next_element = get_next_element(Feld[oldx][oldy]);
4890
4891   RemoveField(oldx, oldy);
4892   RemoveField(newx, newy);
4893
4894   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4895
4896   if (next_element != EL_UNDEFINED)
4897     Feld[oldx][oldy] = next_element;
4898
4899   DrawLevelField(oldx, oldy);
4900   DrawLevelField(newx, newy);
4901 }
4902
4903 void DrawDynamite(int x, int y)
4904 {
4905   int sx = SCREENX(x), sy = SCREENY(y);
4906   int graphic = el2img(Feld[x][y]);
4907   int frame;
4908
4909   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4910     return;
4911
4912   if (IS_WALKABLE_INSIDE(Back[x][y]))
4913     return;
4914
4915   if (Back[x][y])
4916     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4917   else if (Store[x][y])
4918     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4919
4920   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4921
4922   if (Back[x][y] || Store[x][y])
4923     DrawGraphicThruMask(sx, sy, graphic, frame);
4924   else
4925     DrawGraphic(sx, sy, graphic, frame);
4926 }
4927
4928 void CheckDynamite(int x, int y)
4929 {
4930   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4931   {
4932     MovDelay[x][y]--;
4933
4934     if (MovDelay[x][y] != 0)
4935     {
4936       DrawDynamite(x, y);
4937       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4938
4939       return;
4940     }
4941   }
4942
4943   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4944
4945   Bang(x, y);
4946 }
4947
4948 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4949 {
4950   boolean num_checked_players = 0;
4951   int i;
4952
4953   for (i = 0; i < MAX_PLAYERS; i++)
4954   {
4955     if (stored_player[i].active)
4956     {
4957       int sx = stored_player[i].jx;
4958       int sy = stored_player[i].jy;
4959
4960       if (num_checked_players == 0)
4961       {
4962         *sx1 = *sx2 = sx;
4963         *sy1 = *sy2 = sy;
4964       }
4965       else
4966       {
4967         *sx1 = MIN(*sx1, sx);
4968         *sy1 = MIN(*sy1, sy);
4969         *sx2 = MAX(*sx2, sx);
4970         *sy2 = MAX(*sy2, sy);
4971       }
4972
4973       num_checked_players++;
4974     }
4975   }
4976 }
4977
4978 static boolean checkIfAllPlayersFitToScreen_RND()
4979 {
4980   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4981
4982   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4983
4984   return (sx2 - sx1 < SCR_FIELDX &&
4985           sy2 - sy1 < SCR_FIELDY);
4986 }
4987
4988 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4989 {
4990   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4991
4992   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4993
4994   *sx = (sx1 + sx2) / 2;
4995   *sy = (sy1 + sy2) / 2;
4996 }
4997
4998 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4999                         boolean center_screen, boolean quick_relocation)
5000 {
5001   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5002   boolean no_delay = (tape.warp_forward);
5003   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5004   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5005
5006   if (quick_relocation)
5007   {
5008     int offset = game.scroll_delay_value;
5009
5010     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5011     {
5012       if (!level.shifted_relocation || center_screen)
5013       {
5014         /* quick relocation (without scrolling), with centering of screen */
5015
5016         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5017                     x > SBX_Right + MIDPOSX ? SBX_Right :
5018                     x - MIDPOSX);
5019
5020         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5021                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5022                     y - MIDPOSY);
5023       }
5024       else
5025       {
5026         /* quick relocation (without scrolling), but do not center screen */
5027
5028         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5029                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5030                                old_x - MIDPOSX);
5031
5032         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5033                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5034                                old_y - MIDPOSY);
5035
5036         int offset_x = x + (scroll_x - center_scroll_x);
5037         int offset_y = y + (scroll_y - center_scroll_y);
5038
5039         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5040                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5041                     offset_x - MIDPOSX);
5042
5043         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5044                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5045                     offset_y - MIDPOSY);
5046       }
5047     }
5048     else
5049     {
5050       /* quick relocation (without scrolling), inside visible screen area */
5051
5052       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5053           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5054         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5055
5056       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5057           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5058         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5059
5060       /* don't scroll over playfield boundaries */
5061       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5062         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5063
5064       /* don't scroll over playfield boundaries */
5065       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5066         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5067     }
5068
5069     RedrawPlayfield(TRUE, 0,0,0,0);
5070   }
5071   else
5072   {
5073 #if 1
5074     int scroll_xx, scroll_yy;
5075
5076     if (!level.shifted_relocation || center_screen)
5077     {
5078       /* visible relocation (with scrolling), with centering of screen */
5079
5080       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5081                    x > SBX_Right + MIDPOSX ? SBX_Right :
5082                    x - MIDPOSX);
5083
5084       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5085                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5086                    y - MIDPOSY);
5087     }
5088     else
5089     {
5090       /* visible relocation (with scrolling), but do not center screen */
5091
5092       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5093                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5094                              old_x - MIDPOSX);
5095
5096       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5097                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5098                              old_y - MIDPOSY);
5099
5100       int offset_x = x + (scroll_x - center_scroll_x);
5101       int offset_y = y + (scroll_y - center_scroll_y);
5102
5103       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5104                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5105                    offset_x - MIDPOSX);
5106
5107       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5108                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5109                    offset_y - MIDPOSY);
5110     }
5111
5112 #else
5113
5114     /* visible relocation (with scrolling), with centering of screen */
5115
5116     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5117                      x > SBX_Right + MIDPOSX ? SBX_Right :
5118                      x - MIDPOSX);
5119
5120     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5121                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5122                      y - MIDPOSY);
5123 #endif
5124
5125     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5126
5127     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5128     {
5129       int dx = 0, dy = 0;
5130       int fx = FX, fy = FY;
5131
5132       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5133       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5134
5135       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5136         break;
5137
5138       scroll_x -= dx;
5139       scroll_y -= dy;
5140
5141       fx += dx * TILEX / 2;
5142       fy += dy * TILEY / 2;
5143
5144       ScrollLevel(dx, dy);
5145       DrawAllPlayers();
5146
5147       /* scroll in two steps of half tile size to make things smoother */
5148       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5149       FlushDisplay();
5150       Delay(wait_delay_value);
5151
5152       /* scroll second step to align at full tile size */
5153       BackToFront();
5154       Delay(wait_delay_value);
5155     }
5156
5157     DrawAllPlayers();
5158     BackToFront();
5159     Delay(wait_delay_value);
5160   }
5161 }
5162
5163 void RelocatePlayer(int jx, int jy, int el_player_raw)
5164 {
5165   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5166   int player_nr = GET_PLAYER_NR(el_player);
5167   struct PlayerInfo *player = &stored_player[player_nr];
5168   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5169   boolean no_delay = (tape.warp_forward);
5170   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5171   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5172   int old_jx = player->jx;
5173   int old_jy = player->jy;
5174   int old_element = Feld[old_jx][old_jy];
5175   int element = Feld[jx][jy];
5176   boolean player_relocated = (old_jx != jx || old_jy != jy);
5177
5178   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5179   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5180   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5181   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5182   int leave_side_horiz = move_dir_horiz;
5183   int leave_side_vert  = move_dir_vert;
5184   int enter_side = enter_side_horiz | enter_side_vert;
5185   int leave_side = leave_side_horiz | leave_side_vert;
5186
5187   if (player->GameOver)         /* do not reanimate dead player */
5188     return;
5189
5190   if (!player_relocated)        /* no need to relocate the player */
5191     return;
5192
5193   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5194   {
5195     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5196     DrawLevelField(jx, jy);
5197   }
5198
5199   if (player->present)
5200   {
5201     while (player->MovPos)
5202     {
5203       ScrollPlayer(player, SCROLL_GO_ON);
5204       ScrollScreen(NULL, SCROLL_GO_ON);
5205
5206       AdvanceFrameAndPlayerCounters(player->index_nr);
5207
5208       DrawPlayer(player);
5209
5210       BackToFront();
5211       Delay(wait_delay_value);
5212     }
5213
5214     DrawPlayer(player);         /* needed here only to cleanup last field */
5215     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5216
5217     player->is_moving = FALSE;
5218   }
5219
5220   if (IS_CUSTOM_ELEMENT(old_element))
5221     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5222                                CE_LEFT_BY_PLAYER,
5223                                player->index_bit, leave_side);
5224
5225   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5226                                       CE_PLAYER_LEAVES_X,
5227                                       player->index_bit, leave_side);
5228
5229   Feld[jx][jy] = el_player;
5230   InitPlayerField(jx, jy, el_player, TRUE);
5231
5232   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5233   {
5234     Feld[jx][jy] = element;
5235     InitField(jx, jy, FALSE);
5236   }
5237
5238   /* only visually relocate centered player */
5239   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5240                      FALSE, level.instant_relocation);
5241
5242   TestIfPlayerTouchesBadThing(jx, jy);
5243   TestIfPlayerTouchesCustomElement(jx, jy);
5244
5245   if (IS_CUSTOM_ELEMENT(element))
5246     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5247                                player->index_bit, enter_side);
5248
5249   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5250                                       player->index_bit, enter_side);
5251 }
5252
5253 void Explode(int ex, int ey, int phase, int mode)
5254 {
5255   int x, y;
5256   int last_phase;
5257   int border_element;
5258
5259   /* !!! eliminate this variable !!! */
5260   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5261
5262   if (game.explosions_delayed)
5263   {
5264     ExplodeField[ex][ey] = mode;
5265     return;
5266   }
5267
5268   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5269   {
5270     int center_element = Feld[ex][ey];
5271     int artwork_element, explosion_element;     /* set these values later */
5272
5273 #if 0
5274     /* --- This is only really needed (and now handled) in "Impact()". --- */
5275     /* do not explode moving elements that left the explode field in time */
5276     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5277         center_element == EL_EMPTY &&
5278         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5279       return;
5280 #endif
5281
5282 #if 0
5283     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5284     if (mode == EX_TYPE_NORMAL ||
5285         mode == EX_TYPE_CENTER ||
5286         mode == EX_TYPE_CROSS)
5287       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5288 #endif
5289
5290     /* remove things displayed in background while burning dynamite */
5291     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5292       Back[ex][ey] = 0;
5293
5294     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5295     {
5296       /* put moving element to center field (and let it explode there) */
5297       center_element = MovingOrBlocked2Element(ex, ey);
5298       RemoveMovingField(ex, ey);
5299       Feld[ex][ey] = center_element;
5300     }
5301
5302     /* now "center_element" is finally determined -- set related values now */
5303     artwork_element = center_element;           /* for custom player artwork */
5304     explosion_element = center_element;         /* for custom player artwork */
5305
5306     if (IS_PLAYER(ex, ey))
5307     {
5308       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5309
5310       artwork_element = stored_player[player_nr].artwork_element;
5311
5312       if (level.use_explosion_element[player_nr])
5313       {
5314         explosion_element = level.explosion_element[player_nr];
5315         artwork_element = explosion_element;
5316       }
5317     }
5318
5319 #if 1
5320     if (mode == EX_TYPE_NORMAL ||
5321         mode == EX_TYPE_CENTER ||
5322         mode == EX_TYPE_CROSS)
5323       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5324 #endif
5325
5326     last_phase = element_info[explosion_element].explosion_delay + 1;
5327
5328     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5329     {
5330       int xx = x - ex + 1;
5331       int yy = y - ey + 1;
5332       int element;
5333
5334       if (!IN_LEV_FIELD(x, y) ||
5335           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5336           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5337         continue;
5338
5339       element = Feld[x][y];
5340
5341       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5342       {
5343         element = MovingOrBlocked2Element(x, y);
5344
5345         if (!IS_EXPLOSION_PROOF(element))
5346           RemoveMovingField(x, y);
5347       }
5348
5349       /* indestructible elements can only explode in center (but not flames) */
5350       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5351                                            mode == EX_TYPE_BORDER)) ||
5352           element == EL_FLAMES)
5353         continue;
5354
5355       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5356          behaviour, for example when touching a yamyam that explodes to rocks
5357          with active deadly shield, a rock is created under the player !!! */
5358       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5359 #if 0
5360       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5361           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5362            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5363 #else
5364       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5365 #endif
5366       {
5367         if (IS_ACTIVE_BOMB(element))
5368         {
5369           /* re-activate things under the bomb like gate or penguin */
5370           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5371           Back[x][y] = 0;
5372         }
5373
5374         continue;
5375       }
5376
5377       /* save walkable background elements while explosion on same tile */
5378       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5379           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5380         Back[x][y] = element;
5381
5382       /* ignite explodable elements reached by other explosion */
5383       if (element == EL_EXPLOSION)
5384         element = Store2[x][y];
5385
5386       if (AmoebaNr[x][y] &&
5387           (element == EL_AMOEBA_FULL ||
5388            element == EL_BD_AMOEBA ||
5389            element == EL_AMOEBA_GROWING))
5390       {
5391         AmoebaCnt[AmoebaNr[x][y]]--;
5392         AmoebaCnt2[AmoebaNr[x][y]]--;
5393       }
5394
5395       RemoveField(x, y);
5396
5397       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5398       {
5399         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5400
5401         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5402
5403         if (PLAYERINFO(ex, ey)->use_murphy)
5404           Store[x][y] = EL_EMPTY;
5405       }
5406
5407       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5408          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5409       else if (ELEM_IS_PLAYER(center_element))
5410         Store[x][y] = EL_EMPTY;
5411       else if (center_element == EL_YAMYAM)
5412         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5413       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5414         Store[x][y] = element_info[center_element].content.e[xx][yy];
5415 #if 1
5416       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5417          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5418          otherwise) -- FIX THIS !!! */
5419       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5420         Store[x][y] = element_info[element].content.e[1][1];
5421 #else
5422       else if (!CAN_EXPLODE(element))
5423         Store[x][y] = element_info[element].content.e[1][1];
5424 #endif
5425       else
5426         Store[x][y] = EL_EMPTY;
5427
5428       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5429           center_element == EL_AMOEBA_TO_DIAMOND)
5430         Store2[x][y] = element;
5431
5432       Feld[x][y] = EL_EXPLOSION;
5433       GfxElement[x][y] = artwork_element;
5434
5435       ExplodePhase[x][y] = 1;
5436       ExplodeDelay[x][y] = last_phase;
5437
5438       Stop[x][y] = TRUE;
5439     }
5440
5441     if (center_element == EL_YAMYAM)
5442       game.yamyam_content_nr =
5443         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5444
5445     return;
5446   }
5447
5448   if (Stop[ex][ey])
5449     return;
5450
5451   x = ex;
5452   y = ey;
5453
5454   if (phase == 1)
5455     GfxFrame[x][y] = 0;         /* restart explosion animation */
5456
5457   last_phase = ExplodeDelay[x][y];
5458
5459   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5460
5461 #ifdef DEBUG
5462
5463   /* activate this even in non-DEBUG version until cause for crash in
5464      getGraphicAnimationFrame() (see below) is found and eliminated */
5465
5466 #endif
5467 #if 1
5468
5469 #if 1
5470   /* this can happen if the player leaves an explosion just in time */
5471   if (GfxElement[x][y] == EL_UNDEFINED)
5472     GfxElement[x][y] = EL_EMPTY;
5473 #else
5474   if (GfxElement[x][y] == EL_UNDEFINED)
5475   {
5476     printf("\n\n");
5477     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5478     printf("Explode(): This should never happen!\n");
5479     printf("\n\n");
5480
5481     GfxElement[x][y] = EL_EMPTY;
5482   }
5483 #endif
5484
5485 #endif
5486
5487   border_element = Store2[x][y];
5488   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5489     border_element = StorePlayer[x][y];
5490
5491   if (phase == element_info[border_element].ignition_delay ||
5492       phase == last_phase)
5493   {
5494     boolean border_explosion = FALSE;
5495
5496     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5497         !PLAYER_EXPLOSION_PROTECTED(x, y))
5498     {
5499       KillPlayerUnlessExplosionProtected(x, y);
5500       border_explosion = TRUE;
5501     }
5502     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5503     {
5504       Feld[x][y] = Store2[x][y];
5505       Store2[x][y] = 0;
5506       Bang(x, y);
5507       border_explosion = TRUE;
5508     }
5509     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5510     {
5511       AmoebeUmwandeln(x, y);
5512       Store2[x][y] = 0;
5513       border_explosion = TRUE;
5514     }
5515
5516     /* if an element just explodes due to another explosion (chain-reaction),
5517        do not immediately end the new explosion when it was the last frame of
5518        the explosion (as it would be done in the following "if"-statement!) */
5519     if (border_explosion && phase == last_phase)
5520       return;
5521   }
5522
5523   if (phase == last_phase)
5524   {
5525     int element;
5526
5527     element = Feld[x][y] = Store[x][y];
5528     Store[x][y] = Store2[x][y] = 0;
5529     GfxElement[x][y] = EL_UNDEFINED;
5530
5531     /* player can escape from explosions and might therefore be still alive */
5532     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5533         element <= EL_PLAYER_IS_EXPLODING_4)
5534     {
5535       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5536       int explosion_element = EL_PLAYER_1 + player_nr;
5537       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5538       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5539
5540       if (level.use_explosion_element[player_nr])
5541         explosion_element = level.explosion_element[player_nr];
5542
5543       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5544                     element_info[explosion_element].content.e[xx][yy]);
5545     }
5546
5547     /* restore probably existing indestructible background element */
5548     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5549       element = Feld[x][y] = Back[x][y];
5550     Back[x][y] = 0;
5551
5552     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5553     GfxDir[x][y] = MV_NONE;
5554     ChangeDelay[x][y] = 0;
5555     ChangePage[x][y] = -1;
5556
5557 #if USE_NEW_CUSTOM_VALUE
5558     CustomValue[x][y] = 0;
5559 #endif
5560
5561     InitField_WithBug2(x, y, FALSE);
5562
5563     DrawLevelField(x, y);
5564
5565     TestIfElementTouchesCustomElement(x, y);
5566
5567     if (GFX_CRUMBLED(element))
5568       DrawLevelFieldCrumbledSandNeighbours(x, y);
5569
5570     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5571       StorePlayer[x][y] = 0;
5572
5573     if (ELEM_IS_PLAYER(element))
5574       RelocatePlayer(x, y, element);
5575   }
5576   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5577   {
5578     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5579     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5580
5581     if (phase == delay)
5582       DrawLevelFieldCrumbledSand(x, y);
5583
5584     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5585     {
5586       DrawLevelElement(x, y, Back[x][y]);
5587       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5588     }
5589     else if (IS_WALKABLE_UNDER(Back[x][y]))
5590     {
5591       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5592       DrawLevelElementThruMask(x, y, Back[x][y]);
5593     }
5594     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5595       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5596   }
5597 }
5598
5599 void DynaExplode(int ex, int ey)
5600 {
5601   int i, j;
5602   int dynabomb_element = Feld[ex][ey];
5603   int dynabomb_size = 1;
5604   boolean dynabomb_xl = FALSE;
5605   struct PlayerInfo *player;
5606   static int xy[4][2] =
5607   {
5608     { 0, -1 },
5609     { -1, 0 },
5610     { +1, 0 },
5611     { 0, +1 }
5612   };
5613
5614   if (IS_ACTIVE_BOMB(dynabomb_element))
5615   {
5616     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5617     dynabomb_size = player->dynabomb_size;
5618     dynabomb_xl = player->dynabomb_xl;
5619     player->dynabombs_left++;
5620   }
5621
5622   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5623
5624   for (i = 0; i < NUM_DIRECTIONS; i++)
5625   {
5626     for (j = 1; j <= dynabomb_size; j++)
5627     {
5628       int x = ex + j * xy[i][0];
5629       int y = ey + j * xy[i][1];
5630       int element;
5631
5632       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5633         break;
5634
5635       element = Feld[x][y];
5636
5637       /* do not restart explosions of fields with active bombs */
5638       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5639         continue;
5640
5641       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5642
5643       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5644           !IS_DIGGABLE(element) && !dynabomb_xl)
5645         break;
5646     }
5647   }
5648 }
5649
5650 void Bang(int x, int y)
5651 {
5652   int element = MovingOrBlocked2Element(x, y);
5653   int explosion_type = EX_TYPE_NORMAL;
5654
5655   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5656   {
5657     struct PlayerInfo *player = PLAYERINFO(x, y);
5658
5659     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5660                             player->element_nr);
5661
5662     if (level.use_explosion_element[player->index_nr])
5663     {
5664       int explosion_element = level.explosion_element[player->index_nr];
5665
5666       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5667         explosion_type = EX_TYPE_CROSS;
5668       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5669         explosion_type = EX_TYPE_CENTER;
5670     }
5671   }
5672
5673   switch (element)
5674   {
5675     case EL_BUG:
5676     case EL_SPACESHIP:
5677     case EL_BD_BUTTERFLY:
5678     case EL_BD_FIREFLY:
5679     case EL_YAMYAM:
5680     case EL_DARK_YAMYAM:
5681     case EL_ROBOT:
5682     case EL_PACMAN:
5683     case EL_MOLE:
5684       RaiseScoreElement(element);
5685       break;
5686
5687     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5688     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5689     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5690     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5691     case EL_DYNABOMB_INCREASE_NUMBER:
5692     case EL_DYNABOMB_INCREASE_SIZE:
5693     case EL_DYNABOMB_INCREASE_POWER:
5694       explosion_type = EX_TYPE_DYNA;
5695       break;
5696
5697     case EL_DC_LANDMINE:
5698 #if 0
5699     case EL_EM_EXIT_OPEN:
5700     case EL_EM_STEEL_EXIT_OPEN:
5701 #endif
5702       explosion_type = EX_TYPE_CENTER;
5703       break;
5704
5705     case EL_PENGUIN:
5706     case EL_LAMP:
5707     case EL_LAMP_ACTIVE:
5708     case EL_AMOEBA_TO_DIAMOND:
5709       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5710         explosion_type = EX_TYPE_CENTER;
5711       break;
5712
5713     default:
5714       if (element_info[element].explosion_type == EXPLODES_CROSS)
5715         explosion_type = EX_TYPE_CROSS;
5716       else if (element_info[element].explosion_type == EXPLODES_1X1)
5717         explosion_type = EX_TYPE_CENTER;
5718       break;
5719   }
5720
5721   if (explosion_type == EX_TYPE_DYNA)
5722     DynaExplode(x, y);
5723   else
5724     Explode(x, y, EX_PHASE_START, explosion_type);
5725
5726   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5727 }
5728
5729 void SplashAcid(int x, int y)
5730 {
5731   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5732       (!IN_LEV_FIELD(x - 1, y - 2) ||
5733        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5734     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5735
5736   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5737       (!IN_LEV_FIELD(x + 1, y - 2) ||
5738        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5739     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5740
5741   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5742 }
5743
5744 static void InitBeltMovement()
5745 {
5746   static int belt_base_element[4] =
5747   {
5748     EL_CONVEYOR_BELT_1_LEFT,
5749     EL_CONVEYOR_BELT_2_LEFT,
5750     EL_CONVEYOR_BELT_3_LEFT,
5751     EL_CONVEYOR_BELT_4_LEFT
5752   };
5753   static int belt_base_active_element[4] =
5754   {
5755     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5756     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5757     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5758     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5759   };
5760
5761   int x, y, i, j;
5762
5763   /* set frame order for belt animation graphic according to belt direction */
5764   for (i = 0; i < NUM_BELTS; i++)
5765   {
5766     int belt_nr = i;
5767
5768     for (j = 0; j < NUM_BELT_PARTS; j++)
5769     {
5770       int element = belt_base_active_element[belt_nr] + j;
5771       int graphic_1 = el2img(element);
5772       int graphic_2 = el2panelimg(element);
5773
5774       if (game.belt_dir[i] == MV_LEFT)
5775       {
5776         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5777         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5778       }
5779       else
5780       {
5781         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5782         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5783       }
5784     }
5785   }
5786
5787   SCAN_PLAYFIELD(x, y)
5788   {
5789     int element = Feld[x][y];
5790
5791     for (i = 0; i < NUM_BELTS; i++)
5792     {
5793       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5794       {
5795         int e_belt_nr = getBeltNrFromBeltElement(element);
5796         int belt_nr = i;
5797
5798         if (e_belt_nr == belt_nr)
5799         {
5800           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5801
5802           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5803         }
5804       }
5805     }
5806   }
5807 }
5808
5809 static void ToggleBeltSwitch(int x, int y)
5810 {
5811   static int belt_base_element[4] =
5812   {
5813     EL_CONVEYOR_BELT_1_LEFT,
5814     EL_CONVEYOR_BELT_2_LEFT,
5815     EL_CONVEYOR_BELT_3_LEFT,
5816     EL_CONVEYOR_BELT_4_LEFT
5817   };
5818   static int belt_base_active_element[4] =
5819   {
5820     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5821     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5822     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5823     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5824   };
5825   static int belt_base_switch_element[4] =
5826   {
5827     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5828     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5829     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5830     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5831   };
5832   static int belt_move_dir[4] =
5833   {
5834     MV_LEFT,
5835     MV_NONE,
5836     MV_RIGHT,
5837     MV_NONE,
5838   };
5839
5840   int element = Feld[x][y];
5841   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5842   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5843   int belt_dir = belt_move_dir[belt_dir_nr];
5844   int xx, yy, i;
5845
5846   if (!IS_BELT_SWITCH(element))
5847     return;
5848
5849   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5850   game.belt_dir[belt_nr] = belt_dir;
5851
5852   if (belt_dir_nr == 3)
5853     belt_dir_nr = 1;
5854
5855   /* set frame order for belt animation graphic according to belt direction */
5856   for (i = 0; i < NUM_BELT_PARTS; i++)
5857   {
5858     int element = belt_base_active_element[belt_nr] + i;
5859     int graphic_1 = el2img(element);
5860     int graphic_2 = el2panelimg(element);
5861
5862     if (belt_dir == MV_LEFT)
5863     {
5864       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5865       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5866     }
5867     else
5868     {
5869       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5870       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5871     }
5872   }
5873
5874   SCAN_PLAYFIELD(xx, yy)
5875   {
5876     int element = Feld[xx][yy];
5877
5878     if (IS_BELT_SWITCH(element))
5879     {
5880       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5881
5882       if (e_belt_nr == belt_nr)
5883       {
5884         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5885         DrawLevelField(xx, yy);
5886       }
5887     }
5888     else if (IS_BELT(element) && belt_dir != MV_NONE)
5889     {
5890       int e_belt_nr = getBeltNrFromBeltElement(element);
5891
5892       if (e_belt_nr == belt_nr)
5893       {
5894         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5895
5896         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5897         DrawLevelField(xx, yy);
5898       }
5899     }
5900     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5901     {
5902       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5903
5904       if (e_belt_nr == belt_nr)
5905       {
5906         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5907
5908         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5909         DrawLevelField(xx, yy);
5910       }
5911     }
5912   }
5913 }
5914
5915 static void ToggleSwitchgateSwitch(int x, int y)
5916 {
5917   int xx, yy;
5918
5919   game.switchgate_pos = !game.switchgate_pos;
5920
5921   SCAN_PLAYFIELD(xx, yy)
5922   {
5923     int element = Feld[xx][yy];
5924
5925 #if !USE_BOTH_SWITCHGATE_SWITCHES
5926     if (element == EL_SWITCHGATE_SWITCH_UP ||
5927         element == EL_SWITCHGATE_SWITCH_DOWN)
5928     {
5929       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5930       DrawLevelField(xx, yy);
5931     }
5932     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5933              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5934     {
5935       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5936       DrawLevelField(xx, yy);
5937     }
5938 #else
5939     if (element == EL_SWITCHGATE_SWITCH_UP)
5940     {
5941       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5942       DrawLevelField(xx, yy);
5943     }
5944     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5945     {
5946       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5947       DrawLevelField(xx, yy);
5948     }
5949     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5950     {
5951       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5952       DrawLevelField(xx, yy);
5953     }
5954     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5955     {
5956       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5957       DrawLevelField(xx, yy);
5958     }
5959 #endif
5960     else if (element == EL_SWITCHGATE_OPEN ||
5961              element == EL_SWITCHGATE_OPENING)
5962     {
5963       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5964
5965       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5966     }
5967     else if (element == EL_SWITCHGATE_CLOSED ||
5968              element == EL_SWITCHGATE_CLOSING)
5969     {
5970       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5971
5972       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5973     }
5974   }
5975 }
5976
5977 static int getInvisibleActiveFromInvisibleElement(int element)
5978 {
5979   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5980           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5981           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5982           element);
5983 }
5984
5985 static int getInvisibleFromInvisibleActiveElement(int element)
5986 {
5987   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5988           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5989           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5990           element);
5991 }
5992
5993 static void RedrawAllLightSwitchesAndInvisibleElements()
5994 {
5995   int x, y;
5996
5997   SCAN_PLAYFIELD(x, y)
5998   {
5999     int element = Feld[x][y];
6000
6001     if (element == EL_LIGHT_SWITCH &&
6002         game.light_time_left > 0)
6003     {
6004       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6005       DrawLevelField(x, y);
6006     }
6007     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6008              game.light_time_left == 0)
6009     {
6010       Feld[x][y] = EL_LIGHT_SWITCH;
6011       DrawLevelField(x, y);
6012     }
6013     else if (element == EL_EMC_DRIPPER &&
6014              game.light_time_left > 0)
6015     {
6016       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6017       DrawLevelField(x, y);
6018     }
6019     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6020              game.light_time_left == 0)
6021     {
6022       Feld[x][y] = EL_EMC_DRIPPER;
6023       DrawLevelField(x, y);
6024     }
6025     else if (element == EL_INVISIBLE_STEELWALL ||
6026              element == EL_INVISIBLE_WALL ||
6027              element == EL_INVISIBLE_SAND)
6028     {
6029       if (game.light_time_left > 0)
6030         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6031
6032       DrawLevelField(x, y);
6033
6034       /* uncrumble neighbour fields, if needed */
6035       if (element == EL_INVISIBLE_SAND)
6036         DrawLevelFieldCrumbledSandNeighbours(x, y);
6037     }
6038     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6039              element == EL_INVISIBLE_WALL_ACTIVE ||
6040              element == EL_INVISIBLE_SAND_ACTIVE)
6041     {
6042       if (game.light_time_left == 0)
6043         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6044
6045       DrawLevelField(x, y);
6046
6047       /* re-crumble neighbour fields, if needed */
6048       if (element == EL_INVISIBLE_SAND)
6049         DrawLevelFieldCrumbledSandNeighbours(x, y);
6050     }
6051   }
6052 }
6053
6054 static void RedrawAllInvisibleElementsForLenses()
6055 {
6056   int x, y;
6057
6058   SCAN_PLAYFIELD(x, y)
6059   {
6060     int element = Feld[x][y];
6061
6062     if (element == EL_EMC_DRIPPER &&
6063         game.lenses_time_left > 0)
6064     {
6065       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6066       DrawLevelField(x, y);
6067     }
6068     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6069              game.lenses_time_left == 0)
6070     {
6071       Feld[x][y] = EL_EMC_DRIPPER;
6072       DrawLevelField(x, y);
6073     }
6074     else if (element == EL_INVISIBLE_STEELWALL ||
6075              element == EL_INVISIBLE_WALL ||
6076              element == EL_INVISIBLE_SAND)
6077     {
6078       if (game.lenses_time_left > 0)
6079         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6080
6081       DrawLevelField(x, y);
6082
6083       /* uncrumble neighbour fields, if needed */
6084       if (element == EL_INVISIBLE_SAND)
6085         DrawLevelFieldCrumbledSandNeighbours(x, y);
6086     }
6087     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6088              element == EL_INVISIBLE_WALL_ACTIVE ||
6089              element == EL_INVISIBLE_SAND_ACTIVE)
6090     {
6091       if (game.lenses_time_left == 0)
6092         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6093
6094       DrawLevelField(x, y);
6095
6096       /* re-crumble neighbour fields, if needed */
6097       if (element == EL_INVISIBLE_SAND)
6098         DrawLevelFieldCrumbledSandNeighbours(x, y);
6099     }
6100   }
6101 }
6102
6103 static void RedrawAllInvisibleElementsForMagnifier()
6104 {
6105   int x, y;
6106
6107   SCAN_PLAYFIELD(x, y)
6108   {
6109     int element = Feld[x][y];
6110
6111     if (element == EL_EMC_FAKE_GRASS &&
6112         game.magnify_time_left > 0)
6113     {
6114       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6115       DrawLevelField(x, y);
6116     }
6117     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6118              game.magnify_time_left == 0)
6119     {
6120       Feld[x][y] = EL_EMC_FAKE_GRASS;
6121       DrawLevelField(x, y);
6122     }
6123     else if (IS_GATE_GRAY(element) &&
6124              game.magnify_time_left > 0)
6125     {
6126       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6127                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6128                     IS_EM_GATE_GRAY(element) ?
6129                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6130                     IS_EMC_GATE_GRAY(element) ?
6131                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6132                     element);
6133       DrawLevelField(x, y);
6134     }
6135     else if (IS_GATE_GRAY_ACTIVE(element) &&
6136              game.magnify_time_left == 0)
6137     {
6138       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6139                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6140                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6141                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6142                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6143                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6144                     element);
6145       DrawLevelField(x, y);
6146     }
6147   }
6148 }
6149
6150 static void ToggleLightSwitch(int x, int y)
6151 {
6152   int element = Feld[x][y];
6153
6154   game.light_time_left =
6155     (element == EL_LIGHT_SWITCH ?
6156      level.time_light * FRAMES_PER_SECOND : 0);
6157
6158   RedrawAllLightSwitchesAndInvisibleElements();
6159 }
6160
6161 static void ActivateTimegateSwitch(int x, int y)
6162 {
6163   int xx, yy;
6164
6165   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6166
6167   SCAN_PLAYFIELD(xx, yy)
6168   {
6169     int element = Feld[xx][yy];
6170
6171     if (element == EL_TIMEGATE_CLOSED ||
6172         element == EL_TIMEGATE_CLOSING)
6173     {
6174       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6175       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6176     }
6177
6178     /*
6179     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6180     {
6181       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6182       DrawLevelField(xx, yy);
6183     }
6184     */
6185
6186   }
6187
6188 #if 1
6189   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6190                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6191 #else
6192   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6193 #endif
6194 }
6195
6196 void Impact(int x, int y)
6197 {
6198   boolean last_line = (y == lev_fieldy - 1);
6199   boolean object_hit = FALSE;
6200   boolean impact = (last_line || object_hit);
6201   int element = Feld[x][y];
6202   int smashed = EL_STEELWALL;
6203
6204   if (!last_line)       /* check if element below was hit */
6205   {
6206     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6207       return;
6208
6209     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6210                                          MovDir[x][y + 1] != MV_DOWN ||
6211                                          MovPos[x][y + 1] <= TILEY / 2));
6212
6213     /* do not smash moving elements that left the smashed field in time */
6214     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6215         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6216       object_hit = FALSE;
6217
6218 #if USE_QUICKSAND_IMPACT_BUGFIX
6219     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6220     {
6221       RemoveMovingField(x, y + 1);
6222       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6223       Feld[x][y + 2] = EL_ROCK;
6224       DrawLevelField(x, y + 2);
6225
6226       object_hit = TRUE;
6227     }
6228
6229     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6230     {
6231       RemoveMovingField(x, y + 1);
6232       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6233       Feld[x][y + 2] = EL_ROCK;
6234       DrawLevelField(x, y + 2);
6235
6236       object_hit = TRUE;
6237     }
6238 #endif
6239
6240     if (object_hit)
6241       smashed = MovingOrBlocked2Element(x, y + 1);
6242
6243     impact = (last_line || object_hit);
6244   }
6245
6246   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6247   {
6248     SplashAcid(x, y + 1);
6249     return;
6250   }
6251
6252   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6253   /* only reset graphic animation if graphic really changes after impact */
6254   if (impact &&
6255       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6256   {
6257     ResetGfxAnimation(x, y);
6258     DrawLevelField(x, y);
6259   }
6260
6261   if (impact && CAN_EXPLODE_IMPACT(element))
6262   {
6263     Bang(x, y);
6264     return;
6265   }
6266   else if (impact && element == EL_PEARL &&
6267            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6268   {
6269     ResetGfxAnimation(x, y);
6270
6271     Feld[x][y] = EL_PEARL_BREAKING;
6272     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6273     return;
6274   }
6275   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6276   {
6277     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6278
6279     return;
6280   }
6281
6282   if (impact && element == EL_AMOEBA_DROP)
6283   {
6284     if (object_hit && IS_PLAYER(x, y + 1))
6285       KillPlayerUnlessEnemyProtected(x, y + 1);
6286     else if (object_hit && smashed == EL_PENGUIN)
6287       Bang(x, y + 1);
6288     else
6289     {
6290       Feld[x][y] = EL_AMOEBA_GROWING;
6291       Store[x][y] = EL_AMOEBA_WET;
6292
6293       ResetRandomAnimationValue(x, y);
6294     }
6295     return;
6296   }
6297
6298   if (object_hit)               /* check which object was hit */
6299   {
6300     if ((CAN_PASS_MAGIC_WALL(element) && 
6301          (smashed == EL_MAGIC_WALL ||
6302           smashed == EL_BD_MAGIC_WALL)) ||
6303         (CAN_PASS_DC_MAGIC_WALL(element) &&
6304          smashed == EL_DC_MAGIC_WALL))
6305     {
6306       int xx, yy;
6307       int activated_magic_wall =
6308         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6309          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6310          EL_DC_MAGIC_WALL_ACTIVE);
6311
6312       /* activate magic wall / mill */
6313       SCAN_PLAYFIELD(xx, yy)
6314       {
6315         if (Feld[xx][yy] == smashed)
6316           Feld[xx][yy] = activated_magic_wall;
6317       }
6318
6319       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6320       game.magic_wall_active = TRUE;
6321
6322       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6323                             SND_MAGIC_WALL_ACTIVATING :
6324                             smashed == EL_BD_MAGIC_WALL ?
6325                             SND_BD_MAGIC_WALL_ACTIVATING :
6326                             SND_DC_MAGIC_WALL_ACTIVATING));
6327     }
6328
6329     if (IS_PLAYER(x, y + 1))
6330     {
6331       if (CAN_SMASH_PLAYER(element))
6332       {
6333         KillPlayerUnlessEnemyProtected(x, y + 1);
6334         return;
6335       }
6336     }
6337     else if (smashed == EL_PENGUIN)
6338     {
6339       if (CAN_SMASH_PLAYER(element))
6340       {
6341         Bang(x, y + 1);
6342         return;
6343       }
6344     }
6345     else if (element == EL_BD_DIAMOND)
6346     {
6347       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6348       {
6349         Bang(x, y + 1);
6350         return;
6351       }
6352     }
6353     else if (((element == EL_SP_INFOTRON ||
6354                element == EL_SP_ZONK) &&
6355               (smashed == EL_SP_SNIKSNAK ||
6356                smashed == EL_SP_ELECTRON ||
6357                smashed == EL_SP_DISK_ORANGE)) ||
6358              (element == EL_SP_INFOTRON &&
6359               smashed == EL_SP_DISK_YELLOW))
6360     {
6361       Bang(x, y + 1);
6362       return;
6363     }
6364     else if (CAN_SMASH_EVERYTHING(element))
6365     {
6366       if (IS_CLASSIC_ENEMY(smashed) ||
6367           CAN_EXPLODE_SMASHED(smashed))
6368       {
6369         Bang(x, y + 1);
6370         return;
6371       }
6372       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6373       {
6374         if (smashed == EL_LAMP ||
6375             smashed == EL_LAMP_ACTIVE)
6376         {
6377           Bang(x, y + 1);
6378           return;
6379         }
6380         else if (smashed == EL_NUT)
6381         {
6382           Feld[x][y + 1] = EL_NUT_BREAKING;
6383           PlayLevelSound(x, y, SND_NUT_BREAKING);
6384           RaiseScoreElement(EL_NUT);
6385           return;
6386         }
6387         else if (smashed == EL_PEARL)
6388         {
6389           ResetGfxAnimation(x, y);
6390
6391           Feld[x][y + 1] = EL_PEARL_BREAKING;
6392           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6393           return;
6394         }
6395         else if (smashed == EL_DIAMOND)
6396         {
6397           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6398           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6399           return;
6400         }
6401         else if (IS_BELT_SWITCH(smashed))
6402         {
6403           ToggleBeltSwitch(x, y + 1);
6404         }
6405         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6406                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6407                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6408                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6409         {
6410           ToggleSwitchgateSwitch(x, y + 1);
6411         }
6412         else if (smashed == EL_LIGHT_SWITCH ||
6413                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6414         {
6415           ToggleLightSwitch(x, y + 1);
6416         }
6417         else
6418         {
6419 #if 0
6420           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6421 #endif
6422
6423           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6424
6425           CheckElementChangeBySide(x, y + 1, smashed, element,
6426                                    CE_SWITCHED, CH_SIDE_TOP);
6427           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6428                                             CH_SIDE_TOP);
6429         }
6430       }
6431       else
6432       {
6433         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6434       }
6435     }
6436   }
6437
6438   /* play sound of magic wall / mill */
6439   if (!last_line &&
6440       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6441        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6442        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6443   {
6444     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6445       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6446     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6447       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6448     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6449       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6450
6451     return;
6452   }
6453
6454   /* play sound of object that hits the ground */
6455   if (last_line || object_hit)
6456     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6457 }
6458
6459 inline static void TurnRoundExt(int x, int y)
6460 {
6461   static struct
6462   {
6463     int dx, dy;
6464   } move_xy[] =
6465   {
6466     {  0,  0 },
6467     { -1,  0 },
6468     { +1,  0 },
6469     {  0,  0 },
6470     {  0, -1 },
6471     {  0,  0 }, { 0, 0 }, { 0, 0 },
6472     {  0, +1 }
6473   };
6474   static struct
6475   {
6476     int left, right, back;
6477   } turn[] =
6478   {
6479     { 0,        0,              0        },
6480     { MV_DOWN,  MV_UP,          MV_RIGHT },
6481     { MV_UP,    MV_DOWN,        MV_LEFT  },
6482     { 0,        0,              0        },
6483     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6484     { 0,        0,              0        },
6485     { 0,        0,              0        },
6486     { 0,        0,              0        },
6487     { MV_RIGHT, MV_LEFT,        MV_UP    }
6488   };
6489
6490   int element = Feld[x][y];
6491   int move_pattern = element_info[element].move_pattern;
6492
6493   int old_move_dir = MovDir[x][y];
6494   int left_dir  = turn[old_move_dir].left;
6495   int right_dir = turn[old_move_dir].right;
6496   int back_dir  = turn[old_move_dir].back;
6497
6498   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6499   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6500   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6501   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6502
6503   int left_x  = x + left_dx,  left_y  = y + left_dy;
6504   int right_x = x + right_dx, right_y = y + right_dy;
6505   int move_x  = x + move_dx,  move_y  = y + move_dy;
6506
6507   int xx, yy;
6508
6509   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6510   {
6511     TestIfBadThingTouchesOtherBadThing(x, y);
6512
6513     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6514       MovDir[x][y] = right_dir;
6515     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6516       MovDir[x][y] = left_dir;
6517
6518     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6519       MovDelay[x][y] = 9;
6520     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6521       MovDelay[x][y] = 1;
6522   }
6523   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6524   {
6525     TestIfBadThingTouchesOtherBadThing(x, y);
6526
6527     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6528       MovDir[x][y] = left_dir;
6529     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6530       MovDir[x][y] = right_dir;
6531
6532     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6533       MovDelay[x][y] = 9;
6534     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6535       MovDelay[x][y] = 1;
6536   }
6537   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6538   {
6539     TestIfBadThingTouchesOtherBadThing(x, y);
6540
6541     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6542       MovDir[x][y] = left_dir;
6543     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6544       MovDir[x][y] = right_dir;
6545
6546     if (MovDir[x][y] != old_move_dir)
6547       MovDelay[x][y] = 9;
6548   }
6549   else if (element == EL_YAMYAM)
6550   {
6551     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6552     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6553
6554     if (can_turn_left && can_turn_right)
6555       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6556     else if (can_turn_left)
6557       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6558     else if (can_turn_right)
6559       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6560     else
6561       MovDir[x][y] = back_dir;
6562
6563     MovDelay[x][y] = 16 + 16 * RND(3);
6564   }
6565   else if (element == EL_DARK_YAMYAM)
6566   {
6567     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6568                                                          left_x, left_y);
6569     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6570                                                          right_x, right_y);
6571
6572     if (can_turn_left && can_turn_right)
6573       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6574     else if (can_turn_left)
6575       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6576     else if (can_turn_right)
6577       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6578     else
6579       MovDir[x][y] = back_dir;
6580
6581     MovDelay[x][y] = 16 + 16 * RND(3);
6582   }
6583   else if (element == EL_PACMAN)
6584   {
6585     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6586     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6587
6588     if (can_turn_left && can_turn_right)
6589       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6590     else if (can_turn_left)
6591       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6592     else if (can_turn_right)
6593       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6594     else
6595       MovDir[x][y] = back_dir;
6596
6597     MovDelay[x][y] = 6 + RND(40);
6598   }
6599   else if (element == EL_PIG)
6600   {
6601     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6602     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6603     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6604     boolean should_turn_left, should_turn_right, should_move_on;
6605     int rnd_value = 24;
6606     int rnd = RND(rnd_value);
6607
6608     should_turn_left = (can_turn_left &&
6609                         (!can_move_on ||
6610                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6611                                                    y + back_dy + left_dy)));
6612     should_turn_right = (can_turn_right &&
6613                          (!can_move_on ||
6614                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6615                                                     y + back_dy + right_dy)));
6616     should_move_on = (can_move_on &&
6617                       (!can_turn_left ||
6618                        !can_turn_right ||
6619                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6620                                                  y + move_dy + left_dy) ||
6621                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6622                                                  y + move_dy + right_dy)));
6623
6624     if (should_turn_left || should_turn_right || should_move_on)
6625     {
6626       if (should_turn_left && should_turn_right && should_move_on)
6627         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6628                         rnd < 2 * rnd_value / 3 ? right_dir :
6629                         old_move_dir);
6630       else if (should_turn_left && should_turn_right)
6631         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6632       else if (should_turn_left && should_move_on)
6633         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6634       else if (should_turn_right && should_move_on)
6635         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6636       else if (should_turn_left)
6637         MovDir[x][y] = left_dir;
6638       else if (should_turn_right)
6639         MovDir[x][y] = right_dir;
6640       else if (should_move_on)
6641         MovDir[x][y] = old_move_dir;
6642     }
6643     else if (can_move_on && rnd > rnd_value / 8)
6644       MovDir[x][y] = old_move_dir;
6645     else if (can_turn_left && can_turn_right)
6646       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6647     else if (can_turn_left && rnd > rnd_value / 8)
6648       MovDir[x][y] = left_dir;
6649     else if (can_turn_right && rnd > rnd_value/8)
6650       MovDir[x][y] = right_dir;
6651     else
6652       MovDir[x][y] = back_dir;
6653
6654     xx = x + move_xy[MovDir[x][y]].dx;
6655     yy = y + move_xy[MovDir[x][y]].dy;
6656
6657     if (!IN_LEV_FIELD(xx, yy) ||
6658         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6659       MovDir[x][y] = old_move_dir;
6660
6661     MovDelay[x][y] = 0;
6662   }
6663   else if (element == EL_DRAGON)
6664   {
6665     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6666     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6667     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6668     int rnd_value = 24;
6669     int rnd = RND(rnd_value);
6670
6671     if (can_move_on && rnd > rnd_value / 8)
6672       MovDir[x][y] = old_move_dir;
6673     else if (can_turn_left && can_turn_right)
6674       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6675     else if (can_turn_left && rnd > rnd_value / 8)
6676       MovDir[x][y] = left_dir;
6677     else if (can_turn_right && rnd > rnd_value / 8)
6678       MovDir[x][y] = right_dir;
6679     else
6680       MovDir[x][y] = back_dir;
6681
6682     xx = x + move_xy[MovDir[x][y]].dx;
6683     yy = y + move_xy[MovDir[x][y]].dy;
6684
6685     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6686       MovDir[x][y] = old_move_dir;
6687
6688     MovDelay[x][y] = 0;
6689   }
6690   else if (element == EL_MOLE)
6691   {
6692     boolean can_move_on =
6693       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6694                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6695                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6696     if (!can_move_on)
6697     {
6698       boolean can_turn_left =
6699         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6700                               IS_AMOEBOID(Feld[left_x][left_y])));
6701
6702       boolean can_turn_right =
6703         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6704                               IS_AMOEBOID(Feld[right_x][right_y])));
6705
6706       if (can_turn_left && can_turn_right)
6707         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6708       else if (can_turn_left)
6709         MovDir[x][y] = left_dir;
6710       else
6711         MovDir[x][y] = right_dir;
6712     }
6713
6714     if (MovDir[x][y] != old_move_dir)
6715       MovDelay[x][y] = 9;
6716   }
6717   else if (element == EL_BALLOON)
6718   {
6719     MovDir[x][y] = game.wind_direction;
6720     MovDelay[x][y] = 0;
6721   }
6722   else if (element == EL_SPRING)
6723   {
6724 #if USE_NEW_SPRING_BUMPER
6725     if (MovDir[x][y] & MV_HORIZONTAL)
6726     {
6727       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6728           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6729       {
6730         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6731         ResetGfxAnimation(move_x, move_y);
6732         DrawLevelField(move_x, move_y);
6733
6734         MovDir[x][y] = back_dir;
6735       }
6736       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6737                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6738         MovDir[x][y] = MV_NONE;
6739     }
6740 #else
6741     if (MovDir[x][y] & MV_HORIZONTAL &&
6742         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6743          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6744       MovDir[x][y] = MV_NONE;
6745 #endif
6746
6747     MovDelay[x][y] = 0;
6748   }
6749   else if (element == EL_ROBOT ||
6750            element == EL_SATELLITE ||
6751            element == EL_PENGUIN ||
6752            element == EL_EMC_ANDROID)
6753   {
6754     int attr_x = -1, attr_y = -1;
6755
6756     if (AllPlayersGone)
6757     {
6758       attr_x = ExitX;
6759       attr_y = ExitY;
6760     }
6761     else
6762     {
6763       int i;
6764
6765       for (i = 0; i < MAX_PLAYERS; i++)
6766       {
6767         struct PlayerInfo *player = &stored_player[i];
6768         int jx = player->jx, jy = player->jy;
6769
6770         if (!player->active)
6771           continue;
6772
6773         if (attr_x == -1 ||
6774             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6775         {
6776           attr_x = jx;
6777           attr_y = jy;
6778         }
6779       }
6780     }
6781
6782     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6783         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6784          game.engine_version < VERSION_IDENT(3,1,0,0)))
6785     {
6786       attr_x = ZX;
6787       attr_y = ZY;
6788     }
6789
6790     if (element == EL_PENGUIN)
6791     {
6792       int i;
6793       static int xy[4][2] =
6794       {
6795         { 0, -1 },
6796         { -1, 0 },
6797         { +1, 0 },
6798         { 0, +1 }
6799       };
6800
6801       for (i = 0; i < NUM_DIRECTIONS; i++)
6802       {
6803         int ex = x + xy[i][0];
6804         int ey = y + xy[i][1];
6805
6806         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6807                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6808                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6809                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6810         {
6811           attr_x = ex;
6812           attr_y = ey;
6813           break;
6814         }
6815       }
6816     }
6817
6818     MovDir[x][y] = MV_NONE;
6819     if (attr_x < x)
6820       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6821     else if (attr_x > x)
6822       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6823     if (attr_y < y)
6824       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6825     else if (attr_y > y)
6826       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6827
6828     if (element == EL_ROBOT)
6829     {
6830       int newx, newy;
6831
6832       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6833         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6834       Moving2Blocked(x, y, &newx, &newy);
6835
6836       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6837         MovDelay[x][y] = 8 + 8 * !RND(3);
6838       else
6839         MovDelay[x][y] = 16;
6840     }
6841     else if (element == EL_PENGUIN)
6842     {
6843       int newx, newy;
6844
6845       MovDelay[x][y] = 1;
6846
6847       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6848       {
6849         boolean first_horiz = RND(2);
6850         int new_move_dir = MovDir[x][y];
6851
6852         MovDir[x][y] =
6853           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6854         Moving2Blocked(x, y, &newx, &newy);
6855
6856         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6857           return;
6858
6859         MovDir[x][y] =
6860           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6861         Moving2Blocked(x, y, &newx, &newy);
6862
6863         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6864           return;
6865
6866         MovDir[x][y] = old_move_dir;
6867         return;
6868       }
6869     }
6870     else if (element == EL_SATELLITE)
6871     {
6872       int newx, newy;
6873
6874       MovDelay[x][y] = 1;
6875
6876       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6877       {
6878         boolean first_horiz = RND(2);
6879         int new_move_dir = MovDir[x][y];
6880
6881         MovDir[x][y] =
6882           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6883         Moving2Blocked(x, y, &newx, &newy);
6884
6885         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6886           return;
6887
6888         MovDir[x][y] =
6889           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6890         Moving2Blocked(x, y, &newx, &newy);
6891
6892         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6893           return;
6894
6895         MovDir[x][y] = old_move_dir;
6896         return;
6897       }
6898     }
6899     else if (element == EL_EMC_ANDROID)
6900     {
6901       static int check_pos[16] =
6902       {
6903         -1,             /*  0 => (invalid)          */
6904         7,              /*  1 => MV_LEFT            */
6905         3,              /*  2 => MV_RIGHT           */
6906         -1,             /*  3 => (invalid)          */
6907         1,              /*  4 =>            MV_UP   */
6908         0,              /*  5 => MV_LEFT  | MV_UP   */
6909         2,              /*  6 => MV_RIGHT | MV_UP   */
6910         -1,             /*  7 => (invalid)          */
6911         5,              /*  8 =>            MV_DOWN */
6912         6,              /*  9 => MV_LEFT  | MV_DOWN */
6913         4,              /* 10 => MV_RIGHT | MV_DOWN */
6914         -1,             /* 11 => (invalid)          */
6915         -1,             /* 12 => (invalid)          */
6916         -1,             /* 13 => (invalid)          */
6917         -1,             /* 14 => (invalid)          */
6918         -1,             /* 15 => (invalid)          */
6919       };
6920       static struct
6921       {
6922         int dx, dy;
6923         int dir;
6924       } check_xy[8] =
6925       {
6926         { -1, -1,       MV_LEFT  | MV_UP   },
6927         {  0, -1,                  MV_UP   },
6928         { +1, -1,       MV_RIGHT | MV_UP   },
6929         { +1,  0,       MV_RIGHT           },
6930         { +1, +1,       MV_RIGHT | MV_DOWN },
6931         {  0, +1,                  MV_DOWN },
6932         { -1, +1,       MV_LEFT  | MV_DOWN },
6933         { -1,  0,       MV_LEFT            },
6934       };
6935       int start_pos, check_order;
6936       boolean can_clone = FALSE;
6937       int i;
6938
6939       /* check if there is any free field around current position */
6940       for (i = 0; i < 8; i++)
6941       {
6942         int newx = x + check_xy[i].dx;
6943         int newy = y + check_xy[i].dy;
6944
6945         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6946         {
6947           can_clone = TRUE;
6948
6949           break;
6950         }
6951       }
6952
6953       if (can_clone)            /* randomly find an element to clone */
6954       {
6955         can_clone = FALSE;
6956
6957         start_pos = check_pos[RND(8)];
6958         check_order = (RND(2) ? -1 : +1);
6959
6960         for (i = 0; i < 8; i++)
6961         {
6962           int pos_raw = start_pos + i * check_order;
6963           int pos = (pos_raw + 8) % 8;
6964           int newx = x + check_xy[pos].dx;
6965           int newy = y + check_xy[pos].dy;
6966
6967           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6968           {
6969             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6970             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6971
6972             Store[x][y] = Feld[newx][newy];
6973
6974             can_clone = TRUE;
6975
6976             break;
6977           }
6978         }
6979       }
6980
6981       if (can_clone)            /* randomly find a direction to move */
6982       {
6983         can_clone = FALSE;
6984
6985         start_pos = check_pos[RND(8)];
6986         check_order = (RND(2) ? -1 : +1);
6987
6988         for (i = 0; i < 8; i++)
6989         {
6990           int pos_raw = start_pos + i * check_order;
6991           int pos = (pos_raw + 8) % 8;
6992           int newx = x + check_xy[pos].dx;
6993           int newy = y + check_xy[pos].dy;
6994           int new_move_dir = check_xy[pos].dir;
6995
6996           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6997           {
6998             MovDir[x][y] = new_move_dir;
6999             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7000
7001             can_clone = TRUE;
7002
7003             break;
7004           }
7005         }
7006       }
7007
7008       if (can_clone)            /* cloning and moving successful */
7009         return;
7010
7011       /* cannot clone -- try to move towards player */
7012
7013       start_pos = check_pos[MovDir[x][y] & 0x0f];
7014       check_order = (RND(2) ? -1 : +1);
7015
7016       for (i = 0; i < 3; i++)
7017       {
7018         /* first check start_pos, then previous/next or (next/previous) pos */
7019         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7020         int pos = (pos_raw + 8) % 8;
7021         int newx = x + check_xy[pos].dx;
7022         int newy = y + check_xy[pos].dy;
7023         int new_move_dir = check_xy[pos].dir;
7024
7025         if (IS_PLAYER(newx, newy))
7026           break;
7027
7028         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7029         {
7030           MovDir[x][y] = new_move_dir;
7031           MovDelay[x][y] = level.android_move_time * 8 + 1;
7032
7033           break;
7034         }
7035       }
7036     }
7037   }
7038   else if (move_pattern == MV_TURNING_LEFT ||
7039            move_pattern == MV_TURNING_RIGHT ||
7040            move_pattern == MV_TURNING_LEFT_RIGHT ||
7041            move_pattern == MV_TURNING_RIGHT_LEFT ||
7042            move_pattern == MV_TURNING_RANDOM ||
7043            move_pattern == MV_ALL_DIRECTIONS)
7044   {
7045     boolean can_turn_left =
7046       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7047     boolean can_turn_right =
7048       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7049
7050     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7051       return;
7052
7053     if (move_pattern == MV_TURNING_LEFT)
7054       MovDir[x][y] = left_dir;
7055     else if (move_pattern == MV_TURNING_RIGHT)
7056       MovDir[x][y] = right_dir;
7057     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7058       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7059     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7060       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7061     else if (move_pattern == MV_TURNING_RANDOM)
7062       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7063                       can_turn_right && !can_turn_left ? right_dir :
7064                       RND(2) ? left_dir : right_dir);
7065     else if (can_turn_left && can_turn_right)
7066       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7067     else if (can_turn_left)
7068       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7069     else if (can_turn_right)
7070       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7071     else
7072       MovDir[x][y] = back_dir;
7073
7074     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7075   }
7076   else if (move_pattern == MV_HORIZONTAL ||
7077            move_pattern == MV_VERTICAL)
7078   {
7079     if (move_pattern & old_move_dir)
7080       MovDir[x][y] = back_dir;
7081     else if (move_pattern == MV_HORIZONTAL)
7082       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7083     else if (move_pattern == MV_VERTICAL)
7084       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7085
7086     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7087   }
7088   else if (move_pattern & MV_ANY_DIRECTION)
7089   {
7090     MovDir[x][y] = move_pattern;
7091     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7092   }
7093   else if (move_pattern & MV_WIND_DIRECTION)
7094   {
7095     MovDir[x][y] = game.wind_direction;
7096     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7097   }
7098   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7099   {
7100     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7101       MovDir[x][y] = left_dir;
7102     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7103       MovDir[x][y] = right_dir;
7104
7105     if (MovDir[x][y] != old_move_dir)
7106       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7107   }
7108   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7109   {
7110     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7111       MovDir[x][y] = right_dir;
7112     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7113       MovDir[x][y] = left_dir;
7114
7115     if (MovDir[x][y] != old_move_dir)
7116       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7117   }
7118   else if (move_pattern == MV_TOWARDS_PLAYER ||
7119            move_pattern == MV_AWAY_FROM_PLAYER)
7120   {
7121     int attr_x = -1, attr_y = -1;
7122     int newx, newy;
7123     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7124
7125     if (AllPlayersGone)
7126     {
7127       attr_x = ExitX;
7128       attr_y = ExitY;
7129     }
7130     else
7131     {
7132       int i;
7133
7134       for (i = 0; i < MAX_PLAYERS; i++)
7135       {
7136         struct PlayerInfo *player = &stored_player[i];
7137         int jx = player->jx, jy = player->jy;
7138
7139         if (!player->active)
7140           continue;
7141
7142         if (attr_x == -1 ||
7143             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7144         {
7145           attr_x = jx;
7146           attr_y = jy;
7147         }
7148       }
7149     }
7150
7151     MovDir[x][y] = MV_NONE;
7152     if (attr_x < x)
7153       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7154     else if (attr_x > x)
7155       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7156     if (attr_y < y)
7157       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7158     else if (attr_y > y)
7159       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7160
7161     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162
7163     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7164     {
7165       boolean first_horiz = RND(2);
7166       int new_move_dir = MovDir[x][y];
7167
7168       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7169       {
7170         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7171         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7172
7173         return;
7174       }
7175
7176       MovDir[x][y] =
7177         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178       Moving2Blocked(x, y, &newx, &newy);
7179
7180       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7181         return;
7182
7183       MovDir[x][y] =
7184         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185       Moving2Blocked(x, y, &newx, &newy);
7186
7187       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7188         return;
7189
7190       MovDir[x][y] = old_move_dir;
7191     }
7192   }
7193   else if (move_pattern == MV_WHEN_PUSHED ||
7194            move_pattern == MV_WHEN_DROPPED)
7195   {
7196     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7197       MovDir[x][y] = MV_NONE;
7198
7199     MovDelay[x][y] = 0;
7200   }
7201   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7202   {
7203     static int test_xy[7][2] =
7204     {
7205       { 0, -1 },
7206       { -1, 0 },
7207       { +1, 0 },
7208       { 0, +1 },
7209       { 0, -1 },
7210       { -1, 0 },
7211       { +1, 0 },
7212     };
7213     static int test_dir[7] =
7214     {
7215       MV_UP,
7216       MV_LEFT,
7217       MV_RIGHT,
7218       MV_DOWN,
7219       MV_UP,
7220       MV_LEFT,
7221       MV_RIGHT,
7222     };
7223     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7224     int move_preference = -1000000;     /* start with very low preference */
7225     int new_move_dir = MV_NONE;
7226     int start_test = RND(4);
7227     int i;
7228
7229     for (i = 0; i < NUM_DIRECTIONS; i++)
7230     {
7231       int move_dir = test_dir[start_test + i];
7232       int move_dir_preference;
7233
7234       xx = x + test_xy[start_test + i][0];
7235       yy = y + test_xy[start_test + i][1];
7236
7237       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7238           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7239       {
7240         new_move_dir = move_dir;
7241
7242         break;
7243       }
7244
7245       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7246         continue;
7247
7248       move_dir_preference = -1 * RunnerVisit[xx][yy];
7249       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7250         move_dir_preference = PlayerVisit[xx][yy];
7251
7252       if (move_dir_preference > move_preference)
7253       {
7254         /* prefer field that has not been visited for the longest time */
7255         move_preference = move_dir_preference;
7256         new_move_dir = move_dir;
7257       }
7258       else if (move_dir_preference == move_preference &&
7259                move_dir == old_move_dir)
7260       {
7261         /* prefer last direction when all directions are preferred equally */
7262         move_preference = move_dir_preference;
7263         new_move_dir = move_dir;
7264       }
7265     }
7266
7267     MovDir[x][y] = new_move_dir;
7268     if (old_move_dir != new_move_dir)
7269       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7270   }
7271 }
7272
7273 static void TurnRound(int x, int y)
7274 {
7275   int direction = MovDir[x][y];
7276
7277   TurnRoundExt(x, y);
7278
7279   GfxDir[x][y] = MovDir[x][y];
7280
7281   if (direction != MovDir[x][y])
7282     GfxFrame[x][y] = 0;
7283
7284   if (MovDelay[x][y])
7285     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7286
7287   ResetGfxFrame(x, y, FALSE);
7288 }
7289
7290 static boolean JustBeingPushed(int x, int y)
7291 {
7292   int i;
7293
7294   for (i = 0; i < MAX_PLAYERS; i++)
7295   {
7296     struct PlayerInfo *player = &stored_player[i];
7297
7298     if (player->active && player->is_pushing && player->MovPos)
7299     {
7300       int next_jx = player->jx + (player->jx - player->last_jx);
7301       int next_jy = player->jy + (player->jy - player->last_jy);
7302
7303       if (x == next_jx && y == next_jy)
7304         return TRUE;
7305     }
7306   }
7307
7308   return FALSE;
7309 }
7310
7311 void StartMoving(int x, int y)
7312 {
7313   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7314   int element = Feld[x][y];
7315
7316   if (Stop[x][y])
7317     return;
7318
7319   if (MovDelay[x][y] == 0)
7320     GfxAction[x][y] = ACTION_DEFAULT;
7321
7322   if (CAN_FALL(element) && y < lev_fieldy - 1)
7323   {
7324     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7325         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7326       if (JustBeingPushed(x, y))
7327         return;
7328
7329     if (element == EL_QUICKSAND_FULL)
7330     {
7331       if (IS_FREE(x, y + 1))
7332       {
7333         InitMovingField(x, y, MV_DOWN);
7334         started_moving = TRUE;
7335
7336         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7337 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7338         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7339           Store[x][y] = EL_ROCK;
7340 #else
7341         Store[x][y] = EL_ROCK;
7342 #endif
7343
7344         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7345       }
7346       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7347       {
7348         if (!MovDelay[x][y])
7349           MovDelay[x][y] = TILEY + 1;
7350
7351         if (MovDelay[x][y])
7352         {
7353           MovDelay[x][y]--;
7354           if (MovDelay[x][y])
7355             return;
7356         }
7357
7358         Feld[x][y] = EL_QUICKSAND_EMPTY;
7359         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7360         Store[x][y + 1] = Store[x][y];
7361         Store[x][y] = 0;
7362
7363         PlayLevelSoundAction(x, y, ACTION_FILLING);
7364       }
7365     }
7366     else if (element == EL_QUICKSAND_FAST_FULL)
7367     {
7368       if (IS_FREE(x, y + 1))
7369       {
7370         InitMovingField(x, y, MV_DOWN);
7371         started_moving = TRUE;
7372
7373         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7374 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7375         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7376           Store[x][y] = EL_ROCK;
7377 #else
7378         Store[x][y] = EL_ROCK;
7379 #endif
7380
7381         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7382       }
7383       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7384       {
7385         if (!MovDelay[x][y])
7386           MovDelay[x][y] = TILEY + 1;
7387
7388         if (MovDelay[x][y])
7389         {
7390           MovDelay[x][y]--;
7391           if (MovDelay[x][y])
7392             return;
7393         }
7394
7395         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7396         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7397         Store[x][y + 1] = Store[x][y];
7398         Store[x][y] = 0;
7399
7400         PlayLevelSoundAction(x, y, ACTION_FILLING);
7401       }
7402     }
7403     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7404              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7405     {
7406       InitMovingField(x, y, MV_DOWN);
7407       started_moving = TRUE;
7408
7409       Feld[x][y] = EL_QUICKSAND_FILLING;
7410       Store[x][y] = element;
7411
7412       PlayLevelSoundAction(x, y, ACTION_FILLING);
7413     }
7414     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7415              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7416     {
7417       InitMovingField(x, y, MV_DOWN);
7418       started_moving = TRUE;
7419
7420       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7421       Store[x][y] = element;
7422
7423       PlayLevelSoundAction(x, y, ACTION_FILLING);
7424     }
7425     else if (element == EL_MAGIC_WALL_FULL)
7426     {
7427       if (IS_FREE(x, y + 1))
7428       {
7429         InitMovingField(x, y, MV_DOWN);
7430         started_moving = TRUE;
7431
7432         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7433         Store[x][y] = EL_CHANGED(Store[x][y]);
7434       }
7435       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7436       {
7437         if (!MovDelay[x][y])
7438           MovDelay[x][y] = TILEY/4 + 1;
7439
7440         if (MovDelay[x][y])
7441         {
7442           MovDelay[x][y]--;
7443           if (MovDelay[x][y])
7444             return;
7445         }
7446
7447         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7448         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7449         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7450         Store[x][y] = 0;
7451       }
7452     }
7453     else if (element == EL_BD_MAGIC_WALL_FULL)
7454     {
7455       if (IS_FREE(x, y + 1))
7456       {
7457         InitMovingField(x, y, MV_DOWN);
7458         started_moving = TRUE;
7459
7460         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7461         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7462       }
7463       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7464       {
7465         if (!MovDelay[x][y])
7466           MovDelay[x][y] = TILEY/4 + 1;
7467
7468         if (MovDelay[x][y])
7469         {
7470           MovDelay[x][y]--;
7471           if (MovDelay[x][y])
7472             return;
7473         }
7474
7475         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7476         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7477         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7478         Store[x][y] = 0;
7479       }
7480     }
7481     else if (element == EL_DC_MAGIC_WALL_FULL)
7482     {
7483       if (IS_FREE(x, y + 1))
7484       {
7485         InitMovingField(x, y, MV_DOWN);
7486         started_moving = TRUE;
7487
7488         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7489         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7490       }
7491       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7492       {
7493         if (!MovDelay[x][y])
7494           MovDelay[x][y] = TILEY/4 + 1;
7495
7496         if (MovDelay[x][y])
7497         {
7498           MovDelay[x][y]--;
7499           if (MovDelay[x][y])
7500             return;
7501         }
7502
7503         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7504         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7505         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7506         Store[x][y] = 0;
7507       }
7508     }
7509     else if ((CAN_PASS_MAGIC_WALL(element) &&
7510               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7511                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7512              (CAN_PASS_DC_MAGIC_WALL(element) &&
7513               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7514
7515     {
7516       InitMovingField(x, y, MV_DOWN);
7517       started_moving = TRUE;
7518
7519       Feld[x][y] =
7520         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7521          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7522          EL_DC_MAGIC_WALL_FILLING);
7523       Store[x][y] = element;
7524     }
7525     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7526     {
7527       SplashAcid(x, y + 1);
7528
7529       InitMovingField(x, y, MV_DOWN);
7530       started_moving = TRUE;
7531
7532       Store[x][y] = EL_ACID;
7533     }
7534     else if (
7535 #if USE_FIX_IMPACT_COLLISION
7536              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7537               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7538 #else
7539              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7540               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7541 #endif
7542              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7543               CAN_FALL(element) && WasJustFalling[x][y] &&
7544               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7545
7546              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7547               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7548               (Feld[x][y + 1] == EL_BLOCKED)))
7549     {
7550       /* this is needed for a special case not covered by calling "Impact()"
7551          from "ContinueMoving()": if an element moves to a tile directly below
7552          another element which was just falling on that tile (which was empty
7553          in the previous frame), the falling element above would just stop
7554          instead of smashing the element below (in previous version, the above
7555          element was just checked for "moving" instead of "falling", resulting
7556          in incorrect smashes caused by horizontal movement of the above
7557          element; also, the case of the player being the element to smash was
7558          simply not covered here... :-/ ) */
7559
7560       CheckCollision[x][y] = 0;
7561       CheckImpact[x][y] = 0;
7562
7563       Impact(x, y);
7564     }
7565     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7566     {
7567       if (MovDir[x][y] == MV_NONE)
7568       {
7569         InitMovingField(x, y, MV_DOWN);
7570         started_moving = TRUE;
7571       }
7572     }
7573     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7574     {
7575       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7576         MovDir[x][y] = MV_DOWN;
7577
7578       InitMovingField(x, y, MV_DOWN);
7579       started_moving = TRUE;
7580     }
7581     else if (element == EL_AMOEBA_DROP)
7582     {
7583       Feld[x][y] = EL_AMOEBA_GROWING;
7584       Store[x][y] = EL_AMOEBA_WET;
7585     }
7586     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7587               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7588              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7589              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7590     {
7591       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7592                                 (IS_FREE(x - 1, y + 1) ||
7593                                  Feld[x - 1][y + 1] == EL_ACID));
7594       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7595                                 (IS_FREE(x + 1, y + 1) ||
7596                                  Feld[x + 1][y + 1] == EL_ACID));
7597       boolean can_fall_any  = (can_fall_left || can_fall_right);
7598       boolean can_fall_both = (can_fall_left && can_fall_right);
7599       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7600
7601 #if USE_NEW_ALL_SLIPPERY
7602       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7603       {
7604         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7605           can_fall_right = FALSE;
7606         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7607           can_fall_left = FALSE;
7608         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7609           can_fall_right = FALSE;
7610         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7611           can_fall_left = FALSE;
7612
7613         can_fall_any  = (can_fall_left || can_fall_right);
7614         can_fall_both = FALSE;
7615       }
7616 #else
7617       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7618       {
7619         if (slippery_type == SLIPPERY_ONLY_LEFT)
7620           can_fall_right = FALSE;
7621         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7622           can_fall_left = FALSE;
7623         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7624           can_fall_right = FALSE;
7625         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7626           can_fall_left = FALSE;
7627
7628         can_fall_any  = (can_fall_left || can_fall_right);
7629         can_fall_both = (can_fall_left && can_fall_right);
7630       }
7631 #endif
7632
7633 #if USE_NEW_ALL_SLIPPERY
7634 #else
7635 #if USE_NEW_SP_SLIPPERY
7636       /* !!! better use the same properties as for custom elements here !!! */
7637       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7638                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7639       {
7640         can_fall_right = FALSE;         /* slip down on left side */
7641         can_fall_both = FALSE;
7642       }
7643 #endif
7644 #endif
7645
7646 #if USE_NEW_ALL_SLIPPERY
7647       if (can_fall_both)
7648       {
7649         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7650           can_fall_right = FALSE;       /* slip down on left side */
7651         else
7652           can_fall_left = !(can_fall_right = RND(2));
7653
7654         can_fall_both = FALSE;
7655       }
7656 #else
7657       if (can_fall_both)
7658       {
7659         if (game.emulation == EMU_BOULDERDASH ||
7660             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7661           can_fall_right = FALSE;       /* slip down on left side */
7662         else
7663           can_fall_left = !(can_fall_right = RND(2));
7664
7665         can_fall_both = FALSE;
7666       }
7667 #endif
7668
7669       if (can_fall_any)
7670       {
7671         /* if not determined otherwise, prefer left side for slipping down */
7672         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7673         started_moving = TRUE;
7674       }
7675     }
7676 #if 0
7677     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7678 #else
7679     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7680 #endif
7681     {
7682       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7683       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7684       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7685       int belt_dir = game.belt_dir[belt_nr];
7686
7687       if ((belt_dir == MV_LEFT  && left_is_free) ||
7688           (belt_dir == MV_RIGHT && right_is_free))
7689       {
7690         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7691
7692         InitMovingField(x, y, belt_dir);
7693         started_moving = TRUE;
7694
7695         Pushed[x][y] = TRUE;
7696         Pushed[nextx][y] = TRUE;
7697
7698         GfxAction[x][y] = ACTION_DEFAULT;
7699       }
7700       else
7701       {
7702         MovDir[x][y] = 0;       /* if element was moving, stop it */
7703       }
7704     }
7705   }
7706
7707   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7708 #if 0
7709   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7710 #else
7711   if (CAN_MOVE(element) && !started_moving)
7712 #endif
7713   {
7714     int move_pattern = element_info[element].move_pattern;
7715     int newx, newy;
7716
7717 #if 0
7718 #if DEBUG
7719     if (MovDir[x][y] == MV_NONE)
7720     {
7721       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7722              x, y, element, element_info[element].token_name);
7723       printf("StartMoving(): This should never happen!\n");
7724     }
7725 #endif
7726 #endif
7727
7728     Moving2Blocked(x, y, &newx, &newy);
7729
7730     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7731       return;
7732
7733     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7734         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7735     {
7736       WasJustMoving[x][y] = 0;
7737       CheckCollision[x][y] = 0;
7738
7739       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7740
7741       if (Feld[x][y] != element)        /* element has changed */
7742         return;
7743     }
7744
7745     if (!MovDelay[x][y])        /* start new movement phase */
7746     {
7747       /* all objects that can change their move direction after each step
7748          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7749
7750       if (element != EL_YAMYAM &&
7751           element != EL_DARK_YAMYAM &&
7752           element != EL_PACMAN &&
7753           !(move_pattern & MV_ANY_DIRECTION) &&
7754           move_pattern != MV_TURNING_LEFT &&
7755           move_pattern != MV_TURNING_RIGHT &&
7756           move_pattern != MV_TURNING_LEFT_RIGHT &&
7757           move_pattern != MV_TURNING_RIGHT_LEFT &&
7758           move_pattern != MV_TURNING_RANDOM)
7759       {
7760         TurnRound(x, y);
7761
7762         if (MovDelay[x][y] && (element == EL_BUG ||
7763                                element == EL_SPACESHIP ||
7764                                element == EL_SP_SNIKSNAK ||
7765                                element == EL_SP_ELECTRON ||
7766                                element == EL_MOLE))
7767           DrawLevelField(x, y);
7768       }
7769     }
7770
7771     if (MovDelay[x][y])         /* wait some time before next movement */
7772     {
7773       MovDelay[x][y]--;
7774
7775       if (element == EL_ROBOT ||
7776           element == EL_YAMYAM ||
7777           element == EL_DARK_YAMYAM)
7778       {
7779         DrawLevelElementAnimationIfNeeded(x, y, element);
7780         PlayLevelSoundAction(x, y, ACTION_WAITING);
7781       }
7782       else if (element == EL_SP_ELECTRON)
7783         DrawLevelElementAnimationIfNeeded(x, y, element);
7784       else if (element == EL_DRAGON)
7785       {
7786         int i;
7787         int dir = MovDir[x][y];
7788         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7789         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7790         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7791                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7792                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7793                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7794         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7795
7796         GfxAction[x][y] = ACTION_ATTACKING;
7797
7798         if (IS_PLAYER(x, y))
7799           DrawPlayerField(x, y);
7800         else
7801           DrawLevelField(x, y);
7802
7803         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7804
7805         for (i = 1; i <= 3; i++)
7806         {
7807           int xx = x + i * dx;
7808           int yy = y + i * dy;
7809           int sx = SCREENX(xx);
7810           int sy = SCREENY(yy);
7811           int flame_graphic = graphic + (i - 1);
7812
7813           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7814             break;
7815
7816           if (MovDelay[x][y])
7817           {
7818             int flamed = MovingOrBlocked2Element(xx, yy);
7819
7820             /* !!! */
7821 #if 0
7822             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7823               Bang(xx, yy);
7824             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7825               RemoveMovingField(xx, yy);
7826             else
7827               RemoveField(xx, yy);
7828 #else
7829             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7830               Bang(xx, yy);
7831             else
7832               RemoveMovingField(xx, yy);
7833 #endif
7834
7835             ChangeDelay[xx][yy] = 0;
7836
7837             Feld[xx][yy] = EL_FLAMES;
7838
7839             if (IN_SCR_FIELD(sx, sy))
7840             {
7841               DrawLevelFieldCrumbledSand(xx, yy);
7842               DrawGraphic(sx, sy, flame_graphic, frame);
7843             }
7844           }
7845           else
7846           {
7847             if (Feld[xx][yy] == EL_FLAMES)
7848               Feld[xx][yy] = EL_EMPTY;
7849             DrawLevelField(xx, yy);
7850           }
7851         }
7852       }
7853
7854       if (MovDelay[x][y])       /* element still has to wait some time */
7855       {
7856         PlayLevelSoundAction(x, y, ACTION_WAITING);
7857
7858         return;
7859       }
7860     }
7861
7862     /* now make next step */
7863
7864     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7865
7866     if (DONT_COLLIDE_WITH(element) &&
7867         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7868         !PLAYER_ENEMY_PROTECTED(newx, newy))
7869     {
7870       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7871
7872       return;
7873     }
7874
7875     else if (CAN_MOVE_INTO_ACID(element) &&
7876              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7877              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7878              (MovDir[x][y] == MV_DOWN ||
7879               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7880     {
7881       SplashAcid(newx, newy);
7882       Store[x][y] = EL_ACID;
7883     }
7884     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7885     {
7886       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7887           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7888           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7889           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7890       {
7891         RemoveField(x, y);
7892         DrawLevelField(x, y);
7893
7894         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7895         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7896           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7897
7898         local_player->friends_still_needed--;
7899         if (!local_player->friends_still_needed &&
7900             !local_player->GameOver && AllPlayersGone)
7901           PlayerWins(local_player);
7902
7903         return;
7904       }
7905       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7906       {
7907         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7908           DrawLevelField(newx, newy);
7909         else
7910           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7911       }
7912       else if (!IS_FREE(newx, newy))
7913       {
7914         GfxAction[x][y] = ACTION_WAITING;
7915
7916         if (IS_PLAYER(x, y))
7917           DrawPlayerField(x, y);
7918         else
7919           DrawLevelField(x, y);
7920
7921         return;
7922       }
7923     }
7924     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7925     {
7926       if (IS_FOOD_PIG(Feld[newx][newy]))
7927       {
7928         if (IS_MOVING(newx, newy))
7929           RemoveMovingField(newx, newy);
7930         else
7931         {
7932           Feld[newx][newy] = EL_EMPTY;
7933           DrawLevelField(newx, newy);
7934         }
7935
7936         PlayLevelSound(x, y, SND_PIG_DIGGING);
7937       }
7938       else if (!IS_FREE(newx, newy))
7939       {
7940         if (IS_PLAYER(x, y))
7941           DrawPlayerField(x, y);
7942         else
7943           DrawLevelField(x, y);
7944
7945         return;
7946       }
7947     }
7948     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7949     {
7950       if (Store[x][y] != EL_EMPTY)
7951       {
7952         boolean can_clone = FALSE;
7953         int xx, yy;
7954
7955         /* check if element to clone is still there */
7956         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7957         {
7958           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7959           {
7960             can_clone = TRUE;
7961
7962             break;
7963           }
7964         }
7965
7966         /* cannot clone or target field not free anymore -- do not clone */
7967         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7968           Store[x][y] = EL_EMPTY;
7969       }
7970
7971       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7972       {
7973         if (IS_MV_DIAGONAL(MovDir[x][y]))
7974         {
7975           int diagonal_move_dir = MovDir[x][y];
7976           int stored = Store[x][y];
7977           int change_delay = 8;
7978           int graphic;
7979
7980           /* android is moving diagonally */
7981
7982           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7983
7984           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7985           GfxElement[x][y] = EL_EMC_ANDROID;
7986           GfxAction[x][y] = ACTION_SHRINKING;
7987           GfxDir[x][y] = diagonal_move_dir;
7988           ChangeDelay[x][y] = change_delay;
7989
7990           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7991                                    GfxDir[x][y]);
7992
7993           DrawLevelGraphicAnimation(x, y, graphic);
7994           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7995
7996           if (Feld[newx][newy] == EL_ACID)
7997           {
7998             SplashAcid(newx, newy);
7999
8000             return;
8001           }
8002
8003           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8004
8005           Store[newx][newy] = EL_EMC_ANDROID;
8006           GfxElement[newx][newy] = EL_EMC_ANDROID;
8007           GfxAction[newx][newy] = ACTION_GROWING;
8008           GfxDir[newx][newy] = diagonal_move_dir;
8009           ChangeDelay[newx][newy] = change_delay;
8010
8011           graphic = el_act_dir2img(GfxElement[newx][newy],
8012                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8013
8014           DrawLevelGraphicAnimation(newx, newy, graphic);
8015           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8016
8017           return;
8018         }
8019         else
8020         {
8021           Feld[newx][newy] = EL_EMPTY;
8022           DrawLevelField(newx, newy);
8023
8024           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8025         }
8026       }
8027       else if (!IS_FREE(newx, newy))
8028       {
8029 #if 0
8030         if (IS_PLAYER(x, y))
8031           DrawPlayerField(x, y);
8032         else
8033           DrawLevelField(x, y);
8034 #endif
8035
8036         return;
8037       }
8038     }
8039     else if (IS_CUSTOM_ELEMENT(element) &&
8040              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8041     {
8042       int new_element = Feld[newx][newy];
8043
8044       if (!IS_FREE(newx, newy))
8045       {
8046         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8047                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8048                       ACTION_BREAKING);
8049
8050         /* no element can dig solid indestructible elements */
8051         if (IS_INDESTRUCTIBLE(new_element) &&
8052             !IS_DIGGABLE(new_element) &&
8053             !IS_COLLECTIBLE(new_element))
8054           return;
8055
8056         if (AmoebaNr[newx][newy] &&
8057             (new_element == EL_AMOEBA_FULL ||
8058              new_element == EL_BD_AMOEBA ||
8059              new_element == EL_AMOEBA_GROWING))
8060         {
8061           AmoebaCnt[AmoebaNr[newx][newy]]--;
8062           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8063         }
8064
8065         if (IS_MOVING(newx, newy))
8066           RemoveMovingField(newx, newy);
8067         else
8068         {
8069           RemoveField(newx, newy);
8070           DrawLevelField(newx, newy);
8071         }
8072
8073         /* if digged element was about to explode, prevent the explosion */
8074         ExplodeField[newx][newy] = EX_TYPE_NONE;
8075
8076         PlayLevelSoundAction(x, y, action);
8077       }
8078
8079       Store[newx][newy] = EL_EMPTY;
8080 #if 1
8081       /* this makes it possible to leave the removed element again */
8082       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8083         Store[newx][newy] = new_element;
8084 #else
8085       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8086       {
8087         int move_leave_element = element_info[element].move_leave_element;
8088
8089         /* this makes it possible to leave the removed element again */
8090         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8091                              new_element : move_leave_element);
8092       }
8093 #endif
8094
8095       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8096       {
8097         RunnerVisit[x][y] = FrameCounter;
8098         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8099       }
8100     }
8101     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8102     {
8103       if (!IS_FREE(newx, newy))
8104       {
8105         if (IS_PLAYER(x, y))
8106           DrawPlayerField(x, y);
8107         else
8108           DrawLevelField(x, y);
8109
8110         return;
8111       }
8112       else
8113       {
8114         boolean wanna_flame = !RND(10);
8115         int dx = newx - x, dy = newy - y;
8116         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8117         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8118         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8119                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8120         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8121                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8122
8123         if ((wanna_flame ||
8124              IS_CLASSIC_ENEMY(element1) ||
8125              IS_CLASSIC_ENEMY(element2)) &&
8126             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8127             element1 != EL_FLAMES && element2 != EL_FLAMES)
8128         {
8129           ResetGfxAnimation(x, y);
8130           GfxAction[x][y] = ACTION_ATTACKING;
8131
8132           if (IS_PLAYER(x, y))
8133             DrawPlayerField(x, y);
8134           else
8135             DrawLevelField(x, y);
8136
8137           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8138
8139           MovDelay[x][y] = 50;
8140
8141           /* !!! */
8142 #if 0
8143           RemoveField(newx, newy);
8144 #endif
8145           Feld[newx][newy] = EL_FLAMES;
8146           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8147           {
8148 #if 0
8149             RemoveField(newx1, newy1);
8150 #endif
8151             Feld[newx1][newy1] = EL_FLAMES;
8152           }
8153           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8154           {
8155 #if 0
8156             RemoveField(newx2, newy2);
8157 #endif
8158             Feld[newx2][newy2] = EL_FLAMES;
8159           }
8160
8161           return;
8162         }
8163       }
8164     }
8165     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8166              Feld[newx][newy] == EL_DIAMOND)
8167     {
8168       if (IS_MOVING(newx, newy))
8169         RemoveMovingField(newx, newy);
8170       else
8171       {
8172         Feld[newx][newy] = EL_EMPTY;
8173         DrawLevelField(newx, newy);
8174       }
8175
8176       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8177     }
8178     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8179              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8180     {
8181       if (AmoebaNr[newx][newy])
8182       {
8183         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8184         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8185             Feld[newx][newy] == EL_BD_AMOEBA)
8186           AmoebaCnt[AmoebaNr[newx][newy]]--;
8187       }
8188
8189 #if 0
8190       /* !!! test !!! */
8191       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8192       {
8193         RemoveMovingField(newx, newy);
8194       }
8195 #else
8196       if (IS_MOVING(newx, newy))
8197       {
8198         RemoveMovingField(newx, newy);
8199       }
8200 #endif
8201       else
8202       {
8203         Feld[newx][newy] = EL_EMPTY;
8204         DrawLevelField(newx, newy);
8205       }
8206
8207       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8208     }
8209     else if ((element == EL_PACMAN || element == EL_MOLE)
8210              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8211     {
8212       if (AmoebaNr[newx][newy])
8213       {
8214         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8215         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8216             Feld[newx][newy] == EL_BD_AMOEBA)
8217           AmoebaCnt[AmoebaNr[newx][newy]]--;
8218       }
8219
8220       if (element == EL_MOLE)
8221       {
8222         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8223         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8224
8225         ResetGfxAnimation(x, y);
8226         GfxAction[x][y] = ACTION_DIGGING;
8227         DrawLevelField(x, y);
8228
8229         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8230
8231         return;                         /* wait for shrinking amoeba */
8232       }
8233       else      /* element == EL_PACMAN */
8234       {
8235         Feld[newx][newy] = EL_EMPTY;
8236         DrawLevelField(newx, newy);
8237         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8238       }
8239     }
8240     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8241              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8242               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8243     {
8244       /* wait for shrinking amoeba to completely disappear */
8245       return;
8246     }
8247     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8248     {
8249       /* object was running against a wall */
8250
8251       TurnRound(x, y);
8252
8253 #if 0
8254       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8255       if (move_pattern & MV_ANY_DIRECTION &&
8256           move_pattern == MovDir[x][y])
8257       {
8258         int blocking_element =
8259           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8260
8261         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8262                                  MovDir[x][y]);
8263
8264         element = Feld[x][y];   /* element might have changed */
8265       }
8266 #endif
8267
8268       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8269         DrawLevelElementAnimation(x, y, element);
8270
8271       if (DONT_TOUCH(element))
8272         TestIfBadThingTouchesPlayer(x, y);
8273
8274       return;
8275     }
8276
8277     InitMovingField(x, y, MovDir[x][y]);
8278
8279     PlayLevelSoundAction(x, y, ACTION_MOVING);
8280   }
8281
8282   if (MovDir[x][y])
8283     ContinueMoving(x, y);
8284 }
8285
8286 void ContinueMoving(int x, int y)
8287 {
8288   int element = Feld[x][y];
8289   struct ElementInfo *ei = &element_info[element];
8290   int direction = MovDir[x][y];
8291   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8292   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8293   int newx = x + dx, newy = y + dy;
8294   int stored = Store[x][y];
8295   int stored_new = Store[newx][newy];
8296   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8297   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8298   boolean last_line = (newy == lev_fieldy - 1);
8299
8300   MovPos[x][y] += getElementMoveStepsize(x, y);
8301
8302   if (pushed_by_player) /* special case: moving object pushed by player */
8303     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8304
8305   if (ABS(MovPos[x][y]) < TILEX)
8306   {
8307 #if 0
8308     int ee = Feld[x][y];
8309     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8310     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8311
8312     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8313            x, y, ABS(MovPos[x][y]),
8314            ee, gg, ff,
8315            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8316 #endif
8317
8318     DrawLevelField(x, y);
8319
8320     return;     /* element is still moving */
8321   }
8322
8323   /* element reached destination field */
8324
8325   Feld[x][y] = EL_EMPTY;
8326   Feld[newx][newy] = element;
8327   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8328
8329   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8330   {
8331     element = Feld[newx][newy] = EL_ACID;
8332   }
8333   else if (element == EL_MOLE)
8334   {
8335     Feld[x][y] = EL_SAND;
8336
8337     DrawLevelFieldCrumbledSandNeighbours(x, y);
8338   }
8339   else if (element == EL_QUICKSAND_FILLING)
8340   {
8341     element = Feld[newx][newy] = get_next_element(element);
8342     Store[newx][newy] = Store[x][y];
8343   }
8344   else if (element == EL_QUICKSAND_EMPTYING)
8345   {
8346     Feld[x][y] = get_next_element(element);
8347     element = Feld[newx][newy] = Store[x][y];
8348   }
8349   else if (element == EL_QUICKSAND_FAST_FILLING)
8350   {
8351     element = Feld[newx][newy] = get_next_element(element);
8352     Store[newx][newy] = Store[x][y];
8353   }
8354   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8355   {
8356     Feld[x][y] = get_next_element(element);
8357     element = Feld[newx][newy] = Store[x][y];
8358   }
8359   else if (element == EL_MAGIC_WALL_FILLING)
8360   {
8361     element = Feld[newx][newy] = get_next_element(element);
8362     if (!game.magic_wall_active)
8363       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8364     Store[newx][newy] = Store[x][y];
8365   }
8366   else if (element == EL_MAGIC_WALL_EMPTYING)
8367   {
8368     Feld[x][y] = get_next_element(element);
8369     if (!game.magic_wall_active)
8370       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8371     element = Feld[newx][newy] = Store[x][y];
8372
8373 #if USE_NEW_CUSTOM_VALUE
8374     InitField(newx, newy, FALSE);
8375 #endif
8376   }
8377   else if (element == EL_BD_MAGIC_WALL_FILLING)
8378   {
8379     element = Feld[newx][newy] = get_next_element(element);
8380     if (!game.magic_wall_active)
8381       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8382     Store[newx][newy] = Store[x][y];
8383   }
8384   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8385   {
8386     Feld[x][y] = get_next_element(element);
8387     if (!game.magic_wall_active)
8388       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8389     element = Feld[newx][newy] = Store[x][y];
8390
8391 #if USE_NEW_CUSTOM_VALUE
8392     InitField(newx, newy, FALSE);
8393 #endif
8394   }
8395   else if (element == EL_DC_MAGIC_WALL_FILLING)
8396   {
8397     element = Feld[newx][newy] = get_next_element(element);
8398     if (!game.magic_wall_active)
8399       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8400     Store[newx][newy] = Store[x][y];
8401   }
8402   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8403   {
8404     Feld[x][y] = get_next_element(element);
8405     if (!game.magic_wall_active)
8406       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8407     element = Feld[newx][newy] = Store[x][y];
8408
8409 #if USE_NEW_CUSTOM_VALUE
8410     InitField(newx, newy, FALSE);
8411 #endif
8412   }
8413   else if (element == EL_AMOEBA_DROPPING)
8414   {
8415     Feld[x][y] = get_next_element(element);
8416     element = Feld[newx][newy] = Store[x][y];
8417   }
8418   else if (element == EL_SOKOBAN_OBJECT)
8419   {
8420     if (Back[x][y])
8421       Feld[x][y] = Back[x][y];
8422
8423     if (Back[newx][newy])
8424       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8425
8426     Back[x][y] = Back[newx][newy] = 0;
8427   }
8428
8429   Store[x][y] = EL_EMPTY;
8430   MovPos[x][y] = 0;
8431   MovDir[x][y] = 0;
8432   MovDelay[x][y] = 0;
8433
8434   MovDelay[newx][newy] = 0;
8435
8436   if (CAN_CHANGE_OR_HAS_ACTION(element))
8437   {
8438     /* copy element change control values to new field */
8439     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8440     ChangePage[newx][newy]  = ChangePage[x][y];
8441     ChangeCount[newx][newy] = ChangeCount[x][y];
8442     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8443   }
8444
8445 #if USE_NEW_CUSTOM_VALUE
8446     CustomValue[newx][newy] = CustomValue[x][y];
8447 #endif
8448
8449   ChangeDelay[x][y] = 0;
8450   ChangePage[x][y] = -1;
8451   ChangeCount[x][y] = 0;
8452   ChangeEvent[x][y] = -1;
8453
8454 #if USE_NEW_CUSTOM_VALUE
8455   CustomValue[x][y] = 0;
8456 #endif
8457
8458   /* copy animation control values to new field */
8459   GfxFrame[newx][newy]  = GfxFrame[x][y];
8460   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8461   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8462   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8463
8464   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8465
8466   /* some elements can leave other elements behind after moving */
8467 #if 1
8468   if (ei->move_leave_element != EL_EMPTY &&
8469       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8470       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8471 #else
8472   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8473       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8474       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8475 #endif
8476   {
8477     int move_leave_element = ei->move_leave_element;
8478
8479 #if 1
8480 #if 1
8481     /* this makes it possible to leave the removed element again */
8482     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8483       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8484 #else
8485     /* this makes it possible to leave the removed element again */
8486     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8487       move_leave_element = stored;
8488 #endif
8489 #else
8490     /* this makes it possible to leave the removed element again */
8491     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8492         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8493       move_leave_element = stored;
8494 #endif
8495
8496     Feld[x][y] = move_leave_element;
8497
8498     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8499       MovDir[x][y] = direction;
8500
8501     InitField(x, y, FALSE);
8502
8503     if (GFX_CRUMBLED(Feld[x][y]))
8504       DrawLevelFieldCrumbledSandNeighbours(x, y);
8505
8506     if (ELEM_IS_PLAYER(move_leave_element))
8507       RelocatePlayer(x, y, move_leave_element);
8508   }
8509
8510   /* do this after checking for left-behind element */
8511   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8512
8513   if (!CAN_MOVE(element) ||
8514       (CAN_FALL(element) && direction == MV_DOWN &&
8515        (element == EL_SPRING ||
8516         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8517         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8518     GfxDir[x][y] = MovDir[newx][newy] = 0;
8519
8520   DrawLevelField(x, y);
8521   DrawLevelField(newx, newy);
8522
8523   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8524
8525   /* prevent pushed element from moving on in pushed direction */
8526   if (pushed_by_player && CAN_MOVE(element) &&
8527       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8528       !(element_info[element].move_pattern & direction))
8529     TurnRound(newx, newy);
8530
8531   /* prevent elements on conveyor belt from moving on in last direction */
8532   if (pushed_by_conveyor && CAN_FALL(element) &&
8533       direction & MV_HORIZONTAL)
8534     MovDir[newx][newy] = 0;
8535
8536   if (!pushed_by_player)
8537   {
8538     int nextx = newx + dx, nexty = newy + dy;
8539     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8540
8541     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8542
8543     if (CAN_FALL(element) && direction == MV_DOWN)
8544       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8545
8546     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8547       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8548
8549 #if USE_FIX_IMPACT_COLLISION
8550     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8551       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8552 #endif
8553   }
8554
8555   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8556   {
8557     TestIfBadThingTouchesPlayer(newx, newy);
8558     TestIfBadThingTouchesFriend(newx, newy);
8559
8560     if (!IS_CUSTOM_ELEMENT(element))
8561       TestIfBadThingTouchesOtherBadThing(newx, newy);
8562   }
8563   else if (element == EL_PENGUIN)
8564     TestIfFriendTouchesBadThing(newx, newy);
8565
8566   /* give the player one last chance (one more frame) to move away */
8567   if (CAN_FALL(element) && direction == MV_DOWN &&
8568       (last_line || (!IS_FREE(x, newy + 1) &&
8569                      (!IS_PLAYER(x, newy + 1) ||
8570                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8571     Impact(x, newy);
8572
8573   if (pushed_by_player && !game.use_change_when_pushing_bug)
8574   {
8575     int push_side = MV_DIR_OPPOSITE(direction);
8576     struct PlayerInfo *player = PLAYERINFO(x, y);
8577
8578     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8579                                player->index_bit, push_side);
8580     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8581                                         player->index_bit, push_side);
8582   }
8583
8584   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8585     MovDelay[newx][newy] = 1;
8586
8587   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8588
8589   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8590
8591 #if 0
8592   if (ChangePage[newx][newy] != -1)             /* delayed change */
8593   {
8594     int page = ChangePage[newx][newy];
8595     struct ElementChangeInfo *change = &ei->change_page[page];
8596
8597     ChangePage[newx][newy] = -1;
8598
8599     if (change->can_change)
8600     {
8601       if (ChangeElement(newx, newy, element, page))
8602       {
8603         if (change->post_change_function)
8604           change->post_change_function(newx, newy);
8605       }
8606     }
8607
8608     if (change->has_action)
8609       ExecuteCustomElementAction(newx, newy, element, page);
8610   }
8611 #endif
8612
8613   TestIfElementHitsCustomElement(newx, newy, direction);
8614   TestIfPlayerTouchesCustomElement(newx, newy);
8615   TestIfElementTouchesCustomElement(newx, newy);
8616
8617   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8618       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8619     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8620                              MV_DIR_OPPOSITE(direction));
8621 }
8622
8623 int AmoebeNachbarNr(int ax, int ay)
8624 {
8625   int i;
8626   int element = Feld[ax][ay];
8627   int group_nr = 0;
8628   static int xy[4][2] =
8629   {
8630     { 0, -1 },
8631     { -1, 0 },
8632     { +1, 0 },
8633     { 0, +1 }
8634   };
8635
8636   for (i = 0; i < NUM_DIRECTIONS; i++)
8637   {
8638     int x = ax + xy[i][0];
8639     int y = ay + xy[i][1];
8640
8641     if (!IN_LEV_FIELD(x, y))
8642       continue;
8643
8644     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8645       group_nr = AmoebaNr[x][y];
8646   }
8647
8648   return group_nr;
8649 }
8650
8651 void AmoebenVereinigen(int ax, int ay)
8652 {
8653   int i, x, y, xx, yy;
8654   int new_group_nr = AmoebaNr[ax][ay];
8655   static int xy[4][2] =
8656   {
8657     { 0, -1 },
8658     { -1, 0 },
8659     { +1, 0 },
8660     { 0, +1 }
8661   };
8662
8663   if (new_group_nr == 0)
8664     return;
8665
8666   for (i = 0; i < NUM_DIRECTIONS; i++)
8667   {
8668     x = ax + xy[i][0];
8669     y = ay + xy[i][1];
8670
8671     if (!IN_LEV_FIELD(x, y))
8672       continue;
8673
8674     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8675          Feld[x][y] == EL_BD_AMOEBA ||
8676          Feld[x][y] == EL_AMOEBA_DEAD) &&
8677         AmoebaNr[x][y] != new_group_nr)
8678     {
8679       int old_group_nr = AmoebaNr[x][y];
8680
8681       if (old_group_nr == 0)
8682         return;
8683
8684       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8685       AmoebaCnt[old_group_nr] = 0;
8686       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8687       AmoebaCnt2[old_group_nr] = 0;
8688
8689       SCAN_PLAYFIELD(xx, yy)
8690       {
8691         if (AmoebaNr[xx][yy] == old_group_nr)
8692           AmoebaNr[xx][yy] = new_group_nr;
8693       }
8694     }
8695   }
8696 }
8697
8698 void AmoebeUmwandeln(int ax, int ay)
8699 {
8700   int i, x, y;
8701
8702   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8703   {
8704     int group_nr = AmoebaNr[ax][ay];
8705
8706 #ifdef DEBUG
8707     if (group_nr == 0)
8708     {
8709       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8710       printf("AmoebeUmwandeln(): This should never happen!\n");
8711       return;
8712     }
8713 #endif
8714
8715     SCAN_PLAYFIELD(x, y)
8716     {
8717       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8718       {
8719         AmoebaNr[x][y] = 0;
8720         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8721       }
8722     }
8723
8724     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8725                             SND_AMOEBA_TURNING_TO_GEM :
8726                             SND_AMOEBA_TURNING_TO_ROCK));
8727     Bang(ax, ay);
8728   }
8729   else
8730   {
8731     static int xy[4][2] =
8732     {
8733       { 0, -1 },
8734       { -1, 0 },
8735       { +1, 0 },
8736       { 0, +1 }
8737     };
8738
8739     for (i = 0; i < NUM_DIRECTIONS; i++)
8740     {
8741       x = ax + xy[i][0];
8742       y = ay + xy[i][1];
8743
8744       if (!IN_LEV_FIELD(x, y))
8745         continue;
8746
8747       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8748       {
8749         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8750                               SND_AMOEBA_TURNING_TO_GEM :
8751                               SND_AMOEBA_TURNING_TO_ROCK));
8752         Bang(x, y);
8753       }
8754     }
8755   }
8756 }
8757
8758 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8759 {
8760   int x, y;
8761   int group_nr = AmoebaNr[ax][ay];
8762   boolean done = FALSE;
8763
8764 #ifdef DEBUG
8765   if (group_nr == 0)
8766   {
8767     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8768     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8769     return;
8770   }
8771 #endif
8772
8773   SCAN_PLAYFIELD(x, y)
8774   {
8775     if (AmoebaNr[x][y] == group_nr &&
8776         (Feld[x][y] == EL_AMOEBA_DEAD ||
8777          Feld[x][y] == EL_BD_AMOEBA ||
8778          Feld[x][y] == EL_AMOEBA_GROWING))
8779     {
8780       AmoebaNr[x][y] = 0;
8781       Feld[x][y] = new_element;
8782       InitField(x, y, FALSE);
8783       DrawLevelField(x, y);
8784       done = TRUE;
8785     }
8786   }
8787
8788   if (done)
8789     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8790                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8791                             SND_BD_AMOEBA_TURNING_TO_GEM));
8792 }
8793
8794 void AmoebeWaechst(int x, int y)
8795 {
8796   static unsigned long sound_delay = 0;
8797   static unsigned long sound_delay_value = 0;
8798
8799   if (!MovDelay[x][y])          /* start new growing cycle */
8800   {
8801     MovDelay[x][y] = 7;
8802
8803     if (DelayReached(&sound_delay, sound_delay_value))
8804     {
8805       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8806       sound_delay_value = 30;
8807     }
8808   }
8809
8810   if (MovDelay[x][y])           /* wait some time before growing bigger */
8811   {
8812     MovDelay[x][y]--;
8813     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8814     {
8815       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8816                                            6 - MovDelay[x][y]);
8817
8818       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8819     }
8820
8821     if (!MovDelay[x][y])
8822     {
8823       Feld[x][y] = Store[x][y];
8824       Store[x][y] = 0;
8825       DrawLevelField(x, y);
8826     }
8827   }
8828 }
8829
8830 void AmoebaDisappearing(int x, int y)
8831 {
8832   static unsigned long sound_delay = 0;
8833   static unsigned long sound_delay_value = 0;
8834
8835   if (!MovDelay[x][y])          /* start new shrinking cycle */
8836   {
8837     MovDelay[x][y] = 7;
8838
8839     if (DelayReached(&sound_delay, sound_delay_value))
8840       sound_delay_value = 30;
8841   }
8842
8843   if (MovDelay[x][y])           /* wait some time before shrinking */
8844   {
8845     MovDelay[x][y]--;
8846     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8847     {
8848       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8849                                            6 - MovDelay[x][y]);
8850
8851       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8852     }
8853
8854     if (!MovDelay[x][y])
8855     {
8856       Feld[x][y] = EL_EMPTY;
8857       DrawLevelField(x, y);
8858
8859       /* don't let mole enter this field in this cycle;
8860          (give priority to objects falling to this field from above) */
8861       Stop[x][y] = TRUE;
8862     }
8863   }
8864 }
8865
8866 void AmoebeAbleger(int ax, int ay)
8867 {
8868   int i;
8869   int element = Feld[ax][ay];
8870   int graphic = el2img(element);
8871   int newax = ax, neway = ay;
8872   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8873   static int xy[4][2] =
8874   {
8875     { 0, -1 },
8876     { -1, 0 },
8877     { +1, 0 },
8878     { 0, +1 }
8879   };
8880
8881   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8882   {
8883     Feld[ax][ay] = EL_AMOEBA_DEAD;
8884     DrawLevelField(ax, ay);
8885     return;
8886   }
8887
8888   if (IS_ANIMATED(graphic))
8889     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8890
8891   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8892     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8893
8894   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8895   {
8896     MovDelay[ax][ay]--;
8897     if (MovDelay[ax][ay])
8898       return;
8899   }
8900
8901   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8902   {
8903     int start = RND(4);
8904     int x = ax + xy[start][0];
8905     int y = ay + xy[start][1];
8906
8907     if (!IN_LEV_FIELD(x, y))
8908       return;
8909
8910     if (IS_FREE(x, y) ||
8911         CAN_GROW_INTO(Feld[x][y]) ||
8912         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8913         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8914     {
8915       newax = x;
8916       neway = y;
8917     }
8918
8919     if (newax == ax && neway == ay)
8920       return;
8921   }
8922   else                          /* normal or "filled" (BD style) amoeba */
8923   {
8924     int start = RND(4);
8925     boolean waiting_for_player = FALSE;
8926
8927     for (i = 0; i < NUM_DIRECTIONS; i++)
8928     {
8929       int j = (start + i) % 4;
8930       int x = ax + xy[j][0];
8931       int y = ay + xy[j][1];
8932
8933       if (!IN_LEV_FIELD(x, y))
8934         continue;
8935
8936       if (IS_FREE(x, y) ||
8937           CAN_GROW_INTO(Feld[x][y]) ||
8938           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8939           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8940       {
8941         newax = x;
8942         neway = y;
8943         break;
8944       }
8945       else if (IS_PLAYER(x, y))
8946         waiting_for_player = TRUE;
8947     }
8948
8949     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8950     {
8951       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8952       {
8953         Feld[ax][ay] = EL_AMOEBA_DEAD;
8954         DrawLevelField(ax, ay);
8955         AmoebaCnt[AmoebaNr[ax][ay]]--;
8956
8957         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8958         {
8959           if (element == EL_AMOEBA_FULL)
8960             AmoebeUmwandeln(ax, ay);
8961           else if (element == EL_BD_AMOEBA)
8962             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8963         }
8964       }
8965       return;
8966     }
8967     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8968     {
8969       /* amoeba gets larger by growing in some direction */
8970
8971       int new_group_nr = AmoebaNr[ax][ay];
8972
8973 #ifdef DEBUG
8974   if (new_group_nr == 0)
8975   {
8976     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8977     printf("AmoebeAbleger(): This should never happen!\n");
8978     return;
8979   }
8980 #endif
8981
8982       AmoebaNr[newax][neway] = new_group_nr;
8983       AmoebaCnt[new_group_nr]++;
8984       AmoebaCnt2[new_group_nr]++;
8985
8986       /* if amoeba touches other amoeba(s) after growing, unify them */
8987       AmoebenVereinigen(newax, neway);
8988
8989       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8990       {
8991         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8992         return;
8993       }
8994     }
8995   }
8996
8997   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8998       (neway == lev_fieldy - 1 && newax != ax))
8999   {
9000     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9001     Store[newax][neway] = element;
9002   }
9003   else if (neway == ay || element == EL_EMC_DRIPPER)
9004   {
9005     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9006
9007     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9008   }
9009   else
9010   {
9011     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9012     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9013     Store[ax][ay] = EL_AMOEBA_DROP;
9014     ContinueMoving(ax, ay);
9015     return;
9016   }
9017
9018   DrawLevelField(newax, neway);
9019 }
9020
9021 void Life(int ax, int ay)
9022 {
9023   int x1, y1, x2, y2;
9024   int life_time = 40;
9025   int element = Feld[ax][ay];
9026   int graphic = el2img(element);
9027   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9028                          level.biomaze);
9029   boolean changed = FALSE;
9030
9031   if (IS_ANIMATED(graphic))
9032     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9033
9034   if (Stop[ax][ay])
9035     return;
9036
9037   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9038     MovDelay[ax][ay] = life_time;
9039
9040   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9041   {
9042     MovDelay[ax][ay]--;
9043     if (MovDelay[ax][ay])
9044       return;
9045   }
9046
9047   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9048   {
9049     int xx = ax+x1, yy = ay+y1;
9050     int nachbarn = 0;
9051
9052     if (!IN_LEV_FIELD(xx, yy))
9053       continue;
9054
9055     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9056     {
9057       int x = xx+x2, y = yy+y2;
9058
9059       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9060         continue;
9061
9062       if (((Feld[x][y] == element ||
9063             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9064            !Stop[x][y]) ||
9065           (IS_FREE(x, y) && Stop[x][y]))
9066         nachbarn++;
9067     }
9068
9069     if (xx == ax && yy == ay)           /* field in the middle */
9070     {
9071       if (nachbarn < life_parameter[0] ||
9072           nachbarn > life_parameter[1])
9073       {
9074         Feld[xx][yy] = EL_EMPTY;
9075         if (!Stop[xx][yy])
9076           DrawLevelField(xx, yy);
9077         Stop[xx][yy] = TRUE;
9078         changed = TRUE;
9079       }
9080     }
9081     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9082     {                                   /* free border field */
9083       if (nachbarn >= life_parameter[2] &&
9084           nachbarn <= life_parameter[3])
9085       {
9086         Feld[xx][yy] = element;
9087         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9088         if (!Stop[xx][yy])
9089           DrawLevelField(xx, yy);
9090         Stop[xx][yy] = TRUE;
9091         changed = TRUE;
9092       }
9093     }
9094   }
9095
9096   if (changed)
9097     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9098                    SND_GAME_OF_LIFE_GROWING);
9099 }
9100
9101 static void InitRobotWheel(int x, int y)
9102 {
9103   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9104 }
9105
9106 static void RunRobotWheel(int x, int y)
9107 {
9108   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9109 }
9110
9111 static void StopRobotWheel(int x, int y)
9112 {
9113   if (ZX == x && ZY == y)
9114   {
9115     ZX = ZY = -1;
9116
9117     game.robot_wheel_active = FALSE;
9118   }
9119 }
9120
9121 static void InitTimegateWheel(int x, int y)
9122 {
9123   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9124 }
9125
9126 static void RunTimegateWheel(int x, int y)
9127 {
9128   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9129 }
9130
9131 static void InitMagicBallDelay(int x, int y)
9132 {
9133 #if 1
9134   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9135 #else
9136   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9137 #endif
9138 }
9139
9140 static void ActivateMagicBall(int bx, int by)
9141 {
9142   int x, y;
9143
9144   if (level.ball_random)
9145   {
9146     int pos_border = RND(8);    /* select one of the eight border elements */
9147     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9148     int xx = pos_content % 3;
9149     int yy = pos_content / 3;
9150
9151     x = bx - 1 + xx;
9152     y = by - 1 + yy;
9153
9154     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9155       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9156   }
9157   else
9158   {
9159     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9160     {
9161       int xx = x - bx + 1;
9162       int yy = y - by + 1;
9163
9164       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9165         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9166     }
9167   }
9168
9169   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9170 }
9171
9172 void CheckExit(int x, int y)
9173 {
9174   if (local_player->gems_still_needed > 0 ||
9175       local_player->sokobanfields_still_needed > 0 ||
9176       local_player->lights_still_needed > 0)
9177   {
9178     int element = Feld[x][y];
9179     int graphic = el2img(element);
9180
9181     if (IS_ANIMATED(graphic))
9182       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9183
9184     return;
9185   }
9186
9187   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9188     return;
9189
9190   Feld[x][y] = EL_EXIT_OPENING;
9191
9192   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9193 }
9194
9195 void CheckExitEM(int x, int y)
9196 {
9197   if (local_player->gems_still_needed > 0 ||
9198       local_player->sokobanfields_still_needed > 0 ||
9199       local_player->lights_still_needed > 0)
9200   {
9201     int element = Feld[x][y];
9202     int graphic = el2img(element);
9203
9204     if (IS_ANIMATED(graphic))
9205       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9206
9207     return;
9208   }
9209
9210   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9211     return;
9212
9213   Feld[x][y] = EL_EM_EXIT_OPENING;
9214
9215   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9216 }
9217
9218 void CheckExitSteel(int x, int y)
9219 {
9220   if (local_player->gems_still_needed > 0 ||
9221       local_player->sokobanfields_still_needed > 0 ||
9222       local_player->lights_still_needed > 0)
9223   {
9224     int element = Feld[x][y];
9225     int graphic = el2img(element);
9226
9227     if (IS_ANIMATED(graphic))
9228       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9229
9230     return;
9231   }
9232
9233   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9234     return;
9235
9236   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9237
9238   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9239 }
9240
9241 void CheckExitSteelEM(int x, int y)
9242 {
9243   if (local_player->gems_still_needed > 0 ||
9244       local_player->sokobanfields_still_needed > 0 ||
9245       local_player->lights_still_needed > 0)
9246   {
9247     int element = Feld[x][y];
9248     int graphic = el2img(element);
9249
9250     if (IS_ANIMATED(graphic))
9251       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9252
9253     return;
9254   }
9255
9256   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9257     return;
9258
9259   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9260
9261   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9262 }
9263
9264 void CheckExitSP(int x, int y)
9265 {
9266   if (local_player->gems_still_needed > 0)
9267   {
9268     int element = Feld[x][y];
9269     int graphic = el2img(element);
9270
9271     if (IS_ANIMATED(graphic))
9272       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9273
9274     return;
9275   }
9276
9277   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9278     return;
9279
9280   Feld[x][y] = EL_SP_EXIT_OPENING;
9281
9282   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9283 }
9284
9285 static void CloseAllOpenTimegates()
9286 {
9287   int x, y;
9288
9289   SCAN_PLAYFIELD(x, y)
9290   {
9291     int element = Feld[x][y];
9292
9293     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9294     {
9295       Feld[x][y] = EL_TIMEGATE_CLOSING;
9296
9297       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9298     }
9299   }
9300 }
9301
9302 void DrawTwinkleOnField(int x, int y)
9303 {
9304   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9305     return;
9306
9307   if (Feld[x][y] == EL_BD_DIAMOND)
9308     return;
9309
9310   if (MovDelay[x][y] == 0)      /* next animation frame */
9311     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9312
9313   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9314   {
9315     MovDelay[x][y]--;
9316
9317     if (setup.direct_draw && MovDelay[x][y])
9318       SetDrawtoField(DRAW_BUFFERED);
9319
9320     DrawLevelElementAnimation(x, y, Feld[x][y]);
9321
9322     if (MovDelay[x][y] != 0)
9323     {
9324       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9325                                            10 - MovDelay[x][y]);
9326
9327       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9328
9329       if (setup.direct_draw)
9330       {
9331         int dest_x, dest_y;
9332
9333         dest_x = FX + SCREENX(x) * TILEX;
9334         dest_y = FY + SCREENY(y) * TILEY;
9335
9336         BlitBitmap(drawto_field, window,
9337                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9338         SetDrawtoField(DRAW_DIRECT);
9339       }
9340     }
9341   }
9342 }
9343
9344 void MauerWaechst(int x, int y)
9345 {
9346   int delay = 6;
9347
9348   if (!MovDelay[x][y])          /* next animation frame */
9349     MovDelay[x][y] = 3 * delay;
9350
9351   if (MovDelay[x][y])           /* wait some time before next frame */
9352   {
9353     MovDelay[x][y]--;
9354
9355     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9356     {
9357       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9358       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9359
9360       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9361     }
9362
9363     if (!MovDelay[x][y])
9364     {
9365       if (MovDir[x][y] == MV_LEFT)
9366       {
9367         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9368           DrawLevelField(x - 1, y);
9369       }
9370       else if (MovDir[x][y] == MV_RIGHT)
9371       {
9372         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9373           DrawLevelField(x + 1, y);
9374       }
9375       else if (MovDir[x][y] == MV_UP)
9376       {
9377         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9378           DrawLevelField(x, y - 1);
9379       }
9380       else
9381       {
9382         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9383           DrawLevelField(x, y + 1);
9384       }
9385
9386       Feld[x][y] = Store[x][y];
9387       Store[x][y] = 0;
9388       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9389       DrawLevelField(x, y);
9390     }
9391   }
9392 }
9393
9394 void MauerAbleger(int ax, int ay)
9395 {
9396   int element = Feld[ax][ay];
9397   int graphic = el2img(element);
9398   boolean oben_frei = FALSE, unten_frei = FALSE;
9399   boolean links_frei = FALSE, rechts_frei = FALSE;
9400   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9401   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9402   boolean new_wall = FALSE;
9403
9404   if (IS_ANIMATED(graphic))
9405     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9406
9407   if (!MovDelay[ax][ay])        /* start building new wall */
9408     MovDelay[ax][ay] = 6;
9409
9410   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9411   {
9412     MovDelay[ax][ay]--;
9413     if (MovDelay[ax][ay])
9414       return;
9415   }
9416
9417   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9418     oben_frei = TRUE;
9419   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9420     unten_frei = TRUE;
9421   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9422     links_frei = TRUE;
9423   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9424     rechts_frei = TRUE;
9425
9426   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9427       element == EL_EXPANDABLE_WALL_ANY)
9428   {
9429     if (oben_frei)
9430     {
9431       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9432       Store[ax][ay-1] = element;
9433       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9434       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9435         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9436                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9437       new_wall = TRUE;
9438     }
9439     if (unten_frei)
9440     {
9441       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9442       Store[ax][ay+1] = element;
9443       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9444       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9445         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9446                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9447       new_wall = TRUE;
9448     }
9449   }
9450
9451   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9452       element == EL_EXPANDABLE_WALL_ANY ||
9453       element == EL_EXPANDABLE_WALL ||
9454       element == EL_BD_EXPANDABLE_WALL)
9455   {
9456     if (links_frei)
9457     {
9458       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9459       Store[ax-1][ay] = element;
9460       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9461       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9462         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9463                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9464       new_wall = TRUE;
9465     }
9466
9467     if (rechts_frei)
9468     {
9469       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9470       Store[ax+1][ay] = element;
9471       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9472       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9473         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9474                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9475       new_wall = TRUE;
9476     }
9477   }
9478
9479   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9480     DrawLevelField(ax, ay);
9481
9482   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9483     oben_massiv = TRUE;
9484   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9485     unten_massiv = TRUE;
9486   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9487     links_massiv = TRUE;
9488   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9489     rechts_massiv = TRUE;
9490
9491   if (((oben_massiv && unten_massiv) ||
9492        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9493        element == EL_EXPANDABLE_WALL) &&
9494       ((links_massiv && rechts_massiv) ||
9495        element == EL_EXPANDABLE_WALL_VERTICAL))
9496     Feld[ax][ay] = EL_WALL;
9497
9498   if (new_wall)
9499     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9500 }
9501
9502 void MauerAblegerStahl(int ax, int ay)
9503 {
9504   int element = Feld[ax][ay];
9505   int graphic = el2img(element);
9506   boolean oben_frei = FALSE, unten_frei = FALSE;
9507   boolean links_frei = FALSE, rechts_frei = FALSE;
9508   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9509   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9510   boolean new_wall = FALSE;
9511
9512   if (IS_ANIMATED(graphic))
9513     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9514
9515   if (!MovDelay[ax][ay])        /* start building new wall */
9516     MovDelay[ax][ay] = 6;
9517
9518   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9519   {
9520     MovDelay[ax][ay]--;
9521     if (MovDelay[ax][ay])
9522       return;
9523   }
9524
9525   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9526     oben_frei = TRUE;
9527   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9528     unten_frei = TRUE;
9529   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9530     links_frei = TRUE;
9531   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9532     rechts_frei = TRUE;
9533
9534   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9535       element == EL_EXPANDABLE_STEELWALL_ANY)
9536   {
9537     if (oben_frei)
9538     {
9539       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9540       Store[ax][ay-1] = element;
9541       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9542       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9543         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9544                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9545       new_wall = TRUE;
9546     }
9547     if (unten_frei)
9548     {
9549       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9550       Store[ax][ay+1] = element;
9551       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9552       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9553         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9554                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9555       new_wall = TRUE;
9556     }
9557   }
9558
9559   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9560       element == EL_EXPANDABLE_STEELWALL_ANY)
9561   {
9562     if (links_frei)
9563     {
9564       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9565       Store[ax-1][ay] = element;
9566       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9567       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9568         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9569                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9570       new_wall = TRUE;
9571     }
9572
9573     if (rechts_frei)
9574     {
9575       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9576       Store[ax+1][ay] = element;
9577       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9578       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9579         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9580                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9581       new_wall = TRUE;
9582     }
9583   }
9584
9585   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9586     oben_massiv = TRUE;
9587   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9588     unten_massiv = TRUE;
9589   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9590     links_massiv = TRUE;
9591   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9592     rechts_massiv = TRUE;
9593
9594   if (((oben_massiv && unten_massiv) ||
9595        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9596       ((links_massiv && rechts_massiv) ||
9597        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9598     Feld[ax][ay] = EL_WALL;
9599
9600   if (new_wall)
9601     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9602 }
9603
9604 void CheckForDragon(int x, int y)
9605 {
9606   int i, j;
9607   boolean dragon_found = FALSE;
9608   static int xy[4][2] =
9609   {
9610     { 0, -1 },
9611     { -1, 0 },
9612     { +1, 0 },
9613     { 0, +1 }
9614   };
9615
9616   for (i = 0; i < NUM_DIRECTIONS; i++)
9617   {
9618     for (j = 0; j < 4; j++)
9619     {
9620       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9621
9622       if (IN_LEV_FIELD(xx, yy) &&
9623           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9624       {
9625         if (Feld[xx][yy] == EL_DRAGON)
9626           dragon_found = TRUE;
9627       }
9628       else
9629         break;
9630     }
9631   }
9632
9633   if (!dragon_found)
9634   {
9635     for (i = 0; i < NUM_DIRECTIONS; i++)
9636     {
9637       for (j = 0; j < 3; j++)
9638       {
9639         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9640   
9641         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9642         {
9643           Feld[xx][yy] = EL_EMPTY;
9644           DrawLevelField(xx, yy);
9645         }
9646         else
9647           break;
9648       }
9649     }
9650   }
9651 }
9652
9653 static void InitBuggyBase(int x, int y)
9654 {
9655   int element = Feld[x][y];
9656   int activating_delay = FRAMES_PER_SECOND / 4;
9657
9658   ChangeDelay[x][y] =
9659     (element == EL_SP_BUGGY_BASE ?
9660      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9661      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9662      activating_delay :
9663      element == EL_SP_BUGGY_BASE_ACTIVE ?
9664      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9665 }
9666
9667 static void WarnBuggyBase(int x, int y)
9668 {
9669   int i;
9670   static int xy[4][2] =
9671   {
9672     { 0, -1 },
9673     { -1, 0 },
9674     { +1, 0 },
9675     { 0, +1 }
9676   };
9677
9678   for (i = 0; i < NUM_DIRECTIONS; i++)
9679   {
9680     int xx = x + xy[i][0];
9681     int yy = y + xy[i][1];
9682
9683     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9684     {
9685       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9686
9687       break;
9688     }
9689   }
9690 }
9691
9692 static void InitTrap(int x, int y)
9693 {
9694   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9695 }
9696
9697 static void ActivateTrap(int x, int y)
9698 {
9699   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9700 }
9701
9702 static void ChangeActiveTrap(int x, int y)
9703 {
9704   int graphic = IMG_TRAP_ACTIVE;
9705
9706   /* if new animation frame was drawn, correct crumbled sand border */
9707   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9708     DrawLevelFieldCrumbledSand(x, y);
9709 }
9710
9711 static int getSpecialActionElement(int element, int number, int base_element)
9712 {
9713   return (element != EL_EMPTY ? element :
9714           number != -1 ? base_element + number - 1 :
9715           EL_EMPTY);
9716 }
9717
9718 static int getModifiedActionNumber(int value_old, int operator, int operand,
9719                                    int value_min, int value_max)
9720 {
9721   int value_new = (operator == CA_MODE_SET      ? operand :
9722                    operator == CA_MODE_ADD      ? value_old + operand :
9723                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9724                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9725                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9726                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9727                    value_old);
9728
9729   return (value_new < value_min ? value_min :
9730           value_new > value_max ? value_max :
9731           value_new);
9732 }
9733
9734 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9735 {
9736   struct ElementInfo *ei = &element_info[element];
9737   struct ElementChangeInfo *change = &ei->change_page[page];
9738   int target_element = change->target_element;
9739   int action_type = change->action_type;
9740   int action_mode = change->action_mode;
9741   int action_arg = change->action_arg;
9742   int i;
9743
9744   if (!change->has_action)
9745     return;
9746
9747   /* ---------- determine action paramater values -------------------------- */
9748
9749   int level_time_value =
9750     (level.time > 0 ? TimeLeft :
9751      TimePlayed);
9752
9753   int action_arg_element =
9754     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9755      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9756      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9757      EL_EMPTY);
9758
9759   int action_arg_direction =
9760     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9761      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9762      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9763      change->actual_trigger_side :
9764      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9765      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9766      MV_NONE);
9767
9768   int action_arg_number_min =
9769     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9770      CA_ARG_MIN);
9771
9772   int action_arg_number_max =
9773     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9774      action_type == CA_SET_LEVEL_GEMS ? 999 :
9775      action_type == CA_SET_LEVEL_TIME ? 9999 :
9776      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9777      action_type == CA_SET_CE_VALUE ? 9999 :
9778      action_type == CA_SET_CE_SCORE ? 9999 :
9779      CA_ARG_MAX);
9780
9781   int action_arg_number_reset =
9782     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9783      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9784      action_type == CA_SET_LEVEL_TIME ? level.time :
9785      action_type == CA_SET_LEVEL_SCORE ? 0 :
9786 #if USE_NEW_CUSTOM_VALUE
9787      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9788 #else
9789      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9790 #endif
9791      action_type == CA_SET_CE_SCORE ? 0 :
9792      0);
9793
9794   int action_arg_number =
9795     (action_arg <= CA_ARG_MAX ? action_arg :
9796      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9797      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9798      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9799      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9800      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9801      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9802 #if USE_NEW_CUSTOM_VALUE
9803      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9804 #else
9805      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9806 #endif
9807      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9808      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9809      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9810      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9811      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9812      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9813      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9814      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9815      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9816      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9817      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9818      -1);
9819
9820   int action_arg_number_old =
9821     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9822      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9823      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9824      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9825      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9826      0);
9827
9828   int action_arg_number_new =
9829     getModifiedActionNumber(action_arg_number_old,
9830                             action_mode, action_arg_number,
9831                             action_arg_number_min, action_arg_number_max);
9832
9833   int trigger_player_bits =
9834     (change->actual_trigger_player >= EL_PLAYER_1 &&
9835      change->actual_trigger_player <= EL_PLAYER_4 ?
9836      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9837      PLAYER_BITS_ANY);
9838
9839   int action_arg_player_bits =
9840     (action_arg >= CA_ARG_PLAYER_1 &&
9841      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9842      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9843      PLAYER_BITS_ANY);
9844
9845   /* ---------- execute action  -------------------------------------------- */
9846
9847   switch (action_type)
9848   {
9849     case CA_NO_ACTION:
9850     {
9851       return;
9852     }
9853
9854     /* ---------- level actions  ------------------------------------------- */
9855
9856     case CA_RESTART_LEVEL:
9857     {
9858       game.restart_level = TRUE;
9859
9860       break;
9861     }
9862
9863     case CA_SHOW_ENVELOPE:
9864     {
9865       int element = getSpecialActionElement(action_arg_element,
9866                                             action_arg_number, EL_ENVELOPE_1);
9867
9868       if (IS_ENVELOPE(element))
9869         local_player->show_envelope = element;
9870
9871       break;
9872     }
9873
9874     case CA_SET_LEVEL_TIME:
9875     {
9876       if (level.time > 0)       /* only modify limited time value */
9877       {
9878         TimeLeft = action_arg_number_new;
9879
9880 #if 1
9881         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9882
9883         DisplayGameControlValues();
9884 #else
9885         DrawGameValue_Time(TimeLeft);
9886 #endif
9887
9888         if (!TimeLeft && setup.time_limit)
9889           for (i = 0; i < MAX_PLAYERS; i++)
9890             KillPlayer(&stored_player[i]);
9891       }
9892
9893       break;
9894     }
9895
9896     case CA_SET_LEVEL_SCORE:
9897     {
9898       local_player->score = action_arg_number_new;
9899
9900 #if 1
9901       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9902
9903       DisplayGameControlValues();
9904 #else
9905       DrawGameValue_Score(local_player->score);
9906 #endif
9907
9908       break;
9909     }
9910
9911     case CA_SET_LEVEL_GEMS:
9912     {
9913       local_player->gems_still_needed = action_arg_number_new;
9914
9915 #if 1
9916       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9917
9918       DisplayGameControlValues();
9919 #else
9920       DrawGameValue_Emeralds(local_player->gems_still_needed);
9921 #endif
9922
9923       break;
9924     }
9925
9926 #if !USE_PLAYER_GRAVITY
9927     case CA_SET_LEVEL_GRAVITY:
9928     {
9929       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9930                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9931                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9932                       game.gravity);
9933       break;
9934     }
9935 #endif
9936
9937     case CA_SET_LEVEL_WIND:
9938     {
9939       game.wind_direction = action_arg_direction;
9940
9941       break;
9942     }
9943
9944     /* ---------- player actions  ------------------------------------------ */
9945
9946     case CA_MOVE_PLAYER:
9947     {
9948       /* automatically move to the next field in specified direction */
9949       for (i = 0; i < MAX_PLAYERS; i++)
9950         if (trigger_player_bits & (1 << i))
9951           stored_player[i].programmed_action = action_arg_direction;
9952
9953       break;
9954     }
9955
9956     case CA_EXIT_PLAYER:
9957     {
9958       for (i = 0; i < MAX_PLAYERS; i++)
9959         if (action_arg_player_bits & (1 << i))
9960           PlayerWins(&stored_player[i]);
9961
9962       break;
9963     }
9964
9965     case CA_KILL_PLAYER:
9966     {
9967       for (i = 0; i < MAX_PLAYERS; i++)
9968         if (action_arg_player_bits & (1 << i))
9969           KillPlayer(&stored_player[i]);
9970
9971       break;
9972     }
9973
9974     case CA_SET_PLAYER_KEYS:
9975     {
9976       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9977       int element = getSpecialActionElement(action_arg_element,
9978                                             action_arg_number, EL_KEY_1);
9979
9980       if (IS_KEY(element))
9981       {
9982         for (i = 0; i < MAX_PLAYERS; i++)
9983         {
9984           if (trigger_player_bits & (1 << i))
9985           {
9986             stored_player[i].key[KEY_NR(element)] = key_state;
9987
9988             DrawGameDoorValues();
9989           }
9990         }
9991       }
9992
9993       break;
9994     }
9995
9996     case CA_SET_PLAYER_SPEED:
9997     {
9998       for (i = 0; i < MAX_PLAYERS; i++)
9999       {
10000         if (trigger_player_bits & (1 << i))
10001         {
10002           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10003
10004           if (action_arg == CA_ARG_SPEED_FASTER &&
10005               stored_player[i].cannot_move)
10006           {
10007             action_arg_number = STEPSIZE_VERY_SLOW;
10008           }
10009           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10010                    action_arg == CA_ARG_SPEED_FASTER)
10011           {
10012             action_arg_number = 2;
10013             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10014                            CA_MODE_MULTIPLY);
10015           }
10016           else if (action_arg == CA_ARG_NUMBER_RESET)
10017           {
10018             action_arg_number = level.initial_player_stepsize[i];
10019           }
10020
10021           move_stepsize =
10022             getModifiedActionNumber(move_stepsize,
10023                                     action_mode,
10024                                     action_arg_number,
10025                                     action_arg_number_min,
10026                                     action_arg_number_max);
10027
10028           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10029         }
10030       }
10031
10032       break;
10033     }
10034
10035     case CA_SET_PLAYER_SHIELD:
10036     {
10037       for (i = 0; i < MAX_PLAYERS; i++)
10038       {
10039         if (trigger_player_bits & (1 << i))
10040         {
10041           if (action_arg == CA_ARG_SHIELD_OFF)
10042           {
10043             stored_player[i].shield_normal_time_left = 0;
10044             stored_player[i].shield_deadly_time_left = 0;
10045           }
10046           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10047           {
10048             stored_player[i].shield_normal_time_left = 999999;
10049           }
10050           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10051           {
10052             stored_player[i].shield_normal_time_left = 999999;
10053             stored_player[i].shield_deadly_time_left = 999999;
10054           }
10055         }
10056       }
10057
10058       break;
10059     }
10060
10061 #if USE_PLAYER_GRAVITY
10062     case CA_SET_PLAYER_GRAVITY:
10063     {
10064       for (i = 0; i < MAX_PLAYERS; i++)
10065       {
10066         if (trigger_player_bits & (1 << i))
10067         {
10068           stored_player[i].gravity =
10069             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10070              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10071              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10072              stored_player[i].gravity);
10073         }
10074       }
10075
10076       break;
10077     }
10078 #endif
10079
10080     case CA_SET_PLAYER_ARTWORK:
10081     {
10082       for (i = 0; i < MAX_PLAYERS; i++)
10083       {
10084         if (trigger_player_bits & (1 << i))
10085         {
10086           int artwork_element = action_arg_element;
10087
10088           if (action_arg == CA_ARG_ELEMENT_RESET)
10089             artwork_element =
10090               (level.use_artwork_element[i] ? level.artwork_element[i] :
10091                stored_player[i].element_nr);
10092
10093 #if USE_GFX_RESET_PLAYER_ARTWORK
10094           if (stored_player[i].artwork_element != artwork_element)
10095             stored_player[i].Frame = 0;
10096 #endif
10097
10098           stored_player[i].artwork_element = artwork_element;
10099
10100           SetPlayerWaiting(&stored_player[i], FALSE);
10101
10102           /* set number of special actions for bored and sleeping animation */
10103           stored_player[i].num_special_action_bored =
10104             get_num_special_action(artwork_element,
10105                                    ACTION_BORING_1, ACTION_BORING_LAST);
10106           stored_player[i].num_special_action_sleeping =
10107             get_num_special_action(artwork_element,
10108                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10109         }
10110       }
10111
10112       break;
10113     }
10114
10115     /* ---------- CE actions  ---------------------------------------------- */
10116
10117     case CA_SET_CE_VALUE:
10118     {
10119 #if USE_NEW_CUSTOM_VALUE
10120       int last_ce_value = CustomValue[x][y];
10121
10122       CustomValue[x][y] = action_arg_number_new;
10123
10124       if (CustomValue[x][y] != last_ce_value)
10125       {
10126         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10127         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10128
10129         if (CustomValue[x][y] == 0)
10130         {
10131           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10132           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10133         }
10134       }
10135 #endif
10136
10137       break;
10138     }
10139
10140     case CA_SET_CE_SCORE:
10141     {
10142 #if USE_NEW_CUSTOM_VALUE
10143       int last_ce_score = ei->collect_score;
10144
10145       ei->collect_score = action_arg_number_new;
10146
10147       if (ei->collect_score != last_ce_score)
10148       {
10149         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10150         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10151
10152         if (ei->collect_score == 0)
10153         {
10154           int xx, yy;
10155
10156           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10157           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10158
10159           /*
10160             This is a very special case that seems to be a mixture between
10161             CheckElementChange() and CheckTriggeredElementChange(): while
10162             the first one only affects single elements that are triggered
10163             directly, the second one affects multiple elements in the playfield
10164             that are triggered indirectly by another element. This is a third
10165             case: Changing the CE score always affects multiple identical CEs,
10166             so every affected CE must be checked, not only the single CE for
10167             which the CE score was changed in the first place (as every instance
10168             of that CE shares the same CE score, and therefore also can change)!
10169           */
10170           SCAN_PLAYFIELD(xx, yy)
10171           {
10172             if (Feld[xx][yy] == element)
10173               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10174                                  CE_SCORE_GETS_ZERO);
10175           }
10176         }
10177       }
10178 #endif
10179
10180       break;
10181     }
10182
10183     /* ---------- engine actions  ------------------------------------------ */
10184
10185     case CA_SET_ENGINE_SCAN_MODE:
10186     {
10187       InitPlayfieldScanMode(action_arg);
10188
10189       break;
10190     }
10191
10192     default:
10193       break;
10194   }
10195 }
10196
10197 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10198 {
10199   int old_element = Feld[x][y];
10200   int new_element = GetElementFromGroupElement(element);
10201   int previous_move_direction = MovDir[x][y];
10202 #if USE_NEW_CUSTOM_VALUE
10203   int last_ce_value = CustomValue[x][y];
10204 #endif
10205   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10206   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10207   boolean add_player_onto_element = (new_element_is_player &&
10208 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10209                                      /* this breaks SnakeBite when a snake is
10210                                         halfway through a door that closes */
10211                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10212                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10213 #endif
10214                                      IS_WALKABLE(old_element));
10215
10216 #if 0
10217   /* check if element under the player changes from accessible to unaccessible
10218      (needed for special case of dropping element which then changes) */
10219   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10220       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10221   {
10222     Bang(x, y);
10223
10224     return;
10225   }
10226 #endif
10227
10228   if (!add_player_onto_element)
10229   {
10230     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10231       RemoveMovingField(x, y);
10232     else
10233       RemoveField(x, y);
10234
10235     Feld[x][y] = new_element;
10236
10237 #if !USE_GFX_RESET_GFX_ANIMATION
10238     ResetGfxAnimation(x, y);
10239     ResetRandomAnimationValue(x, y);
10240 #endif
10241
10242     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10243       MovDir[x][y] = previous_move_direction;
10244
10245 #if USE_NEW_CUSTOM_VALUE
10246     if (element_info[new_element].use_last_ce_value)
10247       CustomValue[x][y] = last_ce_value;
10248 #endif
10249
10250     InitField_WithBug1(x, y, FALSE);
10251
10252     new_element = Feld[x][y];   /* element may have changed */
10253
10254 #if USE_GFX_RESET_GFX_ANIMATION
10255     ResetGfxAnimation(x, y);
10256     ResetRandomAnimationValue(x, y);
10257 #endif
10258
10259     DrawLevelField(x, y);
10260
10261     if (GFX_CRUMBLED(new_element))
10262       DrawLevelFieldCrumbledSandNeighbours(x, y);
10263   }
10264
10265 #if 1
10266   /* check if element under the player changes from accessible to unaccessible
10267      (needed for special case of dropping element which then changes) */
10268   /* (must be checked after creating new element for walkable group elements) */
10269 #if USE_FIX_KILLED_BY_NON_WALKABLE
10270   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10271       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10272   {
10273     Bang(x, y);
10274
10275     return;
10276   }
10277 #else
10278   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10279       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10280   {
10281     Bang(x, y);
10282
10283     return;
10284   }
10285 #endif
10286 #endif
10287
10288   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10289   if (new_element_is_player)
10290     RelocatePlayer(x, y, new_element);
10291
10292   if (is_change)
10293     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10294
10295   TestIfBadThingTouchesPlayer(x, y);
10296   TestIfPlayerTouchesCustomElement(x, y);
10297   TestIfElementTouchesCustomElement(x, y);
10298 }
10299
10300 static void CreateField(int x, int y, int element)
10301 {
10302   CreateFieldExt(x, y, element, FALSE);
10303 }
10304
10305 static void CreateElementFromChange(int x, int y, int element)
10306 {
10307   element = GET_VALID_RUNTIME_ELEMENT(element);
10308
10309 #if USE_STOP_CHANGED_ELEMENTS
10310   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10311   {
10312     int old_element = Feld[x][y];
10313
10314     /* prevent changed element from moving in same engine frame
10315        unless both old and new element can either fall or move */
10316     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10317         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10318       Stop[x][y] = TRUE;
10319   }
10320 #endif
10321
10322   CreateFieldExt(x, y, element, TRUE);
10323 }
10324
10325 static boolean ChangeElement(int x, int y, int element, int page)
10326 {
10327   struct ElementInfo *ei = &element_info[element];
10328   struct ElementChangeInfo *change = &ei->change_page[page];
10329   int ce_value = CustomValue[x][y];
10330   int ce_score = ei->collect_score;
10331   int target_element;
10332   int old_element = Feld[x][y];
10333
10334   /* always use default change event to prevent running into a loop */
10335   if (ChangeEvent[x][y] == -1)
10336     ChangeEvent[x][y] = CE_DELAY;
10337
10338   if (ChangeEvent[x][y] == CE_DELAY)
10339   {
10340     /* reset actual trigger element, trigger player and action element */
10341     change->actual_trigger_element = EL_EMPTY;
10342     change->actual_trigger_player = EL_PLAYER_1;
10343     change->actual_trigger_side = CH_SIDE_NONE;
10344     change->actual_trigger_ce_value = 0;
10345     change->actual_trigger_ce_score = 0;
10346   }
10347
10348   /* do not change elements more than a specified maximum number of changes */
10349   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10350     return FALSE;
10351
10352   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10353
10354   if (change->explode)
10355   {
10356     Bang(x, y);
10357
10358     return TRUE;
10359   }
10360
10361   if (change->use_target_content)
10362   {
10363     boolean complete_replace = TRUE;
10364     boolean can_replace[3][3];
10365     int xx, yy;
10366
10367     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10368     {
10369       boolean is_empty;
10370       boolean is_walkable;
10371       boolean is_diggable;
10372       boolean is_collectible;
10373       boolean is_removable;
10374       boolean is_destructible;
10375       int ex = x + xx - 1;
10376       int ey = y + yy - 1;
10377       int content_element = change->target_content.e[xx][yy];
10378       int e;
10379
10380       can_replace[xx][yy] = TRUE;
10381
10382       if (ex == x && ey == y)   /* do not check changing element itself */
10383         continue;
10384
10385       if (content_element == EL_EMPTY_SPACE)
10386       {
10387         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10388
10389         continue;
10390       }
10391
10392       if (!IN_LEV_FIELD(ex, ey))
10393       {
10394         can_replace[xx][yy] = FALSE;
10395         complete_replace = FALSE;
10396
10397         continue;
10398       }
10399
10400       e = Feld[ex][ey];
10401
10402       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10403         e = MovingOrBlocked2Element(ex, ey);
10404
10405       is_empty = (IS_FREE(ex, ey) ||
10406                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10407
10408       is_walkable     = (is_empty || IS_WALKABLE(e));
10409       is_diggable     = (is_empty || IS_DIGGABLE(e));
10410       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10411       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10412       is_removable    = (is_diggable || is_collectible);
10413
10414       can_replace[xx][yy] =
10415         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10416           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10417           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10418           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10419           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10420           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10421          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10422
10423       if (!can_replace[xx][yy])
10424         complete_replace = FALSE;
10425     }
10426
10427     if (!change->only_if_complete || complete_replace)
10428     {
10429       boolean something_has_changed = FALSE;
10430
10431       if (change->only_if_complete && change->use_random_replace &&
10432           RND(100) < change->random_percentage)
10433         return FALSE;
10434
10435       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10436       {
10437         int ex = x + xx - 1;
10438         int ey = y + yy - 1;
10439         int content_element;
10440
10441         if (can_replace[xx][yy] && (!change->use_random_replace ||
10442                                     RND(100) < change->random_percentage))
10443         {
10444           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10445             RemoveMovingField(ex, ey);
10446
10447           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10448
10449           content_element = change->target_content.e[xx][yy];
10450           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10451                                               ce_value, ce_score);
10452
10453           CreateElementFromChange(ex, ey, target_element);
10454
10455           something_has_changed = TRUE;
10456
10457           /* for symmetry reasons, freeze newly created border elements */
10458           if (ex != x || ey != y)
10459             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10460         }
10461       }
10462
10463       if (something_has_changed)
10464       {
10465         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10466         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10467       }
10468     }
10469   }
10470   else
10471   {
10472     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10473                                         ce_value, ce_score);
10474
10475     if (element == EL_DIAGONAL_GROWING ||
10476         element == EL_DIAGONAL_SHRINKING)
10477     {
10478       target_element = Store[x][y];
10479
10480       Store[x][y] = EL_EMPTY;
10481     }
10482
10483     CreateElementFromChange(x, y, target_element);
10484
10485     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10486     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10487   }
10488
10489   /* this uses direct change before indirect change */
10490   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10491
10492   return TRUE;
10493 }
10494
10495 #if USE_NEW_DELAYED_ACTION
10496
10497 static void HandleElementChange(int x, int y, int page)
10498 {
10499   int element = MovingOrBlocked2Element(x, y);
10500   struct ElementInfo *ei = &element_info[element];
10501   struct ElementChangeInfo *change = &ei->change_page[page];
10502
10503 #ifdef DEBUG
10504   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10505       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10506   {
10507     printf("\n\n");
10508     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10509            x, y, element, element_info[element].token_name);
10510     printf("HandleElementChange(): This should never happen!\n");
10511     printf("\n\n");
10512   }
10513 #endif
10514
10515   /* this can happen with classic bombs on walkable, changing elements */
10516   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10517   {
10518 #if 0
10519     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10520       ChangeDelay[x][y] = 0;
10521 #endif
10522
10523     return;
10524   }
10525
10526   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10527   {
10528     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10529
10530     if (change->can_change)
10531     {
10532 #if 1
10533       /* !!! not clear why graphic animation should be reset at all here !!! */
10534       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10535 #if USE_GFX_RESET_WHEN_NOT_MOVING
10536       /* when a custom element is about to change (for example by change delay),
10537          do not reset graphic animation when the custom element is moving */
10538       if (!IS_MOVING(x, y))
10539 #endif
10540       {
10541         ResetGfxAnimation(x, y);
10542         ResetRandomAnimationValue(x, y);
10543       }
10544 #endif
10545
10546       if (change->pre_change_function)
10547         change->pre_change_function(x, y);
10548     }
10549   }
10550
10551   ChangeDelay[x][y]--;
10552
10553   if (ChangeDelay[x][y] != 0)           /* continue element change */
10554   {
10555     if (change->can_change)
10556     {
10557       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10558
10559       if (IS_ANIMATED(graphic))
10560         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10561
10562       if (change->change_function)
10563         change->change_function(x, y);
10564     }
10565   }
10566   else                                  /* finish element change */
10567   {
10568     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10569     {
10570       page = ChangePage[x][y];
10571       ChangePage[x][y] = -1;
10572
10573       change = &ei->change_page[page];
10574     }
10575
10576     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10577     {
10578       ChangeDelay[x][y] = 1;            /* try change after next move step */
10579       ChangePage[x][y] = page;          /* remember page to use for change */
10580
10581       return;
10582     }
10583
10584     if (change->can_change)
10585     {
10586       if (ChangeElement(x, y, element, page))
10587       {
10588         if (change->post_change_function)
10589           change->post_change_function(x, y);
10590       }
10591     }
10592
10593     if (change->has_action)
10594       ExecuteCustomElementAction(x, y, element, page);
10595   }
10596 }
10597
10598 #else
10599
10600 static void HandleElementChange(int x, int y, int page)
10601 {
10602   int element = MovingOrBlocked2Element(x, y);
10603   struct ElementInfo *ei = &element_info[element];
10604   struct ElementChangeInfo *change = &ei->change_page[page];
10605
10606 #ifdef DEBUG
10607   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10608   {
10609     printf("\n\n");
10610     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10611            x, y, element, element_info[element].token_name);
10612     printf("HandleElementChange(): This should never happen!\n");
10613     printf("\n\n");
10614   }
10615 #endif
10616
10617   /* this can happen with classic bombs on walkable, changing elements */
10618   if (!CAN_CHANGE(element))
10619   {
10620 #if 0
10621     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10622       ChangeDelay[x][y] = 0;
10623 #endif
10624
10625     return;
10626   }
10627
10628   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10629   {
10630     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10631
10632     ResetGfxAnimation(x, y);
10633     ResetRandomAnimationValue(x, y);
10634
10635     if (change->pre_change_function)
10636       change->pre_change_function(x, y);
10637   }
10638
10639   ChangeDelay[x][y]--;
10640
10641   if (ChangeDelay[x][y] != 0)           /* continue element change */
10642   {
10643     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10644
10645     if (IS_ANIMATED(graphic))
10646       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10647
10648     if (change->change_function)
10649       change->change_function(x, y);
10650   }
10651   else                                  /* finish element change */
10652   {
10653     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10654     {
10655       page = ChangePage[x][y];
10656       ChangePage[x][y] = -1;
10657
10658       change = &ei->change_page[page];
10659     }
10660
10661     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10662     {
10663       ChangeDelay[x][y] = 1;            /* try change after next move step */
10664       ChangePage[x][y] = page;          /* remember page to use for change */
10665
10666       return;
10667     }
10668
10669     if (ChangeElement(x, y, element, page))
10670     {
10671       if (change->post_change_function)
10672         change->post_change_function(x, y);
10673     }
10674   }
10675 }
10676
10677 #endif
10678
10679 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10680                                               int trigger_element,
10681                                               int trigger_event,
10682                                               int trigger_player,
10683                                               int trigger_side,
10684                                               int trigger_page)
10685 {
10686   boolean change_done_any = FALSE;
10687   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10688   int i;
10689
10690   if (!(trigger_events[trigger_element][trigger_event]))
10691     return FALSE;
10692
10693 #if 0
10694   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10695          trigger_event, recursion_loop_depth, recursion_loop_detected,
10696          recursion_loop_element, EL_NAME(recursion_loop_element));
10697 #endif
10698
10699   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10700
10701   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10702   {
10703     int element = EL_CUSTOM_START + i;
10704     boolean change_done = FALSE;
10705     int p;
10706
10707     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10708         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10709       continue;
10710
10711     for (p = 0; p < element_info[element].num_change_pages; p++)
10712     {
10713       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10714
10715       if (change->can_change_or_has_action &&
10716           change->has_event[trigger_event] &&
10717           change->trigger_side & trigger_side &&
10718           change->trigger_player & trigger_player &&
10719           change->trigger_page & trigger_page_bits &&
10720           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10721       {
10722         change->actual_trigger_element = trigger_element;
10723         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10724         change->actual_trigger_side = trigger_side;
10725         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10726         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10727
10728         if ((change->can_change && !change_done) || change->has_action)
10729         {
10730           int x, y;
10731
10732           SCAN_PLAYFIELD(x, y)
10733           {
10734             if (Feld[x][y] == element)
10735             {
10736               if (change->can_change && !change_done)
10737               {
10738                 ChangeDelay[x][y] = 1;
10739                 ChangeEvent[x][y] = trigger_event;
10740
10741                 HandleElementChange(x, y, p);
10742               }
10743 #if USE_NEW_DELAYED_ACTION
10744               else if (change->has_action)
10745               {
10746                 ExecuteCustomElementAction(x, y, element, p);
10747                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10748               }
10749 #else
10750               if (change->has_action)
10751               {
10752                 ExecuteCustomElementAction(x, y, element, p);
10753                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10754               }
10755 #endif
10756             }
10757           }
10758
10759           if (change->can_change)
10760           {
10761             change_done = TRUE;
10762             change_done_any = TRUE;
10763           }
10764         }
10765       }
10766     }
10767   }
10768
10769   RECURSION_LOOP_DETECTION_END();
10770
10771   return change_done_any;
10772 }
10773
10774 static boolean CheckElementChangeExt(int x, int y,
10775                                      int element,
10776                                      int trigger_element,
10777                                      int trigger_event,
10778                                      int trigger_player,
10779                                      int trigger_side)
10780 {
10781   boolean change_done = FALSE;
10782   int p;
10783
10784   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10785       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10786     return FALSE;
10787
10788   if (Feld[x][y] == EL_BLOCKED)
10789   {
10790     Blocked2Moving(x, y, &x, &y);
10791     element = Feld[x][y];
10792   }
10793
10794 #if 0
10795   /* check if element has already changed */
10796   if (Feld[x][y] != element)
10797     return FALSE;
10798 #else
10799   /* check if element has already changed or is about to change after moving */
10800   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10801        Feld[x][y] != element) ||
10802
10803       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10804        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10805         ChangePage[x][y] != -1)))
10806     return FALSE;
10807 #endif
10808
10809 #if 0
10810   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10811          trigger_event, recursion_loop_depth, recursion_loop_detected,
10812          recursion_loop_element, EL_NAME(recursion_loop_element));
10813 #endif
10814
10815   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10816
10817   for (p = 0; p < element_info[element].num_change_pages; p++)
10818   {
10819     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10820
10821     /* check trigger element for all events where the element that is checked
10822        for changing interacts with a directly adjacent element -- this is
10823        different to element changes that affect other elements to change on the
10824        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10825     boolean check_trigger_element =
10826       (trigger_event == CE_TOUCHING_X ||
10827        trigger_event == CE_HITTING_X ||
10828        trigger_event == CE_HIT_BY_X ||
10829 #if 1
10830        /* this one was forgotten until 3.2.3 */
10831        trigger_event == CE_DIGGING_X);
10832 #endif
10833
10834     if (change->can_change_or_has_action &&
10835         change->has_event[trigger_event] &&
10836         change->trigger_side & trigger_side &&
10837         change->trigger_player & trigger_player &&
10838         (!check_trigger_element ||
10839          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10840     {
10841       change->actual_trigger_element = trigger_element;
10842       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10843       change->actual_trigger_side = trigger_side;
10844       change->actual_trigger_ce_value = CustomValue[x][y];
10845       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10846
10847       /* special case: trigger element not at (x,y) position for some events */
10848       if (check_trigger_element)
10849       {
10850         static struct
10851         {
10852           int dx, dy;
10853         } move_xy[] =
10854           {
10855             {  0,  0 },
10856             { -1,  0 },
10857             { +1,  0 },
10858             {  0,  0 },
10859             {  0, -1 },
10860             {  0,  0 }, { 0, 0 }, { 0, 0 },
10861             {  0, +1 }
10862           };
10863
10864         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10865         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10866
10867         change->actual_trigger_ce_value = CustomValue[xx][yy];
10868         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10869       }
10870
10871       if (change->can_change && !change_done)
10872       {
10873         ChangeDelay[x][y] = 1;
10874         ChangeEvent[x][y] = trigger_event;
10875
10876         HandleElementChange(x, y, p);
10877
10878         change_done = TRUE;
10879       }
10880 #if USE_NEW_DELAYED_ACTION
10881       else if (change->has_action)
10882       {
10883         ExecuteCustomElementAction(x, y, element, p);
10884         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10885       }
10886 #else
10887       if (change->has_action)
10888       {
10889         ExecuteCustomElementAction(x, y, element, p);
10890         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10891       }
10892 #endif
10893     }
10894   }
10895
10896   RECURSION_LOOP_DETECTION_END();
10897
10898   return change_done;
10899 }
10900
10901 static void PlayPlayerSound(struct PlayerInfo *player)
10902 {
10903   int jx = player->jx, jy = player->jy;
10904   int sound_element = player->artwork_element;
10905   int last_action = player->last_action_waiting;
10906   int action = player->action_waiting;
10907
10908   if (player->is_waiting)
10909   {
10910     if (action != last_action)
10911       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10912     else
10913       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10914   }
10915   else
10916   {
10917     if (action != last_action)
10918       StopSound(element_info[sound_element].sound[last_action]);
10919
10920     if (last_action == ACTION_SLEEPING)
10921       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10922   }
10923 }
10924
10925 static void PlayAllPlayersSound()
10926 {
10927   int i;
10928
10929   for (i = 0; i < MAX_PLAYERS; i++)
10930     if (stored_player[i].active)
10931       PlayPlayerSound(&stored_player[i]);
10932 }
10933
10934 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10935 {
10936   boolean last_waiting = player->is_waiting;
10937   int move_dir = player->MovDir;
10938
10939   player->dir_waiting = move_dir;
10940   player->last_action_waiting = player->action_waiting;
10941
10942   if (is_waiting)
10943   {
10944     if (!last_waiting)          /* not waiting -> waiting */
10945     {
10946       player->is_waiting = TRUE;
10947
10948       player->frame_counter_bored =
10949         FrameCounter +
10950         game.player_boring_delay_fixed +
10951         GetSimpleRandom(game.player_boring_delay_random);
10952       player->frame_counter_sleeping =
10953         FrameCounter +
10954         game.player_sleeping_delay_fixed +
10955         GetSimpleRandom(game.player_sleeping_delay_random);
10956
10957       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10958     }
10959
10960     if (game.player_sleeping_delay_fixed +
10961         game.player_sleeping_delay_random > 0 &&
10962         player->anim_delay_counter == 0 &&
10963         player->post_delay_counter == 0 &&
10964         FrameCounter >= player->frame_counter_sleeping)
10965       player->is_sleeping = TRUE;
10966     else if (game.player_boring_delay_fixed +
10967              game.player_boring_delay_random > 0 &&
10968              FrameCounter >= player->frame_counter_bored)
10969       player->is_bored = TRUE;
10970
10971     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10972                               player->is_bored ? ACTION_BORING :
10973                               ACTION_WAITING);
10974
10975     if (player->is_sleeping && player->use_murphy)
10976     {
10977       /* special case for sleeping Murphy when leaning against non-free tile */
10978
10979       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10980           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10981            !IS_MOVING(player->jx - 1, player->jy)))
10982         move_dir = MV_LEFT;
10983       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10984                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10985                 !IS_MOVING(player->jx + 1, player->jy)))
10986         move_dir = MV_RIGHT;
10987       else
10988         player->is_sleeping = FALSE;
10989
10990       player->dir_waiting = move_dir;
10991     }
10992
10993     if (player->is_sleeping)
10994     {
10995       if (player->num_special_action_sleeping > 0)
10996       {
10997         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10998         {
10999           int last_special_action = player->special_action_sleeping;
11000           int num_special_action = player->num_special_action_sleeping;
11001           int special_action =
11002             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11003              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11004              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11005              last_special_action + 1 : ACTION_SLEEPING);
11006           int special_graphic =
11007             el_act_dir2img(player->artwork_element, special_action, move_dir);
11008
11009           player->anim_delay_counter =
11010             graphic_info[special_graphic].anim_delay_fixed +
11011             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11012           player->post_delay_counter =
11013             graphic_info[special_graphic].post_delay_fixed +
11014             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11015
11016           player->special_action_sleeping = special_action;
11017         }
11018
11019         if (player->anim_delay_counter > 0)
11020         {
11021           player->action_waiting = player->special_action_sleeping;
11022           player->anim_delay_counter--;
11023         }
11024         else if (player->post_delay_counter > 0)
11025         {
11026           player->post_delay_counter--;
11027         }
11028       }
11029     }
11030     else if (player->is_bored)
11031     {
11032       if (player->num_special_action_bored > 0)
11033       {
11034         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11035         {
11036           int special_action =
11037             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11038           int special_graphic =
11039             el_act_dir2img(player->artwork_element, special_action, move_dir);
11040
11041           player->anim_delay_counter =
11042             graphic_info[special_graphic].anim_delay_fixed +
11043             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11044           player->post_delay_counter =
11045             graphic_info[special_graphic].post_delay_fixed +
11046             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11047
11048           player->special_action_bored = special_action;
11049         }
11050
11051         if (player->anim_delay_counter > 0)
11052         {
11053           player->action_waiting = player->special_action_bored;
11054           player->anim_delay_counter--;
11055         }
11056         else if (player->post_delay_counter > 0)
11057         {
11058           player->post_delay_counter--;
11059         }
11060       }
11061     }
11062   }
11063   else if (last_waiting)        /* waiting -> not waiting */
11064   {
11065     player->is_waiting = FALSE;
11066     player->is_bored = FALSE;
11067     player->is_sleeping = FALSE;
11068
11069     player->frame_counter_bored = -1;
11070     player->frame_counter_sleeping = -1;
11071
11072     player->anim_delay_counter = 0;
11073     player->post_delay_counter = 0;
11074
11075     player->dir_waiting = player->MovDir;
11076     player->action_waiting = ACTION_DEFAULT;
11077
11078     player->special_action_bored = ACTION_DEFAULT;
11079     player->special_action_sleeping = ACTION_DEFAULT;
11080   }
11081 }
11082
11083 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11084 {
11085   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11086   int left      = player_action & JOY_LEFT;
11087   int right     = player_action & JOY_RIGHT;
11088   int up        = player_action & JOY_UP;
11089   int down      = player_action & JOY_DOWN;
11090   int button1   = player_action & JOY_BUTTON_1;
11091   int button2   = player_action & JOY_BUTTON_2;
11092   int dx        = (left ? -1 : right ? 1 : 0);
11093   int dy        = (up   ? -1 : down  ? 1 : 0);
11094
11095   if (!player->active || tape.pausing)
11096     return 0;
11097
11098   if (player_action)
11099   {
11100     if (button1)
11101       snapped = SnapField(player, dx, dy);
11102     else
11103     {
11104       if (button2)
11105         dropped = DropElement(player);
11106
11107       moved = MovePlayer(player, dx, dy);
11108     }
11109
11110     if (tape.single_step && tape.recording && !tape.pausing)
11111     {
11112       if (button1 || (dropped && !moved))
11113       {
11114         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11115         SnapField(player, 0, 0);                /* stop snapping */
11116       }
11117     }
11118
11119     SetPlayerWaiting(player, FALSE);
11120
11121     return player_action;
11122   }
11123   else
11124   {
11125     /* no actions for this player (no input at player's configured device) */
11126
11127     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11128     SnapField(player, 0, 0);
11129     CheckGravityMovementWhenNotMoving(player);
11130
11131     if (player->MovPos == 0)
11132       SetPlayerWaiting(player, TRUE);
11133
11134     if (player->MovPos == 0)    /* needed for tape.playing */
11135       player->is_moving = FALSE;
11136
11137     player->is_dropping = FALSE;
11138     player->is_dropping_pressed = FALSE;
11139     player->drop_pressed_delay = 0;
11140
11141     return 0;
11142   }
11143 }
11144
11145 static void CheckLevelTime()
11146 {
11147   int i;
11148
11149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11150   {
11151     if (level.native_em_level->lev->home == 0)  /* all players at home */
11152     {
11153       PlayerWins(local_player);
11154
11155       AllPlayersGone = TRUE;
11156
11157       level.native_em_level->lev->home = -1;
11158     }
11159
11160     if (level.native_em_level->ply[0]->alive == 0 &&
11161         level.native_em_level->ply[1]->alive == 0 &&
11162         level.native_em_level->ply[2]->alive == 0 &&
11163         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11164       AllPlayersGone = TRUE;
11165   }
11166
11167   if (TimeFrames >= FRAMES_PER_SECOND)
11168   {
11169     TimeFrames = 0;
11170     TapeTime++;
11171
11172     for (i = 0; i < MAX_PLAYERS; i++)
11173     {
11174       struct PlayerInfo *player = &stored_player[i];
11175
11176       if (SHIELD_ON(player))
11177       {
11178         player->shield_normal_time_left--;
11179
11180         if (player->shield_deadly_time_left > 0)
11181           player->shield_deadly_time_left--;
11182       }
11183     }
11184
11185     if (!local_player->LevelSolved && !level.use_step_counter)
11186     {
11187       TimePlayed++;
11188
11189       if (TimeLeft > 0)
11190       {
11191         TimeLeft--;
11192
11193         if (TimeLeft <= 10 && setup.time_limit)
11194           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11195
11196 #if 1
11197         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11198
11199         DisplayGameControlValues();
11200 #else
11201         DrawGameValue_Time(TimeLeft);
11202 #endif
11203
11204         if (!TimeLeft && setup.time_limit)
11205         {
11206           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11207             level.native_em_level->lev->killed_out_of_time = TRUE;
11208           else
11209             for (i = 0; i < MAX_PLAYERS; i++)
11210               KillPlayer(&stored_player[i]);
11211         }
11212       }
11213 #if 1
11214       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11215       {
11216         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11217
11218         DisplayGameControlValues();
11219       }
11220 #else
11221       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11222         DrawGameValue_Time(TimePlayed);
11223 #endif
11224
11225       level.native_em_level->lev->time =
11226         (level.time == 0 ? TimePlayed : TimeLeft);
11227     }
11228
11229     if (tape.recording || tape.playing)
11230       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11231   }
11232
11233   UpdateGameDoorValues();
11234   DrawGameDoorValues();
11235 }
11236
11237 void AdvanceFrameAndPlayerCounters(int player_nr)
11238 {
11239   int i;
11240
11241   /* advance frame counters (global frame counter and time frame counter) */
11242   FrameCounter++;
11243   TimeFrames++;
11244
11245   /* advance player counters (counters for move delay, move animation etc.) */
11246   for (i = 0; i < MAX_PLAYERS; i++)
11247   {
11248     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11249     int move_delay_value = stored_player[i].move_delay_value;
11250     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11251
11252     if (!advance_player_counters)       /* not all players may be affected */
11253       continue;
11254
11255 #if USE_NEW_PLAYER_ANIM
11256     if (move_frames == 0)       /* less than one move per game frame */
11257     {
11258       int stepsize = TILEX / move_delay_value;
11259       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11260       int count = (stored_player[i].is_moving ?
11261                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11262
11263       if (count % delay == 0)
11264         move_frames = 1;
11265     }
11266 #endif
11267
11268     stored_player[i].Frame += move_frames;
11269
11270     if (stored_player[i].MovPos != 0)
11271       stored_player[i].StepFrame += move_frames;
11272
11273     if (stored_player[i].move_delay > 0)
11274       stored_player[i].move_delay--;
11275
11276     /* due to bugs in previous versions, counter must count up, not down */
11277     if (stored_player[i].push_delay != -1)
11278       stored_player[i].push_delay++;
11279
11280     if (stored_player[i].drop_delay > 0)
11281       stored_player[i].drop_delay--;
11282
11283     if (stored_player[i].is_dropping_pressed)
11284       stored_player[i].drop_pressed_delay++;
11285   }
11286 }
11287
11288 void StartGameActions(boolean init_network_game, boolean record_tape,
11289                       long random_seed)
11290 {
11291   unsigned long new_random_seed = InitRND(random_seed);
11292
11293   if (record_tape)
11294     TapeStartRecording(new_random_seed);
11295
11296 #if defined(NETWORK_AVALIABLE)
11297   if (init_network_game)
11298   {
11299     SendToServer_StartPlaying();
11300
11301     return;
11302   }
11303 #endif
11304
11305   InitGame();
11306 }
11307
11308 void GameActions()
11309 {
11310   static unsigned long game_frame_delay = 0;
11311   unsigned long game_frame_delay_value;
11312   byte *recorded_player_action;
11313   byte summarized_player_action = 0;
11314   byte tape_action[MAX_PLAYERS];
11315   int i;
11316
11317   /* detect endless loops, caused by custom element programming */
11318   if (recursion_loop_detected && recursion_loop_depth == 0)
11319   {
11320     char *message = getStringCat3("Internal Error ! Element ",
11321                                   EL_NAME(recursion_loop_element),
11322                                   " caused endless loop ! Quit the game ?");
11323
11324     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11325           EL_NAME(recursion_loop_element));
11326
11327     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11328
11329     recursion_loop_detected = FALSE;    /* if game should be continued */
11330
11331     free(message);
11332
11333     return;
11334   }
11335
11336   if (game.restart_level)
11337     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11338
11339   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11340   {
11341     if (level.native_em_level->lev->home == 0)  /* all players at home */
11342     {
11343       PlayerWins(local_player);
11344
11345       AllPlayersGone = TRUE;
11346
11347       level.native_em_level->lev->home = -1;
11348     }
11349
11350     if (level.native_em_level->ply[0]->alive == 0 &&
11351         level.native_em_level->ply[1]->alive == 0 &&
11352         level.native_em_level->ply[2]->alive == 0 &&
11353         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11354       AllPlayersGone = TRUE;
11355   }
11356
11357   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11358     GameWon();
11359
11360   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11361     TapeStop();
11362
11363   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11364     return;
11365
11366   game_frame_delay_value =
11367     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11368
11369   if (tape.playing && tape.warp_forward && !tape.pausing)
11370     game_frame_delay_value = 0;
11371
11372   /* ---------- main game synchronization point ---------- */
11373
11374   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11375
11376   if (network_playing && !network_player_action_received)
11377   {
11378     /* try to get network player actions in time */
11379
11380 #if defined(NETWORK_AVALIABLE)
11381     /* last chance to get network player actions without main loop delay */
11382     HandleNetworking();
11383 #endif
11384
11385     /* game was quit by network peer */
11386     if (game_status != GAME_MODE_PLAYING)
11387       return;
11388
11389     if (!network_player_action_received)
11390       return;           /* failed to get network player actions in time */
11391
11392     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11393   }
11394
11395   if (tape.pausing)
11396     return;
11397
11398   /* at this point we know that we really continue executing the game */
11399
11400   network_player_action_received = FALSE;
11401
11402   /* when playing tape, read previously recorded player input from tape data */
11403   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11404
11405 #if 1
11406   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11407   if (tape.pausing)
11408     return;
11409 #endif
11410
11411   if (tape.set_centered_player)
11412   {
11413     game.centered_player_nr_next = tape.centered_player_nr_next;
11414     game.set_centered_player = TRUE;
11415   }
11416
11417   for (i = 0; i < MAX_PLAYERS; i++)
11418   {
11419     summarized_player_action |= stored_player[i].action;
11420
11421     if (!network_playing)
11422       stored_player[i].effective_action = stored_player[i].action;
11423   }
11424
11425 #if defined(NETWORK_AVALIABLE)
11426   if (network_playing)
11427     SendToServer_MovePlayer(summarized_player_action);
11428 #endif
11429
11430   if (!options.network && !setup.team_mode)
11431     local_player->effective_action = summarized_player_action;
11432
11433   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11434   {
11435     for (i = 0; i < MAX_PLAYERS; i++)
11436       stored_player[i].effective_action =
11437         (i == game.centered_player_nr ? summarized_player_action : 0);
11438   }
11439
11440   if (recorded_player_action != NULL)
11441     for (i = 0; i < MAX_PLAYERS; i++)
11442       stored_player[i].effective_action = recorded_player_action[i];
11443
11444   for (i = 0; i < MAX_PLAYERS; i++)
11445   {
11446     tape_action[i] = stored_player[i].effective_action;
11447
11448     /* (this can only happen in the R'n'D game engine) */
11449     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11450       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11451   }
11452
11453   /* only record actions from input devices, but not programmed actions */
11454   if (tape.recording)
11455     TapeRecordAction(tape_action);
11456
11457   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11458   {
11459     GameActions_EM_Main();
11460   }
11461   else
11462   {
11463     GameActions_RND();
11464   }
11465 }
11466
11467 void GameActions_EM_Main()
11468 {
11469   byte effective_action[MAX_PLAYERS];
11470   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11471   int i;
11472
11473   for (i = 0; i < MAX_PLAYERS; i++)
11474     effective_action[i] = stored_player[i].effective_action;
11475
11476   GameActions_EM(effective_action, warp_mode);
11477
11478   CheckLevelTime();
11479
11480   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11481 }
11482
11483 void GameActions_RND()
11484 {
11485   int magic_wall_x = 0, magic_wall_y = 0;
11486   int i, x, y, element, graphic;
11487
11488   InitPlayfieldScanModeVars();
11489
11490 #if USE_ONE_MORE_CHANGE_PER_FRAME
11491   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11492   {
11493     SCAN_PLAYFIELD(x, y)
11494     {
11495       ChangeCount[x][y] = 0;
11496       ChangeEvent[x][y] = -1;
11497     }
11498   }
11499 #endif
11500
11501   if (game.set_centered_player)
11502   {
11503     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11504
11505     /* switching to "all players" only possible if all players fit to screen */
11506     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11507     {
11508       game.centered_player_nr_next = game.centered_player_nr;
11509       game.set_centered_player = FALSE;
11510     }
11511
11512     /* do not switch focus to non-existing (or non-active) player */
11513     if (game.centered_player_nr_next >= 0 &&
11514         !stored_player[game.centered_player_nr_next].active)
11515     {
11516       game.centered_player_nr_next = game.centered_player_nr;
11517       game.set_centered_player = FALSE;
11518     }
11519   }
11520
11521   if (game.set_centered_player &&
11522       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11523   {
11524     int sx, sy;
11525
11526     if (game.centered_player_nr_next == -1)
11527     {
11528       setScreenCenteredToAllPlayers(&sx, &sy);
11529     }
11530     else
11531     {
11532       sx = stored_player[game.centered_player_nr_next].jx;
11533       sy = stored_player[game.centered_player_nr_next].jy;
11534     }
11535
11536     game.centered_player_nr = game.centered_player_nr_next;
11537     game.set_centered_player = FALSE;
11538
11539     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11540     DrawGameDoorValues();
11541   }
11542
11543   for (i = 0; i < MAX_PLAYERS; i++)
11544   {
11545     int actual_player_action = stored_player[i].effective_action;
11546
11547 #if 1
11548     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11549        - rnd_equinox_tetrachloride 048
11550        - rnd_equinox_tetrachloride_ii 096
11551        - rnd_emanuel_schmieg 002
11552        - doctor_sloan_ww 001, 020
11553     */
11554     if (stored_player[i].MovPos == 0)
11555       CheckGravityMovement(&stored_player[i]);
11556 #endif
11557
11558     /* overwrite programmed action with tape action */
11559     if (stored_player[i].programmed_action)
11560       actual_player_action = stored_player[i].programmed_action;
11561
11562     PlayerActions(&stored_player[i], actual_player_action);
11563
11564     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11565   }
11566
11567   ScrollScreen(NULL, SCROLL_GO_ON);
11568
11569   /* for backwards compatibility, the following code emulates a fixed bug that
11570      occured when pushing elements (causing elements that just made their last
11571      pushing step to already (if possible) make their first falling step in the
11572      same game frame, which is bad); this code is also needed to use the famous
11573      "spring push bug" which is used in older levels and might be wanted to be
11574      used also in newer levels, but in this case the buggy pushing code is only
11575      affecting the "spring" element and no other elements */
11576
11577   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11578   {
11579     for (i = 0; i < MAX_PLAYERS; i++)
11580     {
11581       struct PlayerInfo *player = &stored_player[i];
11582       int x = player->jx;
11583       int y = player->jy;
11584
11585       if (player->active && player->is_pushing && player->is_moving &&
11586           IS_MOVING(x, y) &&
11587           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11588            Feld[x][y] == EL_SPRING))
11589       {
11590         ContinueMoving(x, y);
11591
11592         /* continue moving after pushing (this is actually a bug) */
11593         if (!IS_MOVING(x, y))
11594           Stop[x][y] = FALSE;
11595       }
11596     }
11597   }
11598
11599 #if 0
11600   debug_print_timestamp(0, "start main loop profiling");
11601 #endif
11602
11603   SCAN_PLAYFIELD(x, y)
11604   {
11605     ChangeCount[x][y] = 0;
11606     ChangeEvent[x][y] = -1;
11607
11608     /* this must be handled before main playfield loop */
11609     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11610     {
11611       MovDelay[x][y]--;
11612       if (MovDelay[x][y] <= 0)
11613         RemoveField(x, y);
11614     }
11615
11616 #if USE_NEW_SNAP_DELAY
11617     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11618     {
11619       MovDelay[x][y]--;
11620       if (MovDelay[x][y] <= 0)
11621       {
11622         RemoveField(x, y);
11623         DrawLevelField(x, y);
11624
11625         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11626       }
11627     }
11628 #endif
11629
11630 #if DEBUG
11631     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11632     {
11633       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11634       printf("GameActions(): This should never happen!\n");
11635
11636       ChangePage[x][y] = -1;
11637     }
11638 #endif
11639
11640     Stop[x][y] = FALSE;
11641     if (WasJustMoving[x][y] > 0)
11642       WasJustMoving[x][y]--;
11643     if (WasJustFalling[x][y] > 0)
11644       WasJustFalling[x][y]--;
11645     if (CheckCollision[x][y] > 0)
11646       CheckCollision[x][y]--;
11647     if (CheckImpact[x][y] > 0)
11648       CheckImpact[x][y]--;
11649
11650     GfxFrame[x][y]++;
11651
11652     /* reset finished pushing action (not done in ContinueMoving() to allow
11653        continuous pushing animation for elements with zero push delay) */
11654     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11655     {
11656       ResetGfxAnimation(x, y);
11657       DrawLevelField(x, y);
11658     }
11659
11660 #if DEBUG
11661     if (IS_BLOCKED(x, y))
11662     {
11663       int oldx, oldy;
11664
11665       Blocked2Moving(x, y, &oldx, &oldy);
11666       if (!IS_MOVING(oldx, oldy))
11667       {
11668         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11669         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11670         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11671         printf("GameActions(): This should never happen!\n");
11672       }
11673     }
11674 #endif
11675   }
11676
11677 #if 0
11678   debug_print_timestamp(0, "- time for pre-main loop:");
11679 #endif
11680
11681 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11682   SCAN_PLAYFIELD(x, y)
11683   {
11684     element = Feld[x][y];
11685     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11686
11687 #if 1
11688     {
11689 #if 1
11690       int element2 = element;
11691       int graphic2 = graphic;
11692 #else
11693       int element2 = Feld[x][y];
11694       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11695 #endif
11696       int last_gfx_frame = GfxFrame[x][y];
11697
11698       if (graphic_info[graphic2].anim_global_sync)
11699         GfxFrame[x][y] = FrameCounter;
11700       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11701         GfxFrame[x][y] = CustomValue[x][y];
11702       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11703         GfxFrame[x][y] = element_info[element2].collect_score;
11704       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11705         GfxFrame[x][y] = ChangeDelay[x][y];
11706
11707       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11708         DrawLevelGraphicAnimation(x, y, graphic2);
11709     }
11710 #else
11711     ResetGfxFrame(x, y, TRUE);
11712 #endif
11713
11714 #if 1
11715     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11716         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11717       ResetRandomAnimationValue(x, y);
11718 #endif
11719
11720 #if 1
11721     SetRandomAnimationValue(x, y);
11722 #endif
11723
11724 #if 1
11725     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11726 #endif
11727   }
11728 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11729
11730 #if 0
11731   debug_print_timestamp(0, "- time for TEST loop:     -->");
11732 #endif
11733
11734   SCAN_PLAYFIELD(x, y)
11735   {
11736     element = Feld[x][y];
11737     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11738
11739     ResetGfxFrame(x, y, TRUE);
11740
11741     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11742         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11743       ResetRandomAnimationValue(x, y);
11744
11745     SetRandomAnimationValue(x, y);
11746
11747     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11748
11749     if (IS_INACTIVE(element))
11750     {
11751       if (IS_ANIMATED(graphic))
11752         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11753
11754       continue;
11755     }
11756
11757     /* this may take place after moving, so 'element' may have changed */
11758     if (IS_CHANGING(x, y) &&
11759         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11760     {
11761       int page = element_info[element].event_page_nr[CE_DELAY];
11762
11763 #if 1
11764       HandleElementChange(x, y, page);
11765 #else
11766       if (CAN_CHANGE(element))
11767         HandleElementChange(x, y, page);
11768
11769       if (HAS_ACTION(element))
11770         ExecuteCustomElementAction(x, y, element, page);
11771 #endif
11772
11773       element = Feld[x][y];
11774       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11775     }
11776
11777 #if 0   // ---------------------------------------------------------------------
11778
11779     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11780     {
11781       StartMoving(x, y);
11782
11783       element = Feld[x][y];
11784       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11785
11786       if (IS_ANIMATED(graphic) &&
11787           !IS_MOVING(x, y) &&
11788           !Stop[x][y])
11789         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11790
11791       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11792         DrawTwinkleOnField(x, y);
11793     }
11794     else if (IS_MOVING(x, y))
11795       ContinueMoving(x, y);
11796     else
11797     {
11798       switch (element)
11799       {
11800         case EL_ACID:
11801         case EL_EXIT_OPEN:
11802         case EL_EM_EXIT_OPEN:
11803         case EL_SP_EXIT_OPEN:
11804         case EL_STEEL_EXIT_OPEN:
11805         case EL_EM_STEEL_EXIT_OPEN:
11806         case EL_SP_TERMINAL:
11807         case EL_SP_TERMINAL_ACTIVE:
11808         case EL_EXTRA_TIME:
11809         case EL_SHIELD_NORMAL:
11810         case EL_SHIELD_DEADLY:
11811           if (IS_ANIMATED(graphic))
11812             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11813           break;
11814
11815         case EL_DYNAMITE_ACTIVE:
11816         case EL_EM_DYNAMITE_ACTIVE:
11817         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11818         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11819         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11820         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11821         case EL_SP_DISK_RED_ACTIVE:
11822           CheckDynamite(x, y);
11823           break;
11824
11825         case EL_AMOEBA_GROWING:
11826           AmoebeWaechst(x, y);
11827           break;
11828
11829         case EL_AMOEBA_SHRINKING:
11830           AmoebaDisappearing(x, y);
11831           break;
11832
11833 #if !USE_NEW_AMOEBA_CODE
11834         case EL_AMOEBA_WET:
11835         case EL_AMOEBA_DRY:
11836         case EL_AMOEBA_FULL:
11837         case EL_BD_AMOEBA:
11838         case EL_EMC_DRIPPER:
11839           AmoebeAbleger(x, y);
11840           break;
11841 #endif
11842
11843         case EL_GAME_OF_LIFE:
11844         case EL_BIOMAZE:
11845           Life(x, y);
11846           break;
11847
11848         case EL_EXIT_CLOSED:
11849           CheckExit(x, y);
11850           break;
11851
11852         case EL_EM_EXIT_CLOSED:
11853           CheckExitEM(x, y);
11854           break;
11855
11856         case EL_STEEL_EXIT_CLOSED:
11857           CheckExitSteel(x, y);
11858           break;
11859
11860         case EL_EM_STEEL_EXIT_CLOSED:
11861           CheckExitSteelEM(x, y);
11862           break;
11863
11864         case EL_SP_EXIT_CLOSED:
11865           CheckExitSP(x, y);
11866           break;
11867
11868         case EL_EXPANDABLE_WALL_GROWING:
11869         case EL_EXPANDABLE_STEELWALL_GROWING:
11870           MauerWaechst(x, y);
11871           break;
11872
11873         case EL_EXPANDABLE_WALL:
11874         case EL_EXPANDABLE_WALL_HORIZONTAL:
11875         case EL_EXPANDABLE_WALL_VERTICAL:
11876         case EL_EXPANDABLE_WALL_ANY:
11877         case EL_BD_EXPANDABLE_WALL:
11878           MauerAbleger(x, y);
11879           break;
11880
11881         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11882         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11883         case EL_EXPANDABLE_STEELWALL_ANY:
11884           MauerAblegerStahl(x, y);
11885           break;
11886
11887         case EL_FLAMES:
11888           CheckForDragon(x, y);
11889           break;
11890
11891         case EL_EXPLOSION:
11892           break;
11893
11894         case EL_ELEMENT_SNAPPING:
11895         case EL_DIAGONAL_SHRINKING:
11896         case EL_DIAGONAL_GROWING:
11897         {
11898           graphic =
11899             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11900
11901           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11902           break;
11903         }
11904
11905         default:
11906           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11907             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11908           break;
11909       }
11910     }
11911
11912 #else   // ---------------------------------------------------------------------
11913
11914     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11915     {
11916       StartMoving(x, y);
11917
11918       element = Feld[x][y];
11919       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11920
11921       if (IS_ANIMATED(graphic) &&
11922           !IS_MOVING(x, y) &&
11923           !Stop[x][y])
11924         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11925
11926       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11927         DrawTwinkleOnField(x, y);
11928     }
11929     else if ((element == EL_ACID ||
11930               element == EL_EXIT_OPEN ||
11931               element == EL_EM_EXIT_OPEN ||
11932               element == EL_SP_EXIT_OPEN ||
11933               element == EL_STEEL_EXIT_OPEN ||
11934               element == EL_EM_STEEL_EXIT_OPEN ||
11935               element == EL_SP_TERMINAL ||
11936               element == EL_SP_TERMINAL_ACTIVE ||
11937               element == EL_EXTRA_TIME ||
11938               element == EL_SHIELD_NORMAL ||
11939               element == EL_SHIELD_DEADLY) &&
11940              IS_ANIMATED(graphic))
11941       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11942     else if (IS_MOVING(x, y))
11943       ContinueMoving(x, y);
11944     else if (IS_ACTIVE_BOMB(element))
11945       CheckDynamite(x, y);
11946     else if (element == EL_AMOEBA_GROWING)
11947       AmoebeWaechst(x, y);
11948     else if (element == EL_AMOEBA_SHRINKING)
11949       AmoebaDisappearing(x, y);
11950
11951 #if !USE_NEW_AMOEBA_CODE
11952     else if (IS_AMOEBALIVE(element))
11953       AmoebeAbleger(x, y);
11954 #endif
11955
11956     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11957       Life(x, y);
11958     else if (element == EL_EXIT_CLOSED)
11959       CheckExit(x, y);
11960     else if (element == EL_EM_EXIT_CLOSED)
11961       CheckExitEM(x, y);
11962     else if (element == EL_STEEL_EXIT_CLOSED)
11963       CheckExitSteel(x, y);
11964     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11965       CheckExitSteelEM(x, y);
11966     else if (element == EL_SP_EXIT_CLOSED)
11967       CheckExitSP(x, y);
11968     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11969              element == EL_EXPANDABLE_STEELWALL_GROWING)
11970       MauerWaechst(x, y);
11971     else if (element == EL_EXPANDABLE_WALL ||
11972              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11973              element == EL_EXPANDABLE_WALL_VERTICAL ||
11974              element == EL_EXPANDABLE_WALL_ANY ||
11975              element == EL_BD_EXPANDABLE_WALL)
11976       MauerAbleger(x, y);
11977     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11978              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11979              element == EL_EXPANDABLE_STEELWALL_ANY)
11980       MauerAblegerStahl(x, y);
11981     else if (element == EL_FLAMES)
11982       CheckForDragon(x, y);
11983     else if (element == EL_EXPLOSION)
11984       ; /* drawing of correct explosion animation is handled separately */
11985     else if (element == EL_ELEMENT_SNAPPING ||
11986              element == EL_DIAGONAL_SHRINKING ||
11987              element == EL_DIAGONAL_GROWING)
11988     {
11989       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11990
11991       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11992     }
11993     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11994       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11995
11996 #endif  // ---------------------------------------------------------------------
11997
11998     if (IS_BELT_ACTIVE(element))
11999       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12000
12001     if (game.magic_wall_active)
12002     {
12003       int jx = local_player->jx, jy = local_player->jy;
12004
12005       /* play the element sound at the position nearest to the player */
12006       if ((element == EL_MAGIC_WALL_FULL ||
12007            element == EL_MAGIC_WALL_ACTIVE ||
12008            element == EL_MAGIC_WALL_EMPTYING ||
12009            element == EL_BD_MAGIC_WALL_FULL ||
12010            element == EL_BD_MAGIC_WALL_ACTIVE ||
12011            element == EL_BD_MAGIC_WALL_EMPTYING ||
12012            element == EL_DC_MAGIC_WALL_FULL ||
12013            element == EL_DC_MAGIC_WALL_ACTIVE ||
12014            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12015           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12016       {
12017         magic_wall_x = x;
12018         magic_wall_y = y;
12019       }
12020     }
12021   }
12022
12023 #if 0
12024   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12025 #endif
12026
12027 #if USE_NEW_AMOEBA_CODE
12028   /* new experimental amoeba growth stuff */
12029   if (!(FrameCounter % 8))
12030   {
12031     static unsigned long random = 1684108901;
12032
12033     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12034     {
12035       x = RND(lev_fieldx);
12036       y = RND(lev_fieldy);
12037       element = Feld[x][y];
12038
12039       if (!IS_PLAYER(x,y) &&
12040           (element == EL_EMPTY ||
12041            CAN_GROW_INTO(element) ||
12042            element == EL_QUICKSAND_EMPTY ||
12043            element == EL_QUICKSAND_FAST_EMPTY ||
12044            element == EL_ACID_SPLASH_LEFT ||
12045            element == EL_ACID_SPLASH_RIGHT))
12046       {
12047         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12048             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12049             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12050             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12051           Feld[x][y] = EL_AMOEBA_DROP;
12052       }
12053
12054       random = random * 129 + 1;
12055     }
12056   }
12057 #endif
12058
12059 #if 0
12060   if (game.explosions_delayed)
12061 #endif
12062   {
12063     game.explosions_delayed = FALSE;
12064
12065     SCAN_PLAYFIELD(x, y)
12066     {
12067       element = Feld[x][y];
12068
12069       if (ExplodeField[x][y])
12070         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12071       else if (element == EL_EXPLOSION)
12072         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12073
12074       ExplodeField[x][y] = EX_TYPE_NONE;
12075     }
12076
12077     game.explosions_delayed = TRUE;
12078   }
12079
12080   if (game.magic_wall_active)
12081   {
12082     if (!(game.magic_wall_time_left % 4))
12083     {
12084       int element = Feld[magic_wall_x][magic_wall_y];
12085
12086       if (element == EL_BD_MAGIC_WALL_FULL ||
12087           element == EL_BD_MAGIC_WALL_ACTIVE ||
12088           element == EL_BD_MAGIC_WALL_EMPTYING)
12089         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12090       else if (element == EL_DC_MAGIC_WALL_FULL ||
12091                element == EL_DC_MAGIC_WALL_ACTIVE ||
12092                element == EL_DC_MAGIC_WALL_EMPTYING)
12093         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12094       else
12095         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12096     }
12097
12098     if (game.magic_wall_time_left > 0)
12099     {
12100       game.magic_wall_time_left--;
12101
12102       if (!game.magic_wall_time_left)
12103       {
12104         SCAN_PLAYFIELD(x, y)
12105         {
12106           element = Feld[x][y];
12107
12108           if (element == EL_MAGIC_WALL_ACTIVE ||
12109               element == EL_MAGIC_WALL_FULL)
12110           {
12111             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12112             DrawLevelField(x, y);
12113           }
12114           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12115                    element == EL_BD_MAGIC_WALL_FULL)
12116           {
12117             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12118             DrawLevelField(x, y);
12119           }
12120           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12121                    element == EL_DC_MAGIC_WALL_FULL)
12122           {
12123             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12124             DrawLevelField(x, y);
12125           }
12126         }
12127
12128         game.magic_wall_active = FALSE;
12129       }
12130     }
12131   }
12132
12133   if (game.light_time_left > 0)
12134   {
12135     game.light_time_left--;
12136
12137     if (game.light_time_left == 0)
12138       RedrawAllLightSwitchesAndInvisibleElements();
12139   }
12140
12141   if (game.timegate_time_left > 0)
12142   {
12143     game.timegate_time_left--;
12144
12145     if (game.timegate_time_left == 0)
12146       CloseAllOpenTimegates();
12147   }
12148
12149   if (game.lenses_time_left > 0)
12150   {
12151     game.lenses_time_left--;
12152
12153     if (game.lenses_time_left == 0)
12154       RedrawAllInvisibleElementsForLenses();
12155   }
12156
12157   if (game.magnify_time_left > 0)
12158   {
12159     game.magnify_time_left--;
12160
12161     if (game.magnify_time_left == 0)
12162       RedrawAllInvisibleElementsForMagnifier();
12163   }
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     struct PlayerInfo *player = &stored_player[i];
12168
12169     if (SHIELD_ON(player))
12170     {
12171       if (player->shield_deadly_time_left)
12172         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12173       else if (player->shield_normal_time_left)
12174         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12175     }
12176   }
12177
12178   CheckLevelTime();
12179
12180   DrawAllPlayers();
12181   PlayAllPlayersSound();
12182
12183   if (options.debug)                    /* calculate frames per second */
12184   {
12185     static unsigned long fps_counter = 0;
12186     static int fps_frames = 0;
12187     unsigned long fps_delay_ms = Counter() - fps_counter;
12188
12189     fps_frames++;
12190
12191     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12192     {
12193       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12194
12195       fps_frames = 0;
12196       fps_counter = Counter();
12197     }
12198
12199     redraw_mask |= REDRAW_FPS;
12200   }
12201
12202   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12203
12204   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12205   {
12206     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12207
12208     local_player->show_envelope = 0;
12209   }
12210
12211 #if 0
12212   debug_print_timestamp(0, "stop main loop profiling ");
12213   printf("----------------------------------------------------------\n");
12214 #endif
12215
12216   /* use random number generator in every frame to make it less predictable */
12217   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12218     RND(1);
12219 }
12220
12221 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12222 {
12223   int min_x = x, min_y = y, max_x = x, max_y = y;
12224   int i;
12225
12226   for (i = 0; i < MAX_PLAYERS; i++)
12227   {
12228     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12229
12230     if (!stored_player[i].active || &stored_player[i] == player)
12231       continue;
12232
12233     min_x = MIN(min_x, jx);
12234     min_y = MIN(min_y, jy);
12235     max_x = MAX(max_x, jx);
12236     max_y = MAX(max_y, jy);
12237   }
12238
12239   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12240 }
12241
12242 static boolean AllPlayersInVisibleScreen()
12243 {
12244   int i;
12245
12246   for (i = 0; i < MAX_PLAYERS; i++)
12247   {
12248     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12249
12250     if (!stored_player[i].active)
12251       continue;
12252
12253     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12254       return FALSE;
12255   }
12256
12257   return TRUE;
12258 }
12259
12260 void ScrollLevel(int dx, int dy)
12261 {
12262 #if 1
12263   static Bitmap *bitmap_db_field2 = NULL;
12264   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12265   int x, y;
12266 #else
12267   int i, x, y;
12268 #endif
12269
12270 #if 0
12271   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12272   /* only horizontal XOR vertical scroll direction allowed */
12273   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12274     return;
12275 #endif
12276
12277 #if 1
12278   if (bitmap_db_field2 == NULL)
12279     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12280
12281   /* needed when blitting directly to same bitmap -- should not be needed with
12282      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12283   BlitBitmap(drawto_field, bitmap_db_field2,
12284              FX + TILEX * (dx == -1) - softscroll_offset,
12285              FY + TILEY * (dy == -1) - softscroll_offset,
12286              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12287              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12288              FX + TILEX * (dx == 1) - softscroll_offset,
12289              FY + TILEY * (dy == 1) - softscroll_offset);
12290   BlitBitmap(bitmap_db_field2, drawto_field,
12291              FX + TILEX * (dx == 1) - softscroll_offset,
12292              FY + TILEY * (dy == 1) - softscroll_offset,
12293              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12294              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12295              FX + TILEX * (dx == 1) - softscroll_offset,
12296              FY + TILEY * (dy == 1) - softscroll_offset);
12297
12298 #else
12299
12300 #if 1
12301   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12302   int xsize = (BX2 - BX1 + 1);
12303   int ysize = (BY2 - BY1 + 1);
12304   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12305   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12306   int step  = (start < end ? +1 : -1);
12307
12308   for (i = start; i != end; i += step)
12309   {
12310     BlitBitmap(drawto_field, drawto_field,
12311                FX + TILEX * (dx != 0 ? i + step : 0),
12312                FY + TILEY * (dy != 0 ? i + step : 0),
12313                TILEX * (dx != 0 ? 1 : xsize),
12314                TILEY * (dy != 0 ? 1 : ysize),
12315                FX + TILEX * (dx != 0 ? i : 0),
12316                FY + TILEY * (dy != 0 ? i : 0));
12317   }
12318
12319 #else
12320
12321   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12322
12323   BlitBitmap(drawto_field, drawto_field,
12324              FX + TILEX * (dx == -1) - softscroll_offset,
12325              FY + TILEY * (dy == -1) - softscroll_offset,
12326              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12327              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12328              FX + TILEX * (dx == 1) - softscroll_offset,
12329              FY + TILEY * (dy == 1) - softscroll_offset);
12330 #endif
12331 #endif
12332
12333   if (dx != 0)
12334   {
12335     x = (dx == 1 ? BX1 : BX2);
12336     for (y = BY1; y <= BY2; y++)
12337       DrawScreenField(x, y);
12338   }
12339
12340   if (dy != 0)
12341   {
12342     y = (dy == 1 ? BY1 : BY2);
12343     for (x = BX1; x <= BX2; x++)
12344       DrawScreenField(x, y);
12345   }
12346
12347   redraw_mask |= REDRAW_FIELD;
12348 }
12349
12350 static boolean canFallDown(struct PlayerInfo *player)
12351 {
12352   int jx = player->jx, jy = player->jy;
12353
12354   return (IN_LEV_FIELD(jx, jy + 1) &&
12355           (IS_FREE(jx, jy + 1) ||
12356            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12357           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12358           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12359 }
12360
12361 static boolean canPassField(int x, int y, int move_dir)
12362 {
12363   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12364   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12365   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12366   int nextx = x + dx;
12367   int nexty = y + dy;
12368   int element = Feld[x][y];
12369
12370   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12371           !CAN_MOVE(element) &&
12372           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12373           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12374           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12375 }
12376
12377 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12378 {
12379   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12380   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12381   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12382   int newx = x + dx;
12383   int newy = y + dy;
12384
12385   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12386           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12387           (IS_DIGGABLE(Feld[newx][newy]) ||
12388            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12389            canPassField(newx, newy, move_dir)));
12390 }
12391
12392 static void CheckGravityMovement(struct PlayerInfo *player)
12393 {
12394 #if USE_PLAYER_GRAVITY
12395   if (player->gravity && !player->programmed_action)
12396 #else
12397   if (game.gravity && !player->programmed_action)
12398 #endif
12399   {
12400     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12401     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12402     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12403     int jx = player->jx, jy = player->jy;
12404     boolean player_is_moving_to_valid_field =
12405       (!player_is_snapping &&
12406        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12407         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12408     boolean player_can_fall_down = canFallDown(player);
12409
12410     if (player_can_fall_down &&
12411         !player_is_moving_to_valid_field)
12412       player->programmed_action = MV_DOWN;
12413   }
12414 }
12415
12416 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12417 {
12418   return CheckGravityMovement(player);
12419
12420 #if USE_PLAYER_GRAVITY
12421   if (player->gravity && !player->programmed_action)
12422 #else
12423   if (game.gravity && !player->programmed_action)
12424 #endif
12425   {
12426     int jx = player->jx, jy = player->jy;
12427     boolean field_under_player_is_free =
12428       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12429     boolean player_is_standing_on_valid_field =
12430       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12431        (IS_WALKABLE(Feld[jx][jy]) &&
12432         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12433
12434     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12435       player->programmed_action = MV_DOWN;
12436   }
12437 }
12438
12439 /*
12440   MovePlayerOneStep()
12441   -----------------------------------------------------------------------------
12442   dx, dy:               direction (non-diagonal) to try to move the player to
12443   real_dx, real_dy:     direction as read from input device (can be diagonal)
12444 */
12445
12446 boolean MovePlayerOneStep(struct PlayerInfo *player,
12447                           int dx, int dy, int real_dx, int real_dy)
12448 {
12449   int jx = player->jx, jy = player->jy;
12450   int new_jx = jx + dx, new_jy = jy + dy;
12451 #if !USE_FIXED_DONT_RUN_INTO
12452   int element;
12453 #endif
12454   int can_move;
12455   boolean player_can_move = !player->cannot_move;
12456
12457   if (!player->active || (!dx && !dy))
12458     return MP_NO_ACTION;
12459
12460   player->MovDir = (dx < 0 ? MV_LEFT :
12461                     dx > 0 ? MV_RIGHT :
12462                     dy < 0 ? MV_UP :
12463                     dy > 0 ? MV_DOWN :  MV_NONE);
12464
12465   if (!IN_LEV_FIELD(new_jx, new_jy))
12466     return MP_NO_ACTION;
12467
12468   if (!player_can_move)
12469   {
12470     if (player->MovPos == 0)
12471     {
12472       player->is_moving = FALSE;
12473       player->is_digging = FALSE;
12474       player->is_collecting = FALSE;
12475       player->is_snapping = FALSE;
12476       player->is_pushing = FALSE;
12477     }
12478   }
12479
12480 #if 1
12481   if (!options.network && game.centered_player_nr == -1 &&
12482       !AllPlayersInSight(player, new_jx, new_jy))
12483     return MP_NO_ACTION;
12484 #else
12485   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12486     return MP_NO_ACTION;
12487 #endif
12488
12489 #if !USE_FIXED_DONT_RUN_INTO
12490   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12491
12492   /* (moved to DigField()) */
12493   if (player_can_move && DONT_RUN_INTO(element))
12494   {
12495     if (element == EL_ACID && dx == 0 && dy == 1)
12496     {
12497       SplashAcid(new_jx, new_jy);
12498       Feld[jx][jy] = EL_PLAYER_1;
12499       InitMovingField(jx, jy, MV_DOWN);
12500       Store[jx][jy] = EL_ACID;
12501       ContinueMoving(jx, jy);
12502       BuryPlayer(player);
12503     }
12504     else
12505       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12506
12507     return MP_MOVING;
12508   }
12509 #endif
12510
12511   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12512   if (can_move != MP_MOVING)
12513     return can_move;
12514
12515   /* check if DigField() has caused relocation of the player */
12516   if (player->jx != jx || player->jy != jy)
12517     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12518
12519   StorePlayer[jx][jy] = 0;
12520   player->last_jx = jx;
12521   player->last_jy = jy;
12522   player->jx = new_jx;
12523   player->jy = new_jy;
12524   StorePlayer[new_jx][new_jy] = player->element_nr;
12525
12526   if (player->move_delay_value_next != -1)
12527   {
12528     player->move_delay_value = player->move_delay_value_next;
12529     player->move_delay_value_next = -1;
12530   }
12531
12532   player->MovPos =
12533     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12534
12535   player->step_counter++;
12536
12537   PlayerVisit[jx][jy] = FrameCounter;
12538
12539 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12540   player->is_moving = TRUE;
12541 #endif
12542
12543 #if 1
12544   /* should better be called in MovePlayer(), but this breaks some tapes */
12545   ScrollPlayer(player, SCROLL_INIT);
12546 #endif
12547
12548   return MP_MOVING;
12549 }
12550
12551 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12552 {
12553   int jx = player->jx, jy = player->jy;
12554   int old_jx = jx, old_jy = jy;
12555   int moved = MP_NO_ACTION;
12556
12557   if (!player->active)
12558     return FALSE;
12559
12560   if (!dx && !dy)
12561   {
12562     if (player->MovPos == 0)
12563     {
12564       player->is_moving = FALSE;
12565       player->is_digging = FALSE;
12566       player->is_collecting = FALSE;
12567       player->is_snapping = FALSE;
12568       player->is_pushing = FALSE;
12569     }
12570
12571     return FALSE;
12572   }
12573
12574   if (player->move_delay > 0)
12575     return FALSE;
12576
12577   player->move_delay = -1;              /* set to "uninitialized" value */
12578
12579   /* store if player is automatically moved to next field */
12580   player->is_auto_moving = (player->programmed_action != MV_NONE);
12581
12582   /* remove the last programmed player action */
12583   player->programmed_action = 0;
12584
12585   if (player->MovPos)
12586   {
12587     /* should only happen if pre-1.2 tape recordings are played */
12588     /* this is only for backward compatibility */
12589
12590     int original_move_delay_value = player->move_delay_value;
12591
12592 #if DEBUG
12593     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12594            tape.counter);
12595 #endif
12596
12597     /* scroll remaining steps with finest movement resolution */
12598     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12599
12600     while (player->MovPos)
12601     {
12602       ScrollPlayer(player, SCROLL_GO_ON);
12603       ScrollScreen(NULL, SCROLL_GO_ON);
12604
12605       AdvanceFrameAndPlayerCounters(player->index_nr);
12606
12607       DrawAllPlayers();
12608       BackToFront();
12609     }
12610
12611     player->move_delay_value = original_move_delay_value;
12612   }
12613
12614   player->is_active = FALSE;
12615
12616   if (player->last_move_dir & MV_HORIZONTAL)
12617   {
12618     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12619       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12620   }
12621   else
12622   {
12623     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12624       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12625   }
12626
12627 #if USE_FIXED_BORDER_RUNNING_GFX
12628   if (!moved && !player->is_active)
12629   {
12630     player->is_moving = FALSE;
12631     player->is_digging = FALSE;
12632     player->is_collecting = FALSE;
12633     player->is_snapping = FALSE;
12634     player->is_pushing = FALSE;
12635   }
12636 #endif
12637
12638   jx = player->jx;
12639   jy = player->jy;
12640
12641 #if 1
12642   if (moved & MP_MOVING && !ScreenMovPos &&
12643       (player->index_nr == game.centered_player_nr ||
12644        game.centered_player_nr == -1))
12645 #else
12646   if (moved & MP_MOVING && !ScreenMovPos &&
12647       (player == local_player || !options.network))
12648 #endif
12649   {
12650     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12651     int offset = game.scroll_delay_value;
12652
12653     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12654     {
12655       /* actual player has left the screen -- scroll in that direction */
12656       if (jx != old_jx)         /* player has moved horizontally */
12657         scroll_x += (jx - old_jx);
12658       else                      /* player has moved vertically */
12659         scroll_y += (jy - old_jy);
12660     }
12661     else
12662     {
12663       if (jx != old_jx)         /* player has moved horizontally */
12664       {
12665         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12666             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12667           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12668
12669         /* don't scroll over playfield boundaries */
12670         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12671           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12672
12673         /* don't scroll more than one field at a time */
12674         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12675
12676         /* don't scroll against the player's moving direction */
12677         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12678             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12679           scroll_x = old_scroll_x;
12680       }
12681       else                      /* player has moved vertically */
12682       {
12683         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12684             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12685           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12686
12687         /* don't scroll over playfield boundaries */
12688         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12689           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12690
12691         /* don't scroll more than one field at a time */
12692         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12693
12694         /* don't scroll against the player's moving direction */
12695         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12696             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12697           scroll_y = old_scroll_y;
12698       }
12699     }
12700
12701     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12702     {
12703 #if 1
12704       if (!options.network && game.centered_player_nr == -1 &&
12705           !AllPlayersInVisibleScreen())
12706       {
12707         scroll_x = old_scroll_x;
12708         scroll_y = old_scroll_y;
12709       }
12710       else
12711 #else
12712       if (!options.network && !AllPlayersInVisibleScreen())
12713       {
12714         scroll_x = old_scroll_x;
12715         scroll_y = old_scroll_y;
12716       }
12717       else
12718 #endif
12719       {
12720         ScrollScreen(player, SCROLL_INIT);
12721         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12722       }
12723     }
12724   }
12725
12726   player->StepFrame = 0;
12727
12728   if (moved & MP_MOVING)
12729   {
12730     if (old_jx != jx && old_jy == jy)
12731       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12732     else if (old_jx == jx && old_jy != jy)
12733       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12734
12735     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12736
12737     player->last_move_dir = player->MovDir;
12738     player->is_moving = TRUE;
12739     player->is_snapping = FALSE;
12740     player->is_switching = FALSE;
12741     player->is_dropping = FALSE;
12742     player->is_dropping_pressed = FALSE;
12743     player->drop_pressed_delay = 0;
12744
12745 #if 0
12746     /* should better be called here than above, but this breaks some tapes */
12747     ScrollPlayer(player, SCROLL_INIT);
12748 #endif
12749   }
12750   else
12751   {
12752     CheckGravityMovementWhenNotMoving(player);
12753
12754     player->is_moving = FALSE;
12755
12756     /* at this point, the player is allowed to move, but cannot move right now
12757        (e.g. because of something blocking the way) -- ensure that the player
12758        is also allowed to move in the next frame (in old versions before 3.1.1,
12759        the player was forced to wait again for eight frames before next try) */
12760
12761     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12762       player->move_delay = 0;   /* allow direct movement in the next frame */
12763   }
12764
12765   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12766     player->move_delay = player->move_delay_value;
12767
12768   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12769   {
12770     TestIfPlayerTouchesBadThing(jx, jy);
12771     TestIfPlayerTouchesCustomElement(jx, jy);
12772   }
12773
12774   if (!player->active)
12775     RemovePlayer(player);
12776
12777   return moved;
12778 }
12779
12780 void ScrollPlayer(struct PlayerInfo *player, int mode)
12781 {
12782   int jx = player->jx, jy = player->jy;
12783   int last_jx = player->last_jx, last_jy = player->last_jy;
12784   int move_stepsize = TILEX / player->move_delay_value;
12785
12786 #if USE_NEW_PLAYER_SPEED
12787   if (!player->active)
12788     return;
12789
12790   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12791     return;
12792 #else
12793   if (!player->active || player->MovPos == 0)
12794     return;
12795 #endif
12796
12797   if (mode == SCROLL_INIT)
12798   {
12799     player->actual_frame_counter = FrameCounter;
12800     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12801
12802     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12803         Feld[last_jx][last_jy] == EL_EMPTY)
12804     {
12805       int last_field_block_delay = 0;   /* start with no blocking at all */
12806       int block_delay_adjustment = player->block_delay_adjustment;
12807
12808       /* if player blocks last field, add delay for exactly one move */
12809       if (player->block_last_field)
12810       {
12811         last_field_block_delay += player->move_delay_value;
12812
12813         /* when blocking enabled, prevent moving up despite gravity */
12814 #if USE_PLAYER_GRAVITY
12815         if (player->gravity && player->MovDir == MV_UP)
12816           block_delay_adjustment = -1;
12817 #else
12818         if (game.gravity && player->MovDir == MV_UP)
12819           block_delay_adjustment = -1;
12820 #endif
12821       }
12822
12823       /* add block delay adjustment (also possible when not blocking) */
12824       last_field_block_delay += block_delay_adjustment;
12825
12826       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12827       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12828     }
12829
12830 #if USE_NEW_PLAYER_SPEED
12831     if (player->MovPos != 0)    /* player has not yet reached destination */
12832       return;
12833 #else
12834     return;
12835 #endif
12836   }
12837   else if (!FrameReached(&player->actual_frame_counter, 1))
12838     return;
12839
12840 #if USE_NEW_PLAYER_SPEED
12841   if (player->MovPos != 0)
12842   {
12843     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12844     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12845
12846     /* before DrawPlayer() to draw correct player graphic for this case */
12847     if (player->MovPos == 0)
12848       CheckGravityMovement(player);
12849   }
12850 #else
12851   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12852   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12853
12854   /* before DrawPlayer() to draw correct player graphic for this case */
12855   if (player->MovPos == 0)
12856     CheckGravityMovement(player);
12857 #endif
12858
12859   if (player->MovPos == 0)      /* player reached destination field */
12860   {
12861     if (player->move_delay_reset_counter > 0)
12862     {
12863       player->move_delay_reset_counter--;
12864
12865       if (player->move_delay_reset_counter == 0)
12866       {
12867         /* continue with normal speed after quickly moving through gate */
12868         HALVE_PLAYER_SPEED(player);
12869
12870         /* be able to make the next move without delay */
12871         player->move_delay = 0;
12872       }
12873     }
12874
12875     player->last_jx = jx;
12876     player->last_jy = jy;
12877
12878     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12879         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12880         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12881         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12882         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12883         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12884     {
12885       DrawPlayer(player);       /* needed here only to cleanup last field */
12886       RemovePlayer(player);
12887
12888       if (local_player->friends_still_needed == 0 ||
12889           IS_SP_ELEMENT(Feld[jx][jy]))
12890         PlayerWins(player);
12891     }
12892
12893     /* this breaks one level: "machine", level 000 */
12894     {
12895       int move_direction = player->MovDir;
12896       int enter_side = MV_DIR_OPPOSITE(move_direction);
12897       int leave_side = move_direction;
12898       int old_jx = last_jx;
12899       int old_jy = last_jy;
12900       int old_element = Feld[old_jx][old_jy];
12901       int new_element = Feld[jx][jy];
12902
12903       if (IS_CUSTOM_ELEMENT(old_element))
12904         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12905                                    CE_LEFT_BY_PLAYER,
12906                                    player->index_bit, leave_side);
12907
12908       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12909                                           CE_PLAYER_LEAVES_X,
12910                                           player->index_bit, leave_side);
12911
12912       if (IS_CUSTOM_ELEMENT(new_element))
12913         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12914                                    player->index_bit, enter_side);
12915
12916       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12917                                           CE_PLAYER_ENTERS_X,
12918                                           player->index_bit, enter_side);
12919
12920       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12921                                         CE_MOVE_OF_X, move_direction);
12922     }
12923
12924     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12925     {
12926       TestIfPlayerTouchesBadThing(jx, jy);
12927       TestIfPlayerTouchesCustomElement(jx, jy);
12928
12929       /* needed because pushed element has not yet reached its destination,
12930          so it would trigger a change event at its previous field location */
12931       if (!player->is_pushing)
12932         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12933
12934       if (!player->active)
12935         RemovePlayer(player);
12936     }
12937
12938     if (!local_player->LevelSolved && level.use_step_counter)
12939     {
12940       int i;
12941
12942       TimePlayed++;
12943
12944       if (TimeLeft > 0)
12945       {
12946         TimeLeft--;
12947
12948         if (TimeLeft <= 10 && setup.time_limit)
12949           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12950
12951 #if 1
12952         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12953
12954         DisplayGameControlValues();
12955 #else
12956         DrawGameValue_Time(TimeLeft);
12957 #endif
12958
12959         if (!TimeLeft && setup.time_limit)
12960           for (i = 0; i < MAX_PLAYERS; i++)
12961             KillPlayer(&stored_player[i]);
12962       }
12963 #if 1
12964       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12965       {
12966         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12967
12968         DisplayGameControlValues();
12969       }
12970 #else
12971       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12972         DrawGameValue_Time(TimePlayed);
12973 #endif
12974     }
12975
12976     if (tape.single_step && tape.recording && !tape.pausing &&
12977         !player->programmed_action)
12978       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12979   }
12980 }
12981
12982 void ScrollScreen(struct PlayerInfo *player, int mode)
12983 {
12984   static unsigned long screen_frame_counter = 0;
12985
12986   if (mode == SCROLL_INIT)
12987   {
12988     /* set scrolling step size according to actual player's moving speed */
12989     ScrollStepSize = TILEX / player->move_delay_value;
12990
12991     screen_frame_counter = FrameCounter;
12992     ScreenMovDir = player->MovDir;
12993     ScreenMovPos = player->MovPos;
12994     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12995     return;
12996   }
12997   else if (!FrameReached(&screen_frame_counter, 1))
12998     return;
12999
13000   if (ScreenMovPos)
13001   {
13002     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13003     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13004     redraw_mask |= REDRAW_FIELD;
13005   }
13006   else
13007     ScreenMovDir = MV_NONE;
13008 }
13009
13010 void TestIfPlayerTouchesCustomElement(int x, int y)
13011 {
13012   static int xy[4][2] =
13013   {
13014     { 0, -1 },
13015     { -1, 0 },
13016     { +1, 0 },
13017     { 0, +1 }
13018   };
13019   static int trigger_sides[4][2] =
13020   {
13021     /* center side       border side */
13022     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13023     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13024     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13025     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13026   };
13027   static int touch_dir[4] =
13028   {
13029     MV_LEFT | MV_RIGHT,
13030     MV_UP   | MV_DOWN,
13031     MV_UP   | MV_DOWN,
13032     MV_LEFT | MV_RIGHT
13033   };
13034   int center_element = Feld[x][y];      /* should always be non-moving! */
13035   int i;
13036
13037   for (i = 0; i < NUM_DIRECTIONS; i++)
13038   {
13039     int xx = x + xy[i][0];
13040     int yy = y + xy[i][1];
13041     int center_side = trigger_sides[i][0];
13042     int border_side = trigger_sides[i][1];
13043     int border_element;
13044
13045     if (!IN_LEV_FIELD(xx, yy))
13046       continue;
13047
13048     if (IS_PLAYER(x, y))
13049     {
13050       struct PlayerInfo *player = PLAYERINFO(x, y);
13051
13052       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13053         border_element = Feld[xx][yy];          /* may be moving! */
13054       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13055         border_element = Feld[xx][yy];
13056       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13057         border_element = MovingOrBlocked2Element(xx, yy);
13058       else
13059         continue;               /* center and border element do not touch */
13060
13061       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13062                                  player->index_bit, border_side);
13063       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13064                                           CE_PLAYER_TOUCHES_X,
13065                                           player->index_bit, border_side);
13066     }
13067     else if (IS_PLAYER(xx, yy))
13068     {
13069       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13070
13071       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13072       {
13073         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13074           continue;             /* center and border element do not touch */
13075       }
13076
13077       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13078                                  player->index_bit, center_side);
13079       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13080                                           CE_PLAYER_TOUCHES_X,
13081                                           player->index_bit, center_side);
13082       break;
13083     }
13084   }
13085 }
13086
13087 #if USE_ELEMENT_TOUCHING_BUGFIX
13088
13089 void TestIfElementTouchesCustomElement(int x, int y)
13090 {
13091   static int xy[4][2] =
13092   {
13093     { 0, -1 },
13094     { -1, 0 },
13095     { +1, 0 },
13096     { 0, +1 }
13097   };
13098   static int trigger_sides[4][2] =
13099   {
13100     /* center side      border side */
13101     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13102     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13103     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13104     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13105   };
13106   static int touch_dir[4] =
13107   {
13108     MV_LEFT | MV_RIGHT,
13109     MV_UP   | MV_DOWN,
13110     MV_UP   | MV_DOWN,
13111     MV_LEFT | MV_RIGHT
13112   };
13113   boolean change_center_element = FALSE;
13114   int center_element = Feld[x][y];      /* should always be non-moving! */
13115   int border_element_old[NUM_DIRECTIONS];
13116   int i;
13117
13118   for (i = 0; i < NUM_DIRECTIONS; i++)
13119   {
13120     int xx = x + xy[i][0];
13121     int yy = y + xy[i][1];
13122     int border_element;
13123
13124     border_element_old[i] = -1;
13125
13126     if (!IN_LEV_FIELD(xx, yy))
13127       continue;
13128
13129     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13130       border_element = Feld[xx][yy];    /* may be moving! */
13131     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13132       border_element = Feld[xx][yy];
13133     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13134       border_element = MovingOrBlocked2Element(xx, yy);
13135     else
13136       continue;                 /* center and border element do not touch */
13137
13138     border_element_old[i] = border_element;
13139   }
13140
13141   for (i = 0; i < NUM_DIRECTIONS; i++)
13142   {
13143     int xx = x + xy[i][0];
13144     int yy = y + xy[i][1];
13145     int center_side = trigger_sides[i][0];
13146     int border_element = border_element_old[i];
13147
13148     if (border_element == -1)
13149       continue;
13150
13151     /* check for change of border element */
13152     CheckElementChangeBySide(xx, yy, border_element, center_element,
13153                              CE_TOUCHING_X, center_side);
13154   }
13155
13156   for (i = 0; i < NUM_DIRECTIONS; i++)
13157   {
13158     int border_side = trigger_sides[i][1];
13159     int border_element = border_element_old[i];
13160
13161     if (border_element == -1)
13162       continue;
13163
13164     /* check for change of center element (but change it only once) */
13165     if (!change_center_element)
13166       change_center_element =
13167         CheckElementChangeBySide(x, y, center_element, border_element,
13168                                  CE_TOUCHING_X, border_side);
13169   }
13170 }
13171
13172 #else
13173
13174 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13175 {
13176   static int xy[4][2] =
13177   {
13178     { 0, -1 },
13179     { -1, 0 },
13180     { +1, 0 },
13181     { 0, +1 }
13182   };
13183   static int trigger_sides[4][2] =
13184   {
13185     /* center side      border side */
13186     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13187     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13188     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13189     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13190   };
13191   static int touch_dir[4] =
13192   {
13193     MV_LEFT | MV_RIGHT,
13194     MV_UP   | MV_DOWN,
13195     MV_UP   | MV_DOWN,
13196     MV_LEFT | MV_RIGHT
13197   };
13198   boolean change_center_element = FALSE;
13199   int center_element = Feld[x][y];      /* should always be non-moving! */
13200   int i;
13201
13202   for (i = 0; i < NUM_DIRECTIONS; i++)
13203   {
13204     int xx = x + xy[i][0];
13205     int yy = y + xy[i][1];
13206     int center_side = trigger_sides[i][0];
13207     int border_side = trigger_sides[i][1];
13208     int border_element;
13209
13210     if (!IN_LEV_FIELD(xx, yy))
13211       continue;
13212
13213     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13214       border_element = Feld[xx][yy];    /* may be moving! */
13215     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13216       border_element = Feld[xx][yy];
13217     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13218       border_element = MovingOrBlocked2Element(xx, yy);
13219     else
13220       continue;                 /* center and border element do not touch */
13221
13222     /* check for change of center element (but change it only once) */
13223     if (!change_center_element)
13224       change_center_element =
13225         CheckElementChangeBySide(x, y, center_element, border_element,
13226                                  CE_TOUCHING_X, border_side);
13227
13228     /* check for change of border element */
13229     CheckElementChangeBySide(xx, yy, border_element, center_element,
13230                              CE_TOUCHING_X, center_side);
13231   }
13232 }
13233
13234 #endif
13235
13236 void TestIfElementHitsCustomElement(int x, int y, int direction)
13237 {
13238   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13239   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13240   int hitx = x + dx, hity = y + dy;
13241   int hitting_element = Feld[x][y];
13242   int touched_element;
13243
13244   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13245     return;
13246
13247   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13248                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13249
13250   if (IN_LEV_FIELD(hitx, hity))
13251   {
13252     int opposite_direction = MV_DIR_OPPOSITE(direction);
13253     int hitting_side = direction;
13254     int touched_side = opposite_direction;
13255     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13256                           MovDir[hitx][hity] != direction ||
13257                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13258
13259     object_hit = TRUE;
13260
13261     if (object_hit)
13262     {
13263       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13264                                CE_HITTING_X, touched_side);
13265
13266       CheckElementChangeBySide(hitx, hity, touched_element,
13267                                hitting_element, CE_HIT_BY_X, hitting_side);
13268
13269       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13270                                CE_HIT_BY_SOMETHING, opposite_direction);
13271     }
13272   }
13273
13274   /* "hitting something" is also true when hitting the playfield border */
13275   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13276                            CE_HITTING_SOMETHING, direction);
13277 }
13278
13279 #if 0
13280 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13281 {
13282   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13283   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13284   int hitx = x + dx, hity = y + dy;
13285   int hitting_element = Feld[x][y];
13286   int touched_element;
13287 #if 0
13288   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13289                         !IS_FREE(hitx, hity) &&
13290                         (!IS_MOVING(hitx, hity) ||
13291                          MovDir[hitx][hity] != direction ||
13292                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13293 #endif
13294
13295   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13296     return;
13297
13298 #if 0
13299   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13300     return;
13301 #endif
13302
13303   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13304                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13305
13306   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13307                            EP_CAN_SMASH_EVERYTHING, direction);
13308
13309   if (IN_LEV_FIELD(hitx, hity))
13310   {
13311     int opposite_direction = MV_DIR_OPPOSITE(direction);
13312     int hitting_side = direction;
13313     int touched_side = opposite_direction;
13314 #if 0
13315     int touched_element = MovingOrBlocked2Element(hitx, hity);
13316 #endif
13317 #if 1
13318     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13319                           MovDir[hitx][hity] != direction ||
13320                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13321
13322     object_hit = TRUE;
13323 #endif
13324
13325     if (object_hit)
13326     {
13327       int i;
13328
13329       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13330                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13331
13332       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13333                                CE_OTHER_IS_SMASHING, touched_side);
13334
13335       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13336                                CE_OTHER_GETS_SMASHED, hitting_side);
13337     }
13338   }
13339 }
13340 #endif
13341
13342 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13343 {
13344   int i, kill_x = -1, kill_y = -1;
13345
13346   int bad_element = -1;
13347   static int test_xy[4][2] =
13348   {
13349     { 0, -1 },
13350     { -1, 0 },
13351     { +1, 0 },
13352     { 0, +1 }
13353   };
13354   static int test_dir[4] =
13355   {
13356     MV_UP,
13357     MV_LEFT,
13358     MV_RIGHT,
13359     MV_DOWN
13360   };
13361
13362   for (i = 0; i < NUM_DIRECTIONS; i++)
13363   {
13364     int test_x, test_y, test_move_dir, test_element;
13365
13366     test_x = good_x + test_xy[i][0];
13367     test_y = good_y + test_xy[i][1];
13368
13369     if (!IN_LEV_FIELD(test_x, test_y))
13370       continue;
13371
13372     test_move_dir =
13373       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13374
13375     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13376
13377     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13378        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13379     */
13380     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13381         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13382     {
13383       kill_x = test_x;
13384       kill_y = test_y;
13385       bad_element = test_element;
13386
13387       break;
13388     }
13389   }
13390
13391   if (kill_x != -1 || kill_y != -1)
13392   {
13393     if (IS_PLAYER(good_x, good_y))
13394     {
13395       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13396
13397       if (player->shield_deadly_time_left > 0 &&
13398           !IS_INDESTRUCTIBLE(bad_element))
13399         Bang(kill_x, kill_y);
13400       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13401         KillPlayer(player);
13402     }
13403     else
13404       Bang(good_x, good_y);
13405   }
13406 }
13407
13408 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13409 {
13410   int i, kill_x = -1, kill_y = -1;
13411   int bad_element = Feld[bad_x][bad_y];
13412   static int test_xy[4][2] =
13413   {
13414     { 0, -1 },
13415     { -1, 0 },
13416     { +1, 0 },
13417     { 0, +1 }
13418   };
13419   static int touch_dir[4] =
13420   {
13421     MV_LEFT | MV_RIGHT,
13422     MV_UP   | MV_DOWN,
13423     MV_UP   | MV_DOWN,
13424     MV_LEFT | MV_RIGHT
13425   };
13426   static int test_dir[4] =
13427   {
13428     MV_UP,
13429     MV_LEFT,
13430     MV_RIGHT,
13431     MV_DOWN
13432   };
13433
13434   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13435     return;
13436
13437   for (i = 0; i < NUM_DIRECTIONS; i++)
13438   {
13439     int test_x, test_y, test_move_dir, test_element;
13440
13441     test_x = bad_x + test_xy[i][0];
13442     test_y = bad_y + test_xy[i][1];
13443     if (!IN_LEV_FIELD(test_x, test_y))
13444       continue;
13445
13446     test_move_dir =
13447       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13448
13449     test_element = Feld[test_x][test_y];
13450
13451     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13452        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13453     */
13454     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13455         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13456     {
13457       /* good thing is player or penguin that does not move away */
13458       if (IS_PLAYER(test_x, test_y))
13459       {
13460         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13461
13462         if (bad_element == EL_ROBOT && player->is_moving)
13463           continue;     /* robot does not kill player if he is moving */
13464
13465         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13466         {
13467           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13468             continue;           /* center and border element do not touch */
13469         }
13470
13471         kill_x = test_x;
13472         kill_y = test_y;
13473         break;
13474       }
13475       else if (test_element == EL_PENGUIN)
13476       {
13477         kill_x = test_x;
13478         kill_y = test_y;
13479         break;
13480       }
13481     }
13482   }
13483
13484   if (kill_x != -1 || kill_y != -1)
13485   {
13486     if (IS_PLAYER(kill_x, kill_y))
13487     {
13488       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13489
13490       if (player->shield_deadly_time_left > 0 &&
13491           !IS_INDESTRUCTIBLE(bad_element))
13492         Bang(bad_x, bad_y);
13493       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13494         KillPlayer(player);
13495     }
13496     else
13497       Bang(kill_x, kill_y);
13498   }
13499 }
13500
13501 void TestIfPlayerTouchesBadThing(int x, int y)
13502 {
13503   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13504 }
13505
13506 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13507 {
13508   TestIfGoodThingHitsBadThing(x, y, move_dir);
13509 }
13510
13511 void TestIfBadThingTouchesPlayer(int x, int y)
13512 {
13513   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13514 }
13515
13516 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13517 {
13518   TestIfBadThingHitsGoodThing(x, y, move_dir);
13519 }
13520
13521 void TestIfFriendTouchesBadThing(int x, int y)
13522 {
13523   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13524 }
13525
13526 void TestIfBadThingTouchesFriend(int x, int y)
13527 {
13528   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13529 }
13530
13531 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13532 {
13533   int i, kill_x = bad_x, kill_y = bad_y;
13534   static int xy[4][2] =
13535   {
13536     { 0, -1 },
13537     { -1, 0 },
13538     { +1, 0 },
13539     { 0, +1 }
13540   };
13541
13542   for (i = 0; i < NUM_DIRECTIONS; i++)
13543   {
13544     int x, y, element;
13545
13546     x = bad_x + xy[i][0];
13547     y = bad_y + xy[i][1];
13548     if (!IN_LEV_FIELD(x, y))
13549       continue;
13550
13551     element = Feld[x][y];
13552     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13553         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13554     {
13555       kill_x = x;
13556       kill_y = y;
13557       break;
13558     }
13559   }
13560
13561   if (kill_x != bad_x || kill_y != bad_y)
13562     Bang(bad_x, bad_y);
13563 }
13564
13565 void KillPlayer(struct PlayerInfo *player)
13566 {
13567   int jx = player->jx, jy = player->jy;
13568
13569   if (!player->active)
13570     return;
13571
13572   /* the following code was introduced to prevent an infinite loop when calling
13573      -> Bang()
13574      -> CheckTriggeredElementChangeExt()
13575      -> ExecuteCustomElementAction()
13576      -> KillPlayer()
13577      -> (infinitely repeating the above sequence of function calls)
13578      which occurs when killing the player while having a CE with the setting
13579      "kill player X when explosion of <player X>"; the solution using a new
13580      field "player->killed" was chosen for backwards compatibility, although
13581      clever use of the fields "player->active" etc. would probably also work */
13582 #if 1
13583   if (player->killed)
13584     return;
13585 #endif
13586
13587   player->killed = TRUE;
13588
13589   /* remove accessible field at the player's position */
13590   Feld[jx][jy] = EL_EMPTY;
13591
13592   /* deactivate shield (else Bang()/Explode() would not work right) */
13593   player->shield_normal_time_left = 0;
13594   player->shield_deadly_time_left = 0;
13595
13596   Bang(jx, jy);
13597   BuryPlayer(player);
13598 }
13599
13600 static void KillPlayerUnlessEnemyProtected(int x, int y)
13601 {
13602   if (!PLAYER_ENEMY_PROTECTED(x, y))
13603     KillPlayer(PLAYERINFO(x, y));
13604 }
13605
13606 static void KillPlayerUnlessExplosionProtected(int x, int y)
13607 {
13608   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13609     KillPlayer(PLAYERINFO(x, y));
13610 }
13611
13612 void BuryPlayer(struct PlayerInfo *player)
13613 {
13614   int jx = player->jx, jy = player->jy;
13615
13616   if (!player->active)
13617     return;
13618
13619   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13620   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13621
13622   player->GameOver = TRUE;
13623   RemovePlayer(player);
13624 }
13625
13626 void RemovePlayer(struct PlayerInfo *player)
13627 {
13628   int jx = player->jx, jy = player->jy;
13629   int i, found = FALSE;
13630
13631   player->present = FALSE;
13632   player->active = FALSE;
13633
13634   if (!ExplodeField[jx][jy])
13635     StorePlayer[jx][jy] = 0;
13636
13637   if (player->is_moving)
13638     DrawLevelField(player->last_jx, player->last_jy);
13639
13640   for (i = 0; i < MAX_PLAYERS; i++)
13641     if (stored_player[i].active)
13642       found = TRUE;
13643
13644   if (!found)
13645     AllPlayersGone = TRUE;
13646
13647   ExitX = ZX = jx;
13648   ExitY = ZY = jy;
13649 }
13650
13651 #if USE_NEW_SNAP_DELAY
13652 static void setFieldForSnapping(int x, int y, int element, int direction)
13653 {
13654   struct ElementInfo *ei = &element_info[element];
13655   int direction_bit = MV_DIR_TO_BIT(direction);
13656   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13657   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13658                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13659
13660   Feld[x][y] = EL_ELEMENT_SNAPPING;
13661   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13662
13663   ResetGfxAnimation(x, y);
13664
13665   GfxElement[x][y] = element;
13666   GfxAction[x][y] = action;
13667   GfxDir[x][y] = direction;
13668   GfxFrame[x][y] = -1;
13669 }
13670 #endif
13671
13672 /*
13673   =============================================================================
13674   checkDiagonalPushing()
13675   -----------------------------------------------------------------------------
13676   check if diagonal input device direction results in pushing of object
13677   (by checking if the alternative direction is walkable, diggable, ...)
13678   =============================================================================
13679 */
13680
13681 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13682                                     int x, int y, int real_dx, int real_dy)
13683 {
13684   int jx, jy, dx, dy, xx, yy;
13685
13686   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13687     return TRUE;
13688
13689   /* diagonal direction: check alternative direction */
13690   jx = player->jx;
13691   jy = player->jy;
13692   dx = x - jx;
13693   dy = y - jy;
13694   xx = jx + (dx == 0 ? real_dx : 0);
13695   yy = jy + (dy == 0 ? real_dy : 0);
13696
13697   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13698 }
13699
13700 /*
13701   =============================================================================
13702   DigField()
13703   -----------------------------------------------------------------------------
13704   x, y:                 field next to player (non-diagonal) to try to dig to
13705   real_dx, real_dy:     direction as read from input device (can be diagonal)
13706   =============================================================================
13707 */
13708
13709 int DigField(struct PlayerInfo *player,
13710              int oldx, int oldy, int x, int y,
13711              int real_dx, int real_dy, int mode)
13712 {
13713   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13714   boolean player_was_pushing = player->is_pushing;
13715   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13716   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13717   int jx = oldx, jy = oldy;
13718   int dx = x - jx, dy = y - jy;
13719   int nextx = x + dx, nexty = y + dy;
13720   int move_direction = (dx == -1 ? MV_LEFT  :
13721                         dx == +1 ? MV_RIGHT :
13722                         dy == -1 ? MV_UP    :
13723                         dy == +1 ? MV_DOWN  : MV_NONE);
13724   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13725   int dig_side = MV_DIR_OPPOSITE(move_direction);
13726   int old_element = Feld[jx][jy];
13727 #if USE_FIXED_DONT_RUN_INTO
13728   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13729 #else
13730   int element;
13731 #endif
13732   int collect_count;
13733
13734   if (is_player)                /* function can also be called by EL_PENGUIN */
13735   {
13736     if (player->MovPos == 0)
13737     {
13738       player->is_digging = FALSE;
13739       player->is_collecting = FALSE;
13740     }
13741
13742     if (player->MovPos == 0)    /* last pushing move finished */
13743       player->is_pushing = FALSE;
13744
13745     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13746     {
13747       player->is_switching = FALSE;
13748       player->push_delay = -1;
13749
13750       return MP_NO_ACTION;
13751     }
13752   }
13753
13754 #if !USE_FIXED_DONT_RUN_INTO
13755   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13756     return MP_NO_ACTION;
13757 #endif
13758
13759   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13760     old_element = Back[jx][jy];
13761
13762   /* in case of element dropped at player position, check background */
13763   else if (Back[jx][jy] != EL_EMPTY &&
13764            game.engine_version >= VERSION_IDENT(2,2,0,0))
13765     old_element = Back[jx][jy];
13766
13767   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13768     return MP_NO_ACTION;        /* field has no opening in this direction */
13769
13770   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13771     return MP_NO_ACTION;        /* field has no opening in this direction */
13772
13773 #if USE_FIXED_DONT_RUN_INTO
13774   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13775   {
13776     SplashAcid(x, y);
13777
13778     Feld[jx][jy] = player->artwork_element;
13779     InitMovingField(jx, jy, MV_DOWN);
13780     Store[jx][jy] = EL_ACID;
13781     ContinueMoving(jx, jy);
13782     BuryPlayer(player);
13783
13784     return MP_DONT_RUN_INTO;
13785   }
13786 #endif
13787
13788 #if USE_FIXED_DONT_RUN_INTO
13789   if (player_can_move && DONT_RUN_INTO(element))
13790   {
13791     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13792
13793     return MP_DONT_RUN_INTO;
13794   }
13795 #endif
13796
13797 #if USE_FIXED_DONT_RUN_INTO
13798   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13799     return MP_NO_ACTION;
13800 #endif
13801
13802 #if !USE_FIXED_DONT_RUN_INTO
13803   element = Feld[x][y];
13804 #endif
13805
13806   collect_count = element_info[element].collect_count_initial;
13807
13808   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13809     return MP_NO_ACTION;
13810
13811   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13812     player_can_move = player_can_move_or_snap;
13813
13814   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13815       game.engine_version >= VERSION_IDENT(2,2,0,0))
13816   {
13817     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13818                                player->index_bit, dig_side);
13819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13820                                         player->index_bit, dig_side);
13821
13822     if (element == EL_DC_LANDMINE)
13823       Bang(x, y);
13824
13825     if (Feld[x][y] != element)          /* field changed by snapping */
13826       return MP_ACTION;
13827
13828     return MP_NO_ACTION;
13829   }
13830
13831 #if USE_PLAYER_GRAVITY
13832   if (player->gravity && is_player && !player->is_auto_moving &&
13833       canFallDown(player) && move_direction != MV_DOWN &&
13834       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13835     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13836 #else
13837   if (game.gravity && is_player && !player->is_auto_moving &&
13838       canFallDown(player) && move_direction != MV_DOWN &&
13839       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13840     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13841 #endif
13842
13843   if (player_can_move &&
13844       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13845   {
13846     int sound_element = SND_ELEMENT(element);
13847     int sound_action = ACTION_WALKING;
13848
13849     if (IS_RND_GATE(element))
13850     {
13851       if (!player->key[RND_GATE_NR(element)])
13852         return MP_NO_ACTION;
13853     }
13854     else if (IS_RND_GATE_GRAY(element))
13855     {
13856       if (!player->key[RND_GATE_GRAY_NR(element)])
13857         return MP_NO_ACTION;
13858     }
13859     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13860     {
13861       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13862         return MP_NO_ACTION;
13863     }
13864     else if (element == EL_EXIT_OPEN ||
13865              element == EL_EM_EXIT_OPEN ||
13866              element == EL_STEEL_EXIT_OPEN ||
13867              element == EL_EM_STEEL_EXIT_OPEN ||
13868              element == EL_SP_EXIT_OPEN ||
13869              element == EL_SP_EXIT_OPENING)
13870     {
13871       sound_action = ACTION_PASSING;    /* player is passing exit */
13872     }
13873     else if (element == EL_EMPTY)
13874     {
13875       sound_action = ACTION_MOVING;             /* nothing to walk on */
13876     }
13877
13878     /* play sound from background or player, whatever is available */
13879     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13880       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13881     else
13882       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13883   }
13884   else if (player_can_move &&
13885            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13886   {
13887     if (!ACCESS_FROM(element, opposite_direction))
13888       return MP_NO_ACTION;      /* field not accessible from this direction */
13889
13890     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13891       return MP_NO_ACTION;
13892
13893     if (IS_EM_GATE(element))
13894     {
13895       if (!player->key[EM_GATE_NR(element)])
13896         return MP_NO_ACTION;
13897     }
13898     else if (IS_EM_GATE_GRAY(element))
13899     {
13900       if (!player->key[EM_GATE_GRAY_NR(element)])
13901         return MP_NO_ACTION;
13902     }
13903     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13904     {
13905       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13906         return MP_NO_ACTION;
13907     }
13908     else if (IS_EMC_GATE(element))
13909     {
13910       if (!player->key[EMC_GATE_NR(element)])
13911         return MP_NO_ACTION;
13912     }
13913     else if (IS_EMC_GATE_GRAY(element))
13914     {
13915       if (!player->key[EMC_GATE_GRAY_NR(element)])
13916         return MP_NO_ACTION;
13917     }
13918     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13919     {
13920       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13921         return MP_NO_ACTION;
13922     }
13923     else if (element == EL_DC_GATE_WHITE ||
13924              element == EL_DC_GATE_WHITE_GRAY ||
13925              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13926     {
13927       if (player->num_white_keys == 0)
13928         return MP_NO_ACTION;
13929
13930       player->num_white_keys--;
13931     }
13932     else if (IS_SP_PORT(element))
13933     {
13934       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13935           element == EL_SP_GRAVITY_PORT_RIGHT ||
13936           element == EL_SP_GRAVITY_PORT_UP ||
13937           element == EL_SP_GRAVITY_PORT_DOWN)
13938 #if USE_PLAYER_GRAVITY
13939         player->gravity = !player->gravity;
13940 #else
13941         game.gravity = !game.gravity;
13942 #endif
13943       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13944                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13945                element == EL_SP_GRAVITY_ON_PORT_UP ||
13946                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13947 #if USE_PLAYER_GRAVITY
13948         player->gravity = TRUE;
13949 #else
13950         game.gravity = TRUE;
13951 #endif
13952       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13953                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13954                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13955                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13956 #if USE_PLAYER_GRAVITY
13957         player->gravity = FALSE;
13958 #else
13959         game.gravity = FALSE;
13960 #endif
13961     }
13962
13963     /* automatically move to the next field with double speed */
13964     player->programmed_action = move_direction;
13965
13966     if (player->move_delay_reset_counter == 0)
13967     {
13968       player->move_delay_reset_counter = 2;     /* two double speed steps */
13969
13970       DOUBLE_PLAYER_SPEED(player);
13971     }
13972
13973     PlayLevelSoundAction(x, y, ACTION_PASSING);
13974   }
13975   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13976   {
13977     RemoveField(x, y);
13978
13979     if (mode != DF_SNAP)
13980     {
13981       GfxElement[x][y] = GFX_ELEMENT(element);
13982       player->is_digging = TRUE;
13983     }
13984
13985     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13986
13987     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13988                                         player->index_bit, dig_side);
13989
13990     if (mode == DF_SNAP)
13991     {
13992 #if USE_NEW_SNAP_DELAY
13993       if (level.block_snap_field)
13994         setFieldForSnapping(x, y, element, move_direction);
13995       else
13996         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13997 #else
13998       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13999 #endif
14000
14001       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14002                                           player->index_bit, dig_side);
14003     }
14004   }
14005   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14006   {
14007     RemoveField(x, y);
14008
14009     if (is_player && mode != DF_SNAP)
14010     {
14011       GfxElement[x][y] = element;
14012       player->is_collecting = TRUE;
14013     }
14014
14015     if (element == EL_SPEED_PILL)
14016     {
14017       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14018     }
14019     else if (element == EL_EXTRA_TIME && level.time > 0)
14020     {
14021       TimeLeft += level.extra_time;
14022
14023 #if 1
14024       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14025
14026       DisplayGameControlValues();
14027 #else
14028       DrawGameValue_Time(TimeLeft);
14029 #endif
14030     }
14031     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14032     {
14033       player->shield_normal_time_left += level.shield_normal_time;
14034       if (element == EL_SHIELD_DEADLY)
14035         player->shield_deadly_time_left += level.shield_deadly_time;
14036     }
14037     else if (element == EL_DYNAMITE ||
14038              element == EL_EM_DYNAMITE ||
14039              element == EL_SP_DISK_RED)
14040     {
14041       if (player->inventory_size < MAX_INVENTORY_SIZE)
14042         player->inventory_element[player->inventory_size++] = element;
14043
14044       DrawGameDoorValues();
14045     }
14046     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14047     {
14048       player->dynabomb_count++;
14049       player->dynabombs_left++;
14050     }
14051     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14052     {
14053       player->dynabomb_size++;
14054     }
14055     else if (element == EL_DYNABOMB_INCREASE_POWER)
14056     {
14057       player->dynabomb_xl = TRUE;
14058     }
14059     else if (IS_KEY(element))
14060     {
14061       player->key[KEY_NR(element)] = TRUE;
14062
14063       DrawGameDoorValues();
14064     }
14065     else if (element == EL_DC_KEY_WHITE)
14066     {
14067       player->num_white_keys++;
14068
14069       /* display white keys? */
14070       /* DrawGameDoorValues(); */
14071     }
14072     else if (IS_ENVELOPE(element))
14073     {
14074       player->show_envelope = element;
14075     }
14076     else if (element == EL_EMC_LENSES)
14077     {
14078       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14079
14080       RedrawAllInvisibleElementsForLenses();
14081     }
14082     else if (element == EL_EMC_MAGNIFIER)
14083     {
14084       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14085
14086       RedrawAllInvisibleElementsForMagnifier();
14087     }
14088     else if (IS_DROPPABLE(element) ||
14089              IS_THROWABLE(element))     /* can be collected and dropped */
14090     {
14091       int i;
14092
14093       if (collect_count == 0)
14094         player->inventory_infinite_element = element;
14095       else
14096         for (i = 0; i < collect_count; i++)
14097           if (player->inventory_size < MAX_INVENTORY_SIZE)
14098             player->inventory_element[player->inventory_size++] = element;
14099
14100       DrawGameDoorValues();
14101     }
14102     else if (collect_count > 0)
14103     {
14104       local_player->gems_still_needed -= collect_count;
14105       if (local_player->gems_still_needed < 0)
14106         local_player->gems_still_needed = 0;
14107
14108 #if 1
14109       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14110
14111       DisplayGameControlValues();
14112 #else
14113       DrawGameValue_Emeralds(local_player->gems_still_needed);
14114 #endif
14115     }
14116
14117     RaiseScoreElement(element);
14118     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14119
14120     if (is_player)
14121       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14122                                           player->index_bit, dig_side);
14123
14124     if (mode == DF_SNAP)
14125     {
14126 #if USE_NEW_SNAP_DELAY
14127       if (level.block_snap_field)
14128         setFieldForSnapping(x, y, element, move_direction);
14129       else
14130         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14131 #else
14132       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14133 #endif
14134
14135       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14136                                           player->index_bit, dig_side);
14137     }
14138   }
14139   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14140   {
14141     if (mode == DF_SNAP && element != EL_BD_ROCK)
14142       return MP_NO_ACTION;
14143
14144     if (CAN_FALL(element) && dy)
14145       return MP_NO_ACTION;
14146
14147     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14148         !(element == EL_SPRING && level.use_spring_bug))
14149       return MP_NO_ACTION;
14150
14151     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14152         ((move_direction & MV_VERTICAL &&
14153           ((element_info[element].move_pattern & MV_LEFT &&
14154             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14155            (element_info[element].move_pattern & MV_RIGHT &&
14156             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14157          (move_direction & MV_HORIZONTAL &&
14158           ((element_info[element].move_pattern & MV_UP &&
14159             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14160            (element_info[element].move_pattern & MV_DOWN &&
14161             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14162       return MP_NO_ACTION;
14163
14164     /* do not push elements already moving away faster than player */
14165     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14166         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14167       return MP_NO_ACTION;
14168
14169     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14170     {
14171       if (player->push_delay_value == -1 || !player_was_pushing)
14172         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14173     }
14174     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14175     {
14176       if (player->push_delay_value == -1)
14177         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14178     }
14179     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14180     {
14181       if (!player->is_pushing)
14182         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14183     }
14184
14185     player->is_pushing = TRUE;
14186     player->is_active = TRUE;
14187
14188     if (!(IN_LEV_FIELD(nextx, nexty) &&
14189           (IS_FREE(nextx, nexty) ||
14190            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14191             IS_SB_ELEMENT(element)))))
14192       return MP_NO_ACTION;
14193
14194     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14195       return MP_NO_ACTION;
14196
14197     if (player->push_delay == -1)       /* new pushing; restart delay */
14198       player->push_delay = 0;
14199
14200     if (player->push_delay < player->push_delay_value &&
14201         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14202         element != EL_SPRING && element != EL_BALLOON)
14203     {
14204       /* make sure that there is no move delay before next try to push */
14205       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14206         player->move_delay = 0;
14207
14208       return MP_NO_ACTION;
14209     }
14210
14211     if (IS_SB_ELEMENT(element))
14212     {
14213       if (element == EL_SOKOBAN_FIELD_FULL)
14214       {
14215         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14216         local_player->sokobanfields_still_needed++;
14217       }
14218
14219       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14220       {
14221         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14222         local_player->sokobanfields_still_needed--;
14223       }
14224
14225       Feld[x][y] = EL_SOKOBAN_OBJECT;
14226
14227       if (Back[x][y] == Back[nextx][nexty])
14228         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14229       else if (Back[x][y] != 0)
14230         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14231                                     ACTION_EMPTYING);
14232       else
14233         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14234                                     ACTION_FILLING);
14235
14236       if (local_player->sokobanfields_still_needed == 0 &&
14237           game.emulation == EMU_SOKOBAN)
14238       {
14239         PlayerWins(player);
14240
14241         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14242       }
14243     }
14244     else
14245       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14246
14247     InitMovingField(x, y, move_direction);
14248     GfxAction[x][y] = ACTION_PUSHING;
14249
14250     if (mode == DF_SNAP)
14251       ContinueMoving(x, y);
14252     else
14253       MovPos[x][y] = (dx != 0 ? dx : dy);
14254
14255     Pushed[x][y] = TRUE;
14256     Pushed[nextx][nexty] = TRUE;
14257
14258     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14259       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14260     else
14261       player->push_delay_value = -1;    /* get new value later */
14262
14263     /* check for element change _after_ element has been pushed */
14264     if (game.use_change_when_pushing_bug)
14265     {
14266       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14267                                  player->index_bit, dig_side);
14268       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14269                                           player->index_bit, dig_side);
14270     }
14271   }
14272   else if (IS_SWITCHABLE(element))
14273   {
14274     if (PLAYER_SWITCHING(player, x, y))
14275     {
14276       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14277                                           player->index_bit, dig_side);
14278
14279       return MP_ACTION;
14280     }
14281
14282     player->is_switching = TRUE;
14283     player->switch_x = x;
14284     player->switch_y = y;
14285
14286     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14287
14288     if (element == EL_ROBOT_WHEEL)
14289     {
14290       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14291       ZX = x;
14292       ZY = y;
14293
14294       game.robot_wheel_active = TRUE;
14295
14296       DrawLevelField(x, y);
14297     }
14298     else if (element == EL_SP_TERMINAL)
14299     {
14300       int xx, yy;
14301
14302       SCAN_PLAYFIELD(xx, yy)
14303       {
14304         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14305           Bang(xx, yy);
14306         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14307           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14308       }
14309     }
14310     else if (IS_BELT_SWITCH(element))
14311     {
14312       ToggleBeltSwitch(x, y);
14313     }
14314     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14315              element == EL_SWITCHGATE_SWITCH_DOWN ||
14316              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14317              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14318     {
14319       ToggleSwitchgateSwitch(x, y);
14320     }
14321     else if (element == EL_LIGHT_SWITCH ||
14322              element == EL_LIGHT_SWITCH_ACTIVE)
14323     {
14324       ToggleLightSwitch(x, y);
14325     }
14326     else if (element == EL_TIMEGATE_SWITCH ||
14327              element == EL_DC_TIMEGATE_SWITCH)
14328     {
14329       ActivateTimegateSwitch(x, y);
14330     }
14331     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14332              element == EL_BALLOON_SWITCH_RIGHT ||
14333              element == EL_BALLOON_SWITCH_UP    ||
14334              element == EL_BALLOON_SWITCH_DOWN  ||
14335              element == EL_BALLOON_SWITCH_NONE  ||
14336              element == EL_BALLOON_SWITCH_ANY)
14337     {
14338       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14339                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14340                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14341                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14342                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14343                              move_direction);
14344     }
14345     else if (element == EL_LAMP)
14346     {
14347       Feld[x][y] = EL_LAMP_ACTIVE;
14348       local_player->lights_still_needed--;
14349
14350       ResetGfxAnimation(x, y);
14351       DrawLevelField(x, y);
14352     }
14353     else if (element == EL_TIME_ORB_FULL)
14354     {
14355       Feld[x][y] = EL_TIME_ORB_EMPTY;
14356
14357       if (level.time > 0 || level.use_time_orb_bug)
14358       {
14359         TimeLeft += level.time_orb_time;
14360
14361 #if 1
14362         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14363
14364         DisplayGameControlValues();
14365 #else
14366         DrawGameValue_Time(TimeLeft);
14367 #endif
14368       }
14369
14370       ResetGfxAnimation(x, y);
14371       DrawLevelField(x, y);
14372     }
14373     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14374              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14375     {
14376       int xx, yy;
14377
14378       game.ball_state = !game.ball_state;
14379
14380       SCAN_PLAYFIELD(xx, yy)
14381       {
14382         int e = Feld[xx][yy];
14383
14384         if (game.ball_state)
14385         {
14386           if (e == EL_EMC_MAGIC_BALL)
14387             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14388           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14389             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14390         }
14391         else
14392         {
14393           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14394             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14395           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14396             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14397         }
14398       }
14399     }
14400
14401     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14402                                         player->index_bit, dig_side);
14403
14404     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14405                                         player->index_bit, dig_side);
14406
14407     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14408                                         player->index_bit, dig_side);
14409
14410     return MP_ACTION;
14411   }
14412   else
14413   {
14414     if (!PLAYER_SWITCHING(player, x, y))
14415     {
14416       player->is_switching = TRUE;
14417       player->switch_x = x;
14418       player->switch_y = y;
14419
14420       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14421                                  player->index_bit, dig_side);
14422       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14423                                           player->index_bit, dig_side);
14424
14425       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14426                                  player->index_bit, dig_side);
14427       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14428                                           player->index_bit, dig_side);
14429     }
14430
14431     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14432                                player->index_bit, dig_side);
14433     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14434                                         player->index_bit, dig_side);
14435
14436     return MP_NO_ACTION;
14437   }
14438
14439   player->push_delay = -1;
14440
14441   if (is_player)                /* function can also be called by EL_PENGUIN */
14442   {
14443     if (Feld[x][y] != element)          /* really digged/collected something */
14444     {
14445       player->is_collecting = !player->is_digging;
14446       player->is_active = TRUE;
14447     }
14448   }
14449
14450   return MP_MOVING;
14451 }
14452
14453 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14454 {
14455   int jx = player->jx, jy = player->jy;
14456   int x = jx + dx, y = jy + dy;
14457   int snap_direction = (dx == -1 ? MV_LEFT  :
14458                         dx == +1 ? MV_RIGHT :
14459                         dy == -1 ? MV_UP    :
14460                         dy == +1 ? MV_DOWN  : MV_NONE);
14461   boolean can_continue_snapping = (level.continuous_snapping &&
14462                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14463
14464   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14465     return FALSE;
14466
14467   if (!player->active || !IN_LEV_FIELD(x, y))
14468     return FALSE;
14469
14470   if (dx && dy)
14471     return FALSE;
14472
14473   if (!dx && !dy)
14474   {
14475     if (player->MovPos == 0)
14476       player->is_pushing = FALSE;
14477
14478     player->is_snapping = FALSE;
14479
14480     if (player->MovPos == 0)
14481     {
14482       player->is_moving = FALSE;
14483       player->is_digging = FALSE;
14484       player->is_collecting = FALSE;
14485     }
14486
14487     return FALSE;
14488   }
14489
14490 #if USE_NEW_CONTINUOUS_SNAPPING
14491   /* prevent snapping with already pressed snap key when not allowed */
14492   if (player->is_snapping && !can_continue_snapping)
14493     return FALSE;
14494 #else
14495   if (player->is_snapping)
14496     return FALSE;
14497 #endif
14498
14499   player->MovDir = snap_direction;
14500
14501   if (player->MovPos == 0)
14502   {
14503     player->is_moving = FALSE;
14504     player->is_digging = FALSE;
14505     player->is_collecting = FALSE;
14506   }
14507
14508   player->is_dropping = FALSE;
14509   player->is_dropping_pressed = FALSE;
14510   player->drop_pressed_delay = 0;
14511
14512   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14513     return FALSE;
14514
14515   player->is_snapping = TRUE;
14516   player->is_active = TRUE;
14517
14518   if (player->MovPos == 0)
14519   {
14520     player->is_moving = FALSE;
14521     player->is_digging = FALSE;
14522     player->is_collecting = FALSE;
14523   }
14524
14525   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14526     DrawLevelField(player->last_jx, player->last_jy);
14527
14528   DrawLevelField(x, y);
14529
14530   return TRUE;
14531 }
14532
14533 boolean DropElement(struct PlayerInfo *player)
14534 {
14535   int old_element, new_element;
14536   int dropx = player->jx, dropy = player->jy;
14537   int drop_direction = player->MovDir;
14538   int drop_side = drop_direction;
14539 #if 1
14540   int drop_element = get_next_dropped_element(player);
14541 #else
14542   int drop_element = (player->inventory_size > 0 ?
14543                       player->inventory_element[player->inventory_size - 1] :
14544                       player->inventory_infinite_element != EL_UNDEFINED ?
14545                       player->inventory_infinite_element :
14546                       player->dynabombs_left > 0 ?
14547                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14548                       EL_UNDEFINED);
14549 #endif
14550
14551   player->is_dropping_pressed = TRUE;
14552
14553   /* do not drop an element on top of another element; when holding drop key
14554      pressed without moving, dropped element must move away before the next
14555      element can be dropped (this is especially important if the next element
14556      is dynamite, which can be placed on background for historical reasons) */
14557   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14558     return MP_ACTION;
14559
14560   if (IS_THROWABLE(drop_element))
14561   {
14562     dropx += GET_DX_FROM_DIR(drop_direction);
14563     dropy += GET_DY_FROM_DIR(drop_direction);
14564
14565     if (!IN_LEV_FIELD(dropx, dropy))
14566       return FALSE;
14567   }
14568
14569   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14570   new_element = drop_element;           /* default: no change when dropping */
14571
14572   /* check if player is active, not moving and ready to drop */
14573   if (!player->active || player->MovPos || player->drop_delay > 0)
14574     return FALSE;
14575
14576   /* check if player has anything that can be dropped */
14577   if (new_element == EL_UNDEFINED)
14578     return FALSE;
14579
14580   /* check if drop key was pressed long enough for EM style dynamite */
14581   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14582     return FALSE;
14583
14584   /* check if anything can be dropped at the current position */
14585   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14586     return FALSE;
14587
14588   /* collected custom elements can only be dropped on empty fields */
14589   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14590     return FALSE;
14591
14592   if (old_element != EL_EMPTY)
14593     Back[dropx][dropy] = old_element;   /* store old element on this field */
14594
14595   ResetGfxAnimation(dropx, dropy);
14596   ResetRandomAnimationValue(dropx, dropy);
14597
14598   if (player->inventory_size > 0 ||
14599       player->inventory_infinite_element != EL_UNDEFINED)
14600   {
14601     if (player->inventory_size > 0)
14602     {
14603       player->inventory_size--;
14604
14605       DrawGameDoorValues();
14606
14607       if (new_element == EL_DYNAMITE)
14608         new_element = EL_DYNAMITE_ACTIVE;
14609       else if (new_element == EL_EM_DYNAMITE)
14610         new_element = EL_EM_DYNAMITE_ACTIVE;
14611       else if (new_element == EL_SP_DISK_RED)
14612         new_element = EL_SP_DISK_RED_ACTIVE;
14613     }
14614
14615     Feld[dropx][dropy] = new_element;
14616
14617     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14618       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14619                           el2img(Feld[dropx][dropy]), 0);
14620
14621     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14622
14623     /* needed if previous element just changed to "empty" in the last frame */
14624     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14625
14626     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14627                                player->index_bit, drop_side);
14628     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14629                                         CE_PLAYER_DROPS_X,
14630                                         player->index_bit, drop_side);
14631
14632     TestIfElementTouchesCustomElement(dropx, dropy);
14633   }
14634   else          /* player is dropping a dyna bomb */
14635   {
14636     player->dynabombs_left--;
14637
14638     Feld[dropx][dropy] = new_element;
14639
14640     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14641       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14642                           el2img(Feld[dropx][dropy]), 0);
14643
14644     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14645   }
14646
14647   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14648     InitField_WithBug1(dropx, dropy, FALSE);
14649
14650   new_element = Feld[dropx][dropy];     /* element might have changed */
14651
14652   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14653       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14654   {
14655     int move_direction, nextx, nexty;
14656
14657     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14658       MovDir[dropx][dropy] = drop_direction;
14659
14660     move_direction = MovDir[dropx][dropy];
14661     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14662     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14663
14664     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14665
14666 #if USE_FIX_IMPACT_COLLISION
14667     /* do not cause impact style collision by dropping elements that can fall */
14668     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14669 #else
14670     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14671 #endif
14672   }
14673
14674   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14675   player->is_dropping = TRUE;
14676
14677   player->drop_pressed_delay = 0;
14678   player->is_dropping_pressed = FALSE;
14679
14680   player->drop_x = dropx;
14681   player->drop_y = dropy;
14682
14683   return TRUE;
14684 }
14685
14686 /* ------------------------------------------------------------------------- */
14687 /* game sound playing functions                                              */
14688 /* ------------------------------------------------------------------------- */
14689
14690 static int *loop_sound_frame = NULL;
14691 static int *loop_sound_volume = NULL;
14692
14693 void InitPlayLevelSound()
14694 {
14695   int num_sounds = getSoundListSize();
14696
14697   checked_free(loop_sound_frame);
14698   checked_free(loop_sound_volume);
14699
14700   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14701   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14702 }
14703
14704 static void PlayLevelSound(int x, int y, int nr)
14705 {
14706   int sx = SCREENX(x), sy = SCREENY(y);
14707   int volume, stereo_position;
14708   int max_distance = 8;
14709   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14710
14711   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14712       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14713     return;
14714
14715   if (!IN_LEV_FIELD(x, y) ||
14716       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14717       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14718     return;
14719
14720   volume = SOUND_MAX_VOLUME;
14721
14722   if (!IN_SCR_FIELD(sx, sy))
14723   {
14724     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14725     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14726
14727     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14728   }
14729
14730   stereo_position = (SOUND_MAX_LEFT +
14731                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14732                      (SCR_FIELDX + 2 * max_distance));
14733
14734   if (IS_LOOP_SOUND(nr))
14735   {
14736     /* This assures that quieter loop sounds do not overwrite louder ones,
14737        while restarting sound volume comparison with each new game frame. */
14738
14739     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14740       return;
14741
14742     loop_sound_volume[nr] = volume;
14743     loop_sound_frame[nr] = FrameCounter;
14744   }
14745
14746   PlaySoundExt(nr, volume, stereo_position, type);
14747 }
14748
14749 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14750 {
14751   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14752                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14753                  y < LEVELY(BY1) ? LEVELY(BY1) :
14754                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14755                  sound_action);
14756 }
14757
14758 static void PlayLevelSoundAction(int x, int y, int action)
14759 {
14760   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14761 }
14762
14763 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14764 {
14765   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14766
14767   if (sound_effect != SND_UNDEFINED)
14768     PlayLevelSound(x, y, sound_effect);
14769 }
14770
14771 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14772                                               int action)
14773 {
14774   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14775
14776   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14777     PlayLevelSound(x, y, sound_effect);
14778 }
14779
14780 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14781 {
14782   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14783
14784   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14785     PlayLevelSound(x, y, sound_effect);
14786 }
14787
14788 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14789 {
14790   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14791
14792   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14793     StopSound(sound_effect);
14794 }
14795
14796 static void PlayLevelMusic()
14797 {
14798   if (levelset.music[level_nr] != MUS_UNDEFINED)
14799     PlayMusic(levelset.music[level_nr]);        /* from config file */
14800   else
14801     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14802 }
14803
14804 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14805 {
14806   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14807   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14808   int x = xx - 1 - offset;
14809   int y = yy - 1 - offset;
14810
14811   switch (sample)
14812   {
14813     case SAMPLE_blank:
14814       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14815       break;
14816
14817     case SAMPLE_roll:
14818       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14819       break;
14820
14821     case SAMPLE_stone:
14822       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14823       break;
14824
14825     case SAMPLE_nut:
14826       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14827       break;
14828
14829     case SAMPLE_crack:
14830       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14831       break;
14832
14833     case SAMPLE_bug:
14834       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14835       break;
14836
14837     case SAMPLE_tank:
14838       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14839       break;
14840
14841     case SAMPLE_android_clone:
14842       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14843       break;
14844
14845     case SAMPLE_android_move:
14846       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14847       break;
14848
14849     case SAMPLE_spring:
14850       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14851       break;
14852
14853     case SAMPLE_slurp:
14854       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14855       break;
14856
14857     case SAMPLE_eater:
14858       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14859       break;
14860
14861     case SAMPLE_eater_eat:
14862       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14863       break;
14864
14865     case SAMPLE_alien:
14866       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14867       break;
14868
14869     case SAMPLE_collect:
14870       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14871       break;
14872
14873     case SAMPLE_diamond:
14874       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14875       break;
14876
14877     case SAMPLE_squash:
14878       /* !!! CHECK THIS !!! */
14879 #if 1
14880       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14881 #else
14882       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14883 #endif
14884       break;
14885
14886     case SAMPLE_wonderfall:
14887       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14888       break;
14889
14890     case SAMPLE_drip:
14891       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14892       break;
14893
14894     case SAMPLE_push:
14895       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14896       break;
14897
14898     case SAMPLE_dirt:
14899       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14900       break;
14901
14902     case SAMPLE_acid:
14903       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14904       break;
14905
14906     case SAMPLE_ball:
14907       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14908       break;
14909
14910     case SAMPLE_grow:
14911       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14912       break;
14913
14914     case SAMPLE_wonder:
14915       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14916       break;
14917
14918     case SAMPLE_door:
14919       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14920       break;
14921
14922     case SAMPLE_exit_open:
14923       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14924       break;
14925
14926     case SAMPLE_exit_leave:
14927       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14928       break;
14929
14930     case SAMPLE_dynamite:
14931       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14932       break;
14933
14934     case SAMPLE_tick:
14935       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14936       break;
14937
14938     case SAMPLE_press:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14940       break;
14941
14942     case SAMPLE_wheel:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14944       break;
14945
14946     case SAMPLE_boom:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14948       break;
14949
14950     case SAMPLE_die:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14952       break;
14953
14954     case SAMPLE_time:
14955       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14956       break;
14957
14958     default:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14960       break;
14961   }
14962 }
14963
14964 #if 0
14965 void ChangeTime(int value)
14966 {
14967   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14968
14969   *time += value;
14970
14971   /* EMC game engine uses value from time counter of RND game engine */
14972   level.native_em_level->lev->time = *time;
14973
14974   DrawGameValue_Time(*time);
14975 }
14976
14977 void RaiseScore(int value)
14978 {
14979   /* EMC game engine and RND game engine have separate score counters */
14980   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14981                 &level.native_em_level->lev->score : &local_player->score);
14982
14983   *score += value;
14984
14985   DrawGameValue_Score(*score);
14986 }
14987 #endif
14988
14989 void RaiseScore(int value)
14990 {
14991   local_player->score += value;
14992
14993 #if 1
14994   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14995
14996   DisplayGameControlValues();
14997 #else
14998   DrawGameValue_Score(local_player->score);
14999 #endif
15000 }
15001
15002 void RaiseScoreElement(int element)
15003 {
15004   switch (element)
15005   {
15006     case EL_EMERALD:
15007     case EL_BD_DIAMOND:
15008     case EL_EMERALD_YELLOW:
15009     case EL_EMERALD_RED:
15010     case EL_EMERALD_PURPLE:
15011     case EL_SP_INFOTRON:
15012       RaiseScore(level.score[SC_EMERALD]);
15013       break;
15014     case EL_DIAMOND:
15015       RaiseScore(level.score[SC_DIAMOND]);
15016       break;
15017     case EL_CRYSTAL:
15018       RaiseScore(level.score[SC_CRYSTAL]);
15019       break;
15020     case EL_PEARL:
15021       RaiseScore(level.score[SC_PEARL]);
15022       break;
15023     case EL_BUG:
15024     case EL_BD_BUTTERFLY:
15025     case EL_SP_ELECTRON:
15026       RaiseScore(level.score[SC_BUG]);
15027       break;
15028     case EL_SPACESHIP:
15029     case EL_BD_FIREFLY:
15030     case EL_SP_SNIKSNAK:
15031       RaiseScore(level.score[SC_SPACESHIP]);
15032       break;
15033     case EL_YAMYAM:
15034     case EL_DARK_YAMYAM:
15035       RaiseScore(level.score[SC_YAMYAM]);
15036       break;
15037     case EL_ROBOT:
15038       RaiseScore(level.score[SC_ROBOT]);
15039       break;
15040     case EL_PACMAN:
15041       RaiseScore(level.score[SC_PACMAN]);
15042       break;
15043     case EL_NUT:
15044       RaiseScore(level.score[SC_NUT]);
15045       break;
15046     case EL_DYNAMITE:
15047     case EL_EM_DYNAMITE:
15048     case EL_SP_DISK_RED:
15049     case EL_DYNABOMB_INCREASE_NUMBER:
15050     case EL_DYNABOMB_INCREASE_SIZE:
15051     case EL_DYNABOMB_INCREASE_POWER:
15052       RaiseScore(level.score[SC_DYNAMITE]);
15053       break;
15054     case EL_SHIELD_NORMAL:
15055     case EL_SHIELD_DEADLY:
15056       RaiseScore(level.score[SC_SHIELD]);
15057       break;
15058     case EL_EXTRA_TIME:
15059       RaiseScore(level.extra_time_score);
15060       break;
15061     case EL_KEY_1:
15062     case EL_KEY_2:
15063     case EL_KEY_3:
15064     case EL_KEY_4:
15065     case EL_EM_KEY_1:
15066     case EL_EM_KEY_2:
15067     case EL_EM_KEY_3:
15068     case EL_EM_KEY_4:
15069     case EL_EMC_KEY_5:
15070     case EL_EMC_KEY_6:
15071     case EL_EMC_KEY_7:
15072     case EL_EMC_KEY_8:
15073     case EL_DC_KEY_WHITE:
15074       RaiseScore(level.score[SC_KEY]);
15075       break;
15076     default:
15077       RaiseScore(element_info[element].collect_score);
15078       break;
15079   }
15080 }
15081
15082 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15083 {
15084   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15085   {
15086 #if defined(NETWORK_AVALIABLE)
15087     if (options.network)
15088       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15089     else
15090 #endif
15091     {
15092       if (quick_quit)
15093       {
15094 #if 1
15095
15096 #if 1
15097         FadeSkipNextFadeIn();
15098 #else
15099         fading = fading_none;
15100 #endif
15101
15102 #else
15103         OpenDoor(DOOR_CLOSE_1);
15104 #endif
15105
15106         game_status = GAME_MODE_MAIN;
15107
15108 #if 1
15109         DrawAndFadeInMainMenu(REDRAW_FIELD);
15110 #else
15111         DrawMainMenu();
15112 #endif
15113       }
15114       else
15115       {
15116 #if 0
15117         FadeOut(REDRAW_FIELD);
15118 #endif
15119
15120         game_status = GAME_MODE_MAIN;
15121
15122         DrawAndFadeInMainMenu(REDRAW_FIELD);
15123       }
15124     }
15125   }
15126   else          /* continue playing the game */
15127   {
15128     if (tape.playing && tape.deactivate_display)
15129       TapeDeactivateDisplayOff(TRUE);
15130
15131     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15132
15133     if (tape.playing && tape.deactivate_display)
15134       TapeDeactivateDisplayOn();
15135   }
15136 }
15137
15138 void RequestQuitGame(boolean ask_if_really_quit)
15139 {
15140   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15141   boolean skip_request = AllPlayersGone || quick_quit;
15142
15143   RequestQuitGameExt(skip_request, quick_quit,
15144                      "Do you really want to quit the game ?");
15145 }
15146
15147
15148 /* ------------------------------------------------------------------------- */
15149 /* random generator functions                                                */
15150 /* ------------------------------------------------------------------------- */
15151
15152 unsigned int InitEngineRandom_RND(long seed)
15153 {
15154   game.num_random_calls = 0;
15155
15156 #if 0
15157   unsigned int rnd_seed = InitEngineRandom(seed);
15158
15159   printf("::: START RND: %d\n", rnd_seed);
15160
15161   return rnd_seed;
15162 #else
15163
15164   return InitEngineRandom(seed);
15165
15166 #endif
15167
15168 }
15169
15170 unsigned int RND(int max)
15171 {
15172   if (max > 0)
15173   {
15174     game.num_random_calls++;
15175
15176     return GetEngineRandom(max);
15177   }
15178
15179   return 0;
15180 }
15181
15182
15183 /* ------------------------------------------------------------------------- */
15184 /* game engine snapshot handling functions                                   */
15185 /* ------------------------------------------------------------------------- */
15186
15187 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15188
15189 struct EngineSnapshotInfo
15190 {
15191   /* runtime values for custom element collect score */
15192   int collect_score[NUM_CUSTOM_ELEMENTS];
15193
15194   /* runtime values for group element choice position */
15195   int choice_pos[NUM_GROUP_ELEMENTS];
15196
15197   /* runtime values for belt position animations */
15198   int belt_graphic[4 * NUM_BELT_PARTS];
15199   int belt_anim_mode[4 * NUM_BELT_PARTS];
15200 };
15201
15202 struct EngineSnapshotNodeInfo
15203 {
15204   void *buffer_orig;
15205   void *buffer_copy;
15206   int size;
15207 };
15208
15209 static struct EngineSnapshotInfo engine_snapshot_rnd;
15210 static ListNode *engine_snapshot_list = NULL;
15211 static char *snapshot_level_identifier = NULL;
15212 static int snapshot_level_nr = -1;
15213
15214 void FreeEngineSnapshot()
15215 {
15216   while (engine_snapshot_list != NULL)
15217     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15218                        checked_free);
15219
15220   setString(&snapshot_level_identifier, NULL);
15221   snapshot_level_nr = -1;
15222 }
15223
15224 static void SaveEngineSnapshotValues_RND()
15225 {
15226   static int belt_base_active_element[4] =
15227   {
15228     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15229     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15230     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15231     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15232   };
15233   int i, j;
15234
15235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15236   {
15237     int element = EL_CUSTOM_START + i;
15238
15239     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15240   }
15241
15242   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15243   {
15244     int element = EL_GROUP_START + i;
15245
15246     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15247   }
15248
15249   for (i = 0; i < 4; i++)
15250   {
15251     for (j = 0; j < NUM_BELT_PARTS; j++)
15252     {
15253       int element = belt_base_active_element[i] + j;
15254       int graphic = el2img(element);
15255       int anim_mode = graphic_info[graphic].anim_mode;
15256
15257       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15258       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15259     }
15260   }
15261 }
15262
15263 static void LoadEngineSnapshotValues_RND()
15264 {
15265   unsigned long num_random_calls = game.num_random_calls;
15266   int i, j;
15267
15268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15269   {
15270     int element = EL_CUSTOM_START + i;
15271
15272     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15273   }
15274
15275   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15276   {
15277     int element = EL_GROUP_START + i;
15278
15279     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15280   }
15281
15282   for (i = 0; i < 4; i++)
15283   {
15284     for (j = 0; j < NUM_BELT_PARTS; j++)
15285     {
15286       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15287       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15288
15289       graphic_info[graphic].anim_mode = anim_mode;
15290     }
15291   }
15292
15293   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15294   {
15295     InitRND(tape.random_seed);
15296     for (i = 0; i < num_random_calls; i++)
15297       RND(1);
15298   }
15299
15300   if (game.num_random_calls != num_random_calls)
15301   {
15302     Error(ERR_INFO, "number of random calls out of sync");
15303     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15304     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15305     Error(ERR_EXIT, "this should not happen -- please debug");
15306   }
15307 }
15308
15309 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15310 {
15311   struct EngineSnapshotNodeInfo *bi =
15312     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15313
15314   bi->buffer_orig = buffer;
15315   bi->buffer_copy = checked_malloc(size);
15316   bi->size = size;
15317
15318   memcpy(bi->buffer_copy, buffer, size);
15319
15320   addNodeToList(&engine_snapshot_list, NULL, bi);
15321 }
15322
15323 void SaveEngineSnapshot()
15324 {
15325   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15326
15327   if (level_editor_test_game)   /* do not save snapshots from editor */
15328     return;
15329
15330   /* copy some special values to a structure better suited for the snapshot */
15331
15332   SaveEngineSnapshotValues_RND();
15333   SaveEngineSnapshotValues_EM();
15334
15335   /* save values stored in special snapshot structure */
15336
15337   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15338   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15339
15340   /* save further RND engine values */
15341
15342   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15343   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15344   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15345
15346   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15347   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15348   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15349   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15350
15351   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15352   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15353   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15354   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15355   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15356
15357   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15358   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15359   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15360
15361   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15362
15363   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15364
15365   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15366   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15367
15368   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15369   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15370   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15371   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15372   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15373   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15374   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15375   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15376   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15377   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15378   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15379   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15380   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15381   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15382   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15383   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15385   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15386
15387   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15388   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15389
15390   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15391   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15392   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15393
15394   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15395   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15396
15397   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15398   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15399   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15400   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15401   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15402
15403   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15404   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15405
15406   /* save level identification information */
15407
15408   setString(&snapshot_level_identifier, leveldir_current->identifier);
15409   snapshot_level_nr = level_nr;
15410
15411 #if 0
15412   ListNode *node = engine_snapshot_list;
15413   int num_bytes = 0;
15414
15415   while (node != NULL)
15416   {
15417     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15418
15419     node = node->next;
15420   }
15421
15422   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15423 #endif
15424 }
15425
15426 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15427 {
15428   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15429 }
15430
15431 void LoadEngineSnapshot()
15432 {
15433   ListNode *node = engine_snapshot_list;
15434
15435   if (engine_snapshot_list == NULL)
15436     return;
15437
15438   while (node != NULL)
15439   {
15440     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15441
15442     node = node->next;
15443   }
15444
15445   /* restore special values from snapshot structure */
15446
15447   LoadEngineSnapshotValues_RND();
15448   LoadEngineSnapshotValues_EM();
15449 }
15450
15451 boolean CheckEngineSnapshot()
15452 {
15453   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15454           snapshot_level_nr == level_nr);
15455 }
15456
15457
15458 /* ---------- new game button stuff ---------------------------------------- */
15459
15460 /* graphic position values for game buttons */
15461 #define GAME_BUTTON_XSIZE       30
15462 #define GAME_BUTTON_YSIZE       30
15463 #define GAME_BUTTON_XPOS        5
15464 #define GAME_BUTTON_YPOS        215
15465 #define SOUND_BUTTON_XPOS       5
15466 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15467
15468 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15469 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15470 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15471 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15472 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15473 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15474
15475 static struct
15476 {
15477   int *x, *y;
15478   int gd_x, gd_y;
15479   int gadget_id;
15480   char *infotext;
15481 } gamebutton_info[NUM_GAME_BUTTONS] =
15482 {
15483 #if 1
15484   {
15485     &game.button.stop.x,        &game.button.stop.y,
15486     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15487     GAME_CTRL_ID_STOP,
15488     "stop game"
15489   },
15490   {
15491     &game.button.pause.x,       &game.button.pause.y,
15492     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15493     GAME_CTRL_ID_PAUSE,
15494     "pause game"
15495   },
15496   {
15497     &game.button.play.x,        &game.button.play.y,
15498     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15499     GAME_CTRL_ID_PLAY,
15500     "play game"
15501   },
15502   {
15503     &game.button.sound_music.x, &game.button.sound_music.y,
15504     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15505     SOUND_CTRL_ID_MUSIC,
15506     "background music on/off"
15507   },
15508   {
15509     &game.button.sound_loops.x, &game.button.sound_loops.y,
15510     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15511     SOUND_CTRL_ID_LOOPS,
15512     "sound loops on/off"
15513   },
15514   {
15515     &game.button.sound_simple.x,&game.button.sound_simple.y,
15516     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15517     SOUND_CTRL_ID_SIMPLE,
15518     "normal sounds on/off"
15519   }
15520 #else
15521   {
15522     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15523     GAME_CTRL_ID_STOP,
15524     "stop game"
15525   },
15526   {
15527     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15528     GAME_CTRL_ID_PAUSE,
15529     "pause game"
15530   },
15531   {
15532     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15533     GAME_CTRL_ID_PLAY,
15534     "play game"
15535   },
15536   {
15537     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15538     SOUND_CTRL_ID_MUSIC,
15539     "background music on/off"
15540   },
15541   {
15542     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15543     SOUND_CTRL_ID_LOOPS,
15544     "sound loops on/off"
15545   },
15546   {
15547     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15548     SOUND_CTRL_ID_SIMPLE,
15549     "normal sounds on/off"
15550   }
15551 #endif
15552 };
15553
15554 void CreateGameButtons()
15555 {
15556   int i;
15557
15558   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15559   {
15560     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15561     struct GadgetInfo *gi;
15562     int button_type;
15563     boolean checked;
15564     unsigned long event_mask;
15565     int x, y;
15566     int gd_xoffset, gd_yoffset;
15567     int gd_x1, gd_x2, gd_y1, gd_y2;
15568     int id = i;
15569
15570     x = DX + *gamebutton_info[i].x;
15571     y = DY + *gamebutton_info[i].y;
15572     gd_xoffset = gamebutton_info[i].gd_x;
15573     gd_yoffset = gamebutton_info[i].gd_y;
15574     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15575     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15576
15577     if (id == GAME_CTRL_ID_STOP ||
15578         id == GAME_CTRL_ID_PAUSE ||
15579         id == GAME_CTRL_ID_PLAY)
15580     {
15581       button_type = GD_TYPE_NORMAL_BUTTON;
15582       checked = FALSE;
15583       event_mask = GD_EVENT_RELEASED;
15584       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15585       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15586     }
15587     else
15588     {
15589       button_type = GD_TYPE_CHECK_BUTTON;
15590       checked =
15591         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15592          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15593          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15594       event_mask = GD_EVENT_PRESSED;
15595       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15596       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15597     }
15598
15599     gi = CreateGadget(GDI_CUSTOM_ID, id,
15600                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15601 #if 1
15602                       GDI_X, x,
15603                       GDI_Y, y,
15604 #else
15605                       GDI_X, DX + gd_xoffset,
15606                       GDI_Y, DY + gd_yoffset,
15607 #endif
15608                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15609                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15610                       GDI_TYPE, button_type,
15611                       GDI_STATE, GD_BUTTON_UNPRESSED,
15612                       GDI_CHECKED, checked,
15613                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15614                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15615                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15616                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15617                       GDI_DIRECT_DRAW, FALSE,
15618                       GDI_EVENT_MASK, event_mask,
15619                       GDI_CALLBACK_ACTION, HandleGameButtons,
15620                       GDI_END);
15621
15622     if (gi == NULL)
15623       Error(ERR_EXIT, "cannot create gadget");
15624
15625     game_gadget[id] = gi;
15626   }
15627 }
15628
15629 void FreeGameButtons()
15630 {
15631   int i;
15632
15633   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15634     FreeGadget(game_gadget[i]);
15635 }
15636
15637 static void MapGameButtons()
15638 {
15639   int i;
15640
15641   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15642     MapGadget(game_gadget[i]);
15643 }
15644
15645 void UnmapGameButtons()
15646 {
15647   int i;
15648
15649   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15650     UnmapGadget(game_gadget[i]);
15651 }
15652
15653 void RedrawGameButtons()
15654 {
15655   int i;
15656
15657   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15658     RedrawGadget(game_gadget[i]);
15659 }
15660
15661 static void HandleGameButtons(struct GadgetInfo *gi)
15662 {
15663   int id = gi->custom_id;
15664
15665   if (game_status != GAME_MODE_PLAYING)
15666     return;
15667
15668   switch (id)
15669   {
15670     case GAME_CTRL_ID_STOP:
15671       if (tape.playing)
15672         TapeStop();
15673       else
15674         RequestQuitGame(TRUE);
15675       break;
15676
15677     case GAME_CTRL_ID_PAUSE:
15678       if (options.network)
15679       {
15680 #if defined(NETWORK_AVALIABLE)
15681         if (tape.pausing)
15682           SendToServer_ContinuePlaying();
15683         else
15684           SendToServer_PausePlaying();
15685 #endif
15686       }
15687       else
15688         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15689       break;
15690
15691     case GAME_CTRL_ID_PLAY:
15692       if (tape.pausing)
15693       {
15694 #if defined(NETWORK_AVALIABLE)
15695         if (options.network)
15696           SendToServer_ContinuePlaying();
15697         else
15698 #endif
15699         {
15700           tape.pausing = FALSE;
15701           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15702         }
15703       }
15704       break;
15705
15706     case SOUND_CTRL_ID_MUSIC:
15707       if (setup.sound_music)
15708       { 
15709         setup.sound_music = FALSE;
15710         FadeMusic();
15711       }
15712       else if (audio.music_available)
15713       { 
15714         setup.sound = setup.sound_music = TRUE;
15715
15716         SetAudioMode(setup.sound);
15717
15718         PlayLevelMusic();
15719       }
15720       break;
15721
15722     case SOUND_CTRL_ID_LOOPS:
15723       if (setup.sound_loops)
15724         setup.sound_loops = FALSE;
15725       else if (audio.loops_available)
15726       {
15727         setup.sound = setup.sound_loops = TRUE;
15728         SetAudioMode(setup.sound);
15729       }
15730       break;
15731
15732     case SOUND_CTRL_ID_SIMPLE:
15733       if (setup.sound_simple)
15734         setup.sound_simple = FALSE;
15735       else if (audio.sound_available)
15736       {
15737         setup.sound = setup.sound_simple = TRUE;
15738         SetAudioMode(setup.sound);
15739       }
15740       break;
15741
15742     default:
15743       break;
15744   }
15745 }