rnd-20070409-1-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 = (local_player->LevelSolved ?
2017               local_player->LevelSolved_CountingTime :
2018               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2019               level.native_em_level->lev->time :
2020               level.time == 0 ? TimePlayed : TimeLeft);
2021   int score = (local_player->LevelSolved ?
2022                local_player->LevelSolved_CountingScore :
2023                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2024                level.native_em_level->lev->score :
2025                local_player->score);
2026   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2027               level.native_em_level->lev->required :
2028               local_player->gems_still_needed);
2029   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                      level.native_em_level->lev->required > 0 :
2031                      local_player->gems_still_needed > 0 ||
2032                      local_player->sokobanfields_still_needed > 0 ||
2033                      local_player->lights_still_needed > 0);
2034
2035   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2036   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2037
2038   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2039   for (i = 0; i < MAX_NUM_KEYS; i++)
2040     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2041   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2042   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2043
2044   if (game.centered_player_nr == -1)
2045   {
2046     for (i = 0; i < MAX_PLAYERS; i++)
2047     {
2048       for (k = 0; k < MAX_NUM_KEYS; k++)
2049       {
2050         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2051         {
2052           if (level.native_em_level->ply[i]->keys & (1 << k))
2053             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2054               get_key_element_from_nr(k);
2055         }
2056         else if (stored_player[i].key[k])
2057           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2058             get_key_element_from_nr(k);
2059       }
2060
2061       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2062         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2063           level.native_em_level->ply[i]->dynamite;
2064       else
2065         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2066           stored_player[i].inventory_size;
2067
2068       if (stored_player[i].num_white_keys > 0)
2069         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2070           EL_DC_KEY_WHITE;
2071
2072       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2073         stored_player[i].num_white_keys;
2074     }
2075   }
2076   else
2077   {
2078     int player_nr = game.centered_player_nr;
2079
2080     for (k = 0; k < MAX_NUM_KEYS; k++)
2081     {
2082       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2083       {
2084         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2085           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2086             get_key_element_from_nr(k);
2087       }
2088       else if (stored_player[player_nr].key[k])
2089         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2090           get_key_element_from_nr(k);
2091     }
2092
2093     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2094       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2095         level.native_em_level->ply[player_nr]->dynamite;
2096     else
2097       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2098         stored_player[player_nr].inventory_size;
2099
2100     if (stored_player[player_nr].num_white_keys > 0)
2101       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2102
2103     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2104       stored_player[player_nr].num_white_keys;
2105   }
2106
2107   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2108   {
2109     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2110       get_inventory_element_from_pos(local_player, i);
2111     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2112       get_inventory_element_from_pos(local_player, -i - 1);
2113   }
2114
2115   game_panel_controls[GAME_PANEL_SCORE].value = score;
2116
2117   game_panel_controls[GAME_PANEL_TIME].value = time;
2118
2119   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2120   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2121   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2122
2123   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2124     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2125      EL_EMPTY);
2126   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2127     local_player->shield_normal_time_left;
2128   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2129     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2130      EL_EMPTY);
2131   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2132     local_player->shield_deadly_time_left;
2133
2134   game_panel_controls[GAME_PANEL_EXIT].value =
2135     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2136
2137   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2138     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2139   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2140     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2141      EL_EMC_MAGIC_BALL_SWITCH);
2142
2143   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2144     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2145   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2146     game.light_time_left;
2147
2148   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2149     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2150   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2151     game.timegate_time_left;
2152
2153   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2154     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2155
2156   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2157     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2158   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2159     game.lenses_time_left;
2160
2161   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2162     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2163   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2164     game.magnify_time_left;
2165
2166   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2167     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2168      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2169      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2170      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2171      EL_BALLOON_SWITCH_NONE);
2172
2173   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2174     local_player->dynabomb_count;
2175   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2176     local_player->dynabomb_size;
2177   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2178     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2179
2180   game_panel_controls[GAME_PANEL_PENGUINS].value =
2181     local_player->friends_still_needed;
2182
2183   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2184     local_player->sokobanfields_still_needed;
2185   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2186     local_player->sokobanfields_still_needed;
2187
2188   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2189     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2190
2191   for (i = 0; i < NUM_BELTS; i++)
2192   {
2193     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2194       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2195        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2196     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2197       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2198   }
2199
2200   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2201     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2202   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2203     game.magic_wall_time_left;
2204
2205 #if USE_PLAYER_GRAVITY
2206   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2207     local_player->gravity;
2208 #else
2209   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2210 #endif
2211
2212   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2213     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2214       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2215        game.panel.element[i].id : EL_UNDEFINED);
2216
2217   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2218     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2219       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2220        element_info[game.panel.ce_score[i].id].collect_score : 0);
2221
2222   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2223     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2224       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2225        element_info[game.panel.ce_score_element[i].id].collect_score :
2226        EL_UNDEFINED);
2227
2228   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2229   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2230   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2231
2232   for (i = 0; game_panel_controls[i].nr != -1; i++)
2233   {
2234     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2235
2236     if (gpc->type == TYPE_ELEMENT)
2237     {
2238       int last_anim_random_frame = gfx.anim_random_frame;
2239       int element = gpc->value;
2240       int graphic = el2panelimg(element);
2241
2242       if (gpc->value != gpc->last_value)
2243       {
2244         gpc->gfx_frame = 0;
2245         gpc->gfx_random = INIT_GFX_RANDOM();
2246       }
2247       else
2248       {
2249         gpc->gfx_frame++;
2250
2251         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2252             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2253           gpc->gfx_random = INIT_GFX_RANDOM();
2254       }
2255
2256       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2257         gfx.anim_random_frame = gpc->gfx_random;
2258
2259       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2260         gpc->gfx_frame = element_info[element].collect_score;
2261
2262       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2263                                             gpc->gfx_frame);
2264
2265       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2266         gfx.anim_random_frame = last_anim_random_frame;
2267     }
2268   }
2269 }
2270
2271 void DisplayGameControlValues()
2272 {
2273   boolean redraw_panel = FALSE;
2274   int i;
2275
2276   for (i = 0; game_panel_controls[i].nr != -1; i++)
2277   {
2278     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2279
2280     if (PANEL_DEACTIVATED(gpc->pos))
2281       continue;
2282
2283     if (gpc->value == gpc->last_value &&
2284         gpc->frame == gpc->last_frame)
2285       continue;
2286
2287     redraw_panel = TRUE;
2288   }
2289
2290   if (!redraw_panel)
2291     return;
2292
2293   /* copy default game door content to main double buffer */
2294   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2295              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2296
2297   /* redraw game control buttons */
2298 #if 1
2299   RedrawGameButtons();
2300 #else
2301   UnmapGameButtons();
2302   MapGameButtons();
2303 #endif
2304
2305   game_status = GAME_MODE_PSEUDO_PANEL;
2306
2307 #if 1
2308   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2309 #else
2310   for (i = 0; game_panel_controls[i].nr != -1; i++)
2311 #endif
2312   {
2313 #if 1
2314     int nr = game_panel_order[i].nr;
2315     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2316 #else
2317     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2318     int nr = gpc->nr;
2319 #endif
2320     struct TextPosInfo *pos = gpc->pos;
2321     int type = gpc->type;
2322     int value = gpc->value;
2323     int frame = gpc->frame;
2324 #if 0
2325     int last_value = gpc->last_value;
2326     int last_frame = gpc->last_frame;
2327 #endif
2328     int size = pos->size;
2329     int font = pos->font;
2330     boolean draw_masked = pos->draw_masked;
2331     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2332
2333     if (PANEL_DEACTIVATED(pos))
2334       continue;
2335
2336 #if 0
2337     if (value == last_value && frame == last_frame)
2338       continue;
2339 #endif
2340
2341     gpc->last_value = value;
2342     gpc->last_frame = frame;
2343
2344 #if 0
2345     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2346 #endif
2347
2348     if (type == TYPE_INTEGER)
2349     {
2350       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2351           nr == GAME_PANEL_TIME)
2352       {
2353         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2354
2355         if (use_dynamic_size)           /* use dynamic number of digits */
2356         {
2357           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2358           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2359           int size2 = size1 + 1;
2360           int font1 = pos->font;
2361           int font2 = pos->font_alt;
2362
2363           size = (value < value_change ? size1 : size2);
2364           font = (value < value_change ? font1 : font2);
2365
2366 #if 0
2367           /* clear background if value just changed its size (dynamic digits) */
2368           if ((last_value < value_change) != (value < value_change))
2369           {
2370             int width1 = size1 * getFontWidth(font1);
2371             int width2 = size2 * getFontWidth(font2);
2372             int max_width = MAX(width1, width2);
2373             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2374
2375             pos->width = max_width;
2376
2377             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2378                                        max_width, max_height);
2379           }
2380 #endif
2381         }
2382       }
2383
2384 #if 1
2385       /* correct text size if "digits" is zero or less */
2386       if (size <= 0)
2387         size = strlen(int2str(value, size));
2388
2389       /* dynamically correct text alignment */
2390       pos->width = size * getFontWidth(font);
2391 #endif
2392
2393       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2394                   int2str(value, size), font, mask_mode);
2395     }
2396     else if (type == TYPE_ELEMENT)
2397     {
2398       int element, graphic;
2399       Bitmap *src_bitmap;
2400       int src_x, src_y;
2401       int width, height;
2402       int dst_x = PANEL_XPOS(pos);
2403       int dst_y = PANEL_YPOS(pos);
2404
2405 #if 1
2406       if (value != EL_UNDEFINED && value != EL_EMPTY)
2407       {
2408         element = value;
2409         graphic = el2panelimg(value);
2410
2411         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2412                               &src_x, &src_y);
2413
2414         width  = graphic_info[graphic].width  * size / TILESIZE;
2415         height = graphic_info[graphic].height * size / TILESIZE;
2416
2417         if (draw_masked)
2418         {
2419           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2420                         dst_x - src_x, dst_y - src_y);
2421           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2422                            dst_x, dst_y);
2423         }
2424         else
2425         {
2426           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2427                      dst_x, dst_y);
2428         }
2429       }
2430 #else
2431       if (value == EL_UNDEFINED || value == EL_EMPTY)
2432       {
2433         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2434         graphic = el2panelimg(element);
2435
2436         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2437         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2438         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2439       }
2440       else
2441       {
2442         element = value;
2443         graphic = el2panelimg(value);
2444
2445         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2446       }
2447
2448       width  = graphic_info[graphic].width  * size / TILESIZE;
2449       height = graphic_info[graphic].height * size / TILESIZE;
2450
2451       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2452 #endif
2453     }
2454     else if (type == TYPE_STRING)
2455     {
2456       boolean active = (value != 0);
2457       char *state_normal = "off";
2458       char *state_active = "on";
2459       char *state = (active ? state_active : state_normal);
2460       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2461                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2462                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2463                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2464
2465       if (nr == GAME_PANEL_GRAVITY_STATE)
2466       {
2467         int font1 = pos->font;          /* (used for normal state) */
2468         int font2 = pos->font_alt;      /* (used for active state) */
2469 #if 0
2470         int size1 = strlen(state_normal);
2471         int size2 = strlen(state_active);
2472         int width1 = size1 * getFontWidth(font1);
2473         int width2 = size2 * getFontWidth(font2);
2474         int max_width = MAX(width1, width2);
2475         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2476
2477         pos->width = max_width;
2478
2479         /* clear background for values that may have changed its size */
2480         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2481                                    max_width, max_height);
2482 #endif
2483
2484         font = (active ? font2 : font1);
2485       }
2486
2487       if (s != NULL)
2488       {
2489         char *s_cut;
2490
2491 #if 1
2492         if (size <= 0)
2493         {
2494           /* don't truncate output if "chars" is zero or less */
2495           size = strlen(s);
2496
2497           /* dynamically correct text alignment */
2498           pos->width = size * getFontWidth(font);
2499         }
2500 #endif
2501
2502         s_cut = getStringCopyN(s, size);
2503
2504         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2505                     s_cut, font, mask_mode);
2506
2507         free(s_cut);
2508       }
2509     }
2510
2511     redraw_mask |= REDRAW_DOOR_1;
2512   }
2513
2514   game_status = GAME_MODE_PLAYING;
2515 }
2516
2517 void DrawGameValue_Emeralds(int value)
2518 {
2519   struct TextPosInfo *pos = &game.panel.gems;
2520 #if 1
2521   int font_nr = pos->font;
2522 #else
2523   int font_nr = FONT_TEXT_2;
2524 #endif
2525   int font_width = getFontWidth(font_nr);
2526   int chars = pos->size;
2527
2528 #if 1
2529   return;       /* !!! USE NEW STUFF !!! */
2530 #endif
2531
2532   if (PANEL_DEACTIVATED(pos))
2533     return;
2534
2535   pos->width = chars * font_width;
2536
2537   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2538 }
2539
2540 void DrawGameValue_Dynamite(int value)
2541 {
2542   struct TextPosInfo *pos = &game.panel.inventory_count;
2543 #if 1
2544   int font_nr = pos->font;
2545 #else
2546   int font_nr = FONT_TEXT_2;
2547 #endif
2548   int font_width = getFontWidth(font_nr);
2549   int chars = pos->size;
2550
2551 #if 1
2552   return;       /* !!! USE NEW STUFF !!! */
2553 #endif
2554
2555   if (PANEL_DEACTIVATED(pos))
2556     return;
2557
2558   pos->width = chars * font_width;
2559
2560   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2561 }
2562
2563 void DrawGameValue_Score(int value)
2564 {
2565   struct TextPosInfo *pos = &game.panel.score;
2566 #if 1
2567   int font_nr = pos->font;
2568 #else
2569   int font_nr = FONT_TEXT_2;
2570 #endif
2571   int font_width = getFontWidth(font_nr);
2572   int chars = pos->size;
2573
2574 #if 1
2575   return;       /* !!! USE NEW STUFF !!! */
2576 #endif
2577
2578   if (PANEL_DEACTIVATED(pos))
2579     return;
2580
2581   pos->width = chars * font_width;
2582
2583   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2584 }
2585
2586 void DrawGameValue_Time(int value)
2587 {
2588   struct TextPosInfo *pos = &game.panel.time;
2589   static int last_value = -1;
2590   int chars1 = 3;
2591   int chars2 = 4;
2592   int chars = pos->size;
2593 #if 1
2594   int font1_nr = pos->font;
2595   int font2_nr = pos->font_alt;
2596 #else
2597   int font1_nr = FONT_TEXT_2;
2598   int font2_nr = FONT_TEXT_1;
2599 #endif
2600   int font_nr = font1_nr;
2601   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2602
2603 #if 1
2604   return;       /* !!! USE NEW STUFF !!! */
2605 #endif
2606
2607   if (PANEL_DEACTIVATED(pos))
2608     return;
2609
2610   if (use_dynamic_chars)                /* use dynamic number of chars */
2611   {
2612     chars   = (value < 1000 ? chars1   : chars2);
2613     font_nr = (value < 1000 ? font1_nr : font2_nr);
2614   }
2615
2616   /* clear background if value just changed its size (dynamic chars only) */
2617   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2618   {
2619     int width1 = chars1 * getFontWidth(font1_nr);
2620     int width2 = chars2 * getFontWidth(font2_nr);
2621     int max_width = MAX(width1, width2);
2622     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2623
2624     pos->width = max_width;
2625
2626     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2627                                max_width, max_height);
2628   }
2629
2630   pos->width = chars * getFontWidth(font_nr);
2631
2632   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2633
2634   last_value = value;
2635 }
2636
2637 void DrawGameValue_Level(int value)
2638 {
2639   struct TextPosInfo *pos = &game.panel.level_number;
2640   int chars1 = 2;
2641   int chars2 = 3;
2642   int chars = pos->size;
2643 #if 1
2644   int font1_nr = pos->font;
2645   int font2_nr = pos->font_alt;
2646 #else
2647   int font1_nr = FONT_TEXT_2;
2648   int font2_nr = FONT_TEXT_1;
2649 #endif
2650   int font_nr = font1_nr;
2651   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2652
2653 #if 1
2654   return;       /* !!! USE NEW STUFF !!! */
2655 #endif
2656
2657   if (PANEL_DEACTIVATED(pos))
2658     return;
2659
2660   if (use_dynamic_chars)                /* use dynamic number of chars */
2661   {
2662     chars   = (level_nr < 100 ? chars1   : chars2);
2663     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2664   }
2665
2666   pos->width = chars * getFontWidth(font_nr);
2667
2668   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2669 }
2670
2671 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2672 {
2673 #if 0
2674   struct TextPosInfo *pos = &game.panel.keys;
2675 #endif
2676 #if 0
2677   int base_key_graphic = EL_KEY_1;
2678 #endif
2679   int i;
2680
2681 #if 1
2682   return;       /* !!! USE NEW STUFF !!! */
2683 #endif
2684
2685 #if 0
2686   if (PANEL_DEACTIVATED(pos))
2687     return;
2688 #endif
2689
2690 #if 0
2691   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2692     base_key_graphic = EL_EM_KEY_1;
2693 #endif
2694
2695 #if 0
2696   pos->width = 4 * MINI_TILEX;
2697 #endif
2698
2699 #if 1
2700   for (i = 0; i < MAX_NUM_KEYS; i++)
2701 #else
2702   /* currently only 4 of 8 possible keys are displayed */
2703   for (i = 0; i < STD_NUM_KEYS; i++)
2704 #endif
2705   {
2706 #if 1
2707     struct TextPosInfo *pos = &game.panel.key[i];
2708 #endif
2709     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2710     int src_y = DOOR_GFX_PAGEY1 + 123;
2711 #if 1
2712     int dst_x = PANEL_XPOS(pos);
2713     int dst_y = PANEL_YPOS(pos);
2714 #else
2715     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2716     int dst_y = PANEL_YPOS(pos);
2717 #endif
2718
2719 #if 1
2720     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2721                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2722                    EL_KEY_1) + i;
2723     int graphic = el2edimg(element);
2724 #endif
2725
2726 #if 1
2727     if (PANEL_DEACTIVATED(pos))
2728       continue;
2729 #endif
2730
2731 #if 0
2732     /* masked blit with tiles from half-size scaled bitmap does not work yet
2733        (no mask bitmap created for these sizes after loading and scaling) --
2734        solution: load without creating mask, scale, then create final mask */
2735
2736     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2737                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2738
2739     if (key[i])
2740     {
2741 #if 0
2742       int graphic = el2edimg(base_key_graphic + i);
2743 #endif
2744       Bitmap *src_bitmap;
2745       int src_x, src_y;
2746
2747       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2748
2749       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2750                     dst_x - src_x, dst_y - src_y);
2751       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2752                        dst_x, dst_y);
2753     }
2754 #else
2755 #if 1
2756     if (key[i])
2757       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2758     else
2759       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2760                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2761 #else
2762     if (key[i])
2763       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2764     else
2765       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2766                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2767 #endif
2768 #endif
2769   }
2770 }
2771
2772 #else
2773
2774 void DrawGameValue_Emeralds(int value)
2775 {
2776   int font_nr = FONT_TEXT_2;
2777   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2778
2779   if (PANEL_DEACTIVATED(game.panel.gems))
2780     return;
2781
2782   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2783 }
2784
2785 void DrawGameValue_Dynamite(int value)
2786 {
2787   int font_nr = FONT_TEXT_2;
2788   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2789
2790   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2791     return;
2792
2793   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2794 }
2795
2796 void DrawGameValue_Score(int value)
2797 {
2798   int font_nr = FONT_TEXT_2;
2799   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2800
2801   if (PANEL_DEACTIVATED(game.panel.score))
2802     return;
2803
2804   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2805 }
2806
2807 void DrawGameValue_Time(int value)
2808 {
2809   int font1_nr = FONT_TEXT_2;
2810 #if 1
2811   int font2_nr = FONT_TEXT_1;
2812 #else
2813   int font2_nr = FONT_LEVEL_NUMBER;
2814 #endif
2815   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2816   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2817
2818   if (PANEL_DEACTIVATED(game.panel.time))
2819     return;
2820
2821   /* clear background if value just changed its size */
2822   if (value == 999 || value == 1000)
2823     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2824
2825   if (value < 1000)
2826     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2827   else
2828     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2829 }
2830
2831 void DrawGameValue_Level(int value)
2832 {
2833   int font1_nr = FONT_TEXT_2;
2834 #if 1
2835   int font2_nr = FONT_TEXT_1;
2836 #else
2837   int font2_nr = FONT_LEVEL_NUMBER;
2838 #endif
2839
2840   if (PANEL_DEACTIVATED(game.panel.level))
2841     return;
2842
2843   if (level_nr < 100)
2844     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2845   else
2846     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2847 }
2848
2849 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2850 {
2851   int base_key_graphic = EL_KEY_1;
2852   int i;
2853
2854   if (PANEL_DEACTIVATED(game.panel.keys))
2855     return;
2856
2857   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2858     base_key_graphic = EL_EM_KEY_1;
2859
2860   /* currently only 4 of 8 possible keys are displayed */
2861   for (i = 0; i < STD_NUM_KEYS; i++)
2862   {
2863     int x = XX_KEYS + i * MINI_TILEX;
2864     int y = YY_KEYS;
2865
2866     if (key[i])
2867       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2868     else
2869       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2870                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2871   }
2872 }
2873
2874 #endif
2875
2876 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2877                        int key_bits)
2878 {
2879   int key[MAX_NUM_KEYS];
2880   int i;
2881
2882   /* prevent EM engine from updating time/score values parallel to GameWon() */
2883   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2884       local_player->LevelSolved)
2885     return;
2886
2887   for (i = 0; i < MAX_NUM_KEYS; i++)
2888     key[i] = key_bits & (1 << i);
2889
2890   DrawGameValue_Level(level_nr);
2891
2892   DrawGameValue_Emeralds(emeralds);
2893   DrawGameValue_Dynamite(dynamite);
2894   DrawGameValue_Score(score);
2895   DrawGameValue_Time(time);
2896
2897   DrawGameValue_Keys(key);
2898 }
2899
2900 void UpdateGameDoorValues()
2901 {
2902   UpdateGameControlValues();
2903 }
2904
2905 void DrawGameDoorValues()
2906 {
2907   DisplayGameControlValues();
2908 }
2909
2910 void DrawGameDoorValues_OLD()
2911 {
2912   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2913   int dynamite_value = 0;
2914   int score_value = (local_player->LevelSolved ? local_player->score_final :
2915                      local_player->score);
2916   int gems_value = local_player->gems_still_needed;
2917   int key_bits = 0;
2918   int i, j;
2919
2920   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2921   {
2922     DrawGameDoorValues_EM();
2923
2924     return;
2925   }
2926
2927   if (game.centered_player_nr == -1)
2928   {
2929     for (i = 0; i < MAX_PLAYERS; i++)
2930     {
2931       for (j = 0; j < MAX_NUM_KEYS; j++)
2932         if (stored_player[i].key[j])
2933           key_bits |= (1 << j);
2934
2935       dynamite_value += stored_player[i].inventory_size;
2936     }
2937   }
2938   else
2939   {
2940     int player_nr = game.centered_player_nr;
2941
2942     for (i = 0; i < MAX_NUM_KEYS; i++)
2943       if (stored_player[player_nr].key[i])
2944         key_bits |= (1 << i);
2945
2946     dynamite_value = stored_player[player_nr].inventory_size;
2947   }
2948
2949   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2950                     key_bits);
2951 }
2952
2953
2954 /*
2955   =============================================================================
2956   InitGameEngine()
2957   -----------------------------------------------------------------------------
2958   initialize game engine due to level / tape version number
2959   =============================================================================
2960 */
2961
2962 static void InitGameEngine()
2963 {
2964   int i, j, k, l, x, y;
2965
2966   /* set game engine from tape file when re-playing, else from level file */
2967   game.engine_version = (tape.playing ? tape.engine_version :
2968                          level.game_version);
2969
2970   /* ---------------------------------------------------------------------- */
2971   /* set flags for bugs and changes according to active game engine version */
2972   /* ---------------------------------------------------------------------- */
2973
2974   /*
2975     Summary of bugfix/change:
2976     Fixed handling for custom elements that change when pushed by the player.
2977
2978     Fixed/changed in version:
2979     3.1.0
2980
2981     Description:
2982     Before 3.1.0, custom elements that "change when pushing" changed directly
2983     after the player started pushing them (until then handled in "DigField()").
2984     Since 3.1.0, these custom elements are not changed until the "pushing"
2985     move of the element is finished (now handled in "ContinueMoving()").
2986
2987     Affected levels/tapes:
2988     The first condition is generally needed for all levels/tapes before version
2989     3.1.0, which might use the old behaviour before it was changed; known tapes
2990     that are affected are some tapes from the level set "Walpurgis Gardens" by
2991     Jamie Cullen.
2992     The second condition is an exception from the above case and is needed for
2993     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2994     above (including some development versions of 3.1.0), but before it was
2995     known that this change would break tapes like the above and was fixed in
2996     3.1.1, so that the changed behaviour was active although the engine version
2997     while recording maybe was before 3.1.0. There is at least one tape that is
2998     affected by this exception, which is the tape for the one-level set "Bug
2999     Machine" by Juergen Bonhagen.
3000   */
3001
3002   game.use_change_when_pushing_bug =
3003     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3004      !(tape.playing &&
3005        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3006        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3007
3008   /*
3009     Summary of bugfix/change:
3010     Fixed handling for blocking the field the player leaves when moving.
3011
3012     Fixed/changed in version:
3013     3.1.1
3014
3015     Description:
3016     Before 3.1.1, when "block last field when moving" was enabled, the field
3017     the player is leaving when moving was blocked for the time of the move,
3018     and was directly unblocked afterwards. This resulted in the last field
3019     being blocked for exactly one less than the number of frames of one player
3020     move. Additionally, even when blocking was disabled, the last field was
3021     blocked for exactly one frame.
3022     Since 3.1.1, due to changes in player movement handling, the last field
3023     is not blocked at all when blocking is disabled. When blocking is enabled,
3024     the last field is blocked for exactly the number of frames of one player
3025     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3026     last field is blocked for exactly one more than the number of frames of
3027     one player move.
3028
3029     Affected levels/tapes:
3030     (!!! yet to be determined -- probably many !!!)
3031   */
3032
3033   game.use_block_last_field_bug =
3034     (game.engine_version < VERSION_IDENT(3,1,1,0));
3035
3036   /*
3037     Summary of bugfix/change:
3038     Changed behaviour of CE changes with multiple changes per single frame.
3039
3040     Fixed/changed in version:
3041     3.2.0-6
3042
3043     Description:
3044     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3045     This resulted in race conditions where CEs seem to behave strange in some
3046     situations (where triggered CE changes were just skipped because there was
3047     already a CE change on that tile in the playfield in that engine frame).
3048     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3049     (The number of changes per frame must be limited in any case, because else
3050     it is easily possible to define CE changes that would result in an infinite
3051     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3052     should be set large enough so that it would only be reached in cases where
3053     the corresponding CE change conditions run into a loop. Therefore, it seems
3054     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3055     maximal number of change pages for custom elements.)
3056
3057     Affected levels/tapes:
3058     Probably many.
3059   */
3060
3061 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3062   game.max_num_changes_per_frame = 1;
3063 #else
3064   game.max_num_changes_per_frame =
3065     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3066 #endif
3067
3068   /* ---------------------------------------------------------------------- */
3069
3070   /* default scan direction: scan playfield from top/left to bottom/right */
3071   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3072
3073   /* dynamically adjust element properties according to game engine version */
3074   InitElementPropertiesEngine(game.engine_version);
3075
3076 #if 0
3077   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3078   printf("          tape version == %06d [%s] [file: %06d]\n",
3079          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3080          tape.file_version);
3081   printf("       => game.engine_version == %06d\n", game.engine_version);
3082 #endif
3083
3084   /* ---------- initialize player's initial move delay --------------------- */
3085
3086   /* dynamically adjust player properties according to level information */
3087   for (i = 0; i < MAX_PLAYERS; i++)
3088     game.initial_move_delay_value[i] =
3089       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3090
3091   /* dynamically adjust player properties according to game engine version */
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay[i] =
3094       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3095        game.initial_move_delay_value[i] : 0);
3096
3097   /* ---------- initialize player's initial push delay --------------------- */
3098
3099   /* dynamically adjust player properties according to game engine version */
3100   game.initial_push_delay_value =
3101     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3102
3103   /* ---------- initialize changing elements ------------------------------- */
3104
3105   /* initialize changing elements information */
3106   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3107   {
3108     struct ElementInfo *ei = &element_info[i];
3109
3110     /* this pointer might have been changed in the level editor */
3111     ei->change = &ei->change_page[0];
3112
3113     if (!IS_CUSTOM_ELEMENT(i))
3114     {
3115       ei->change->target_element = EL_EMPTY_SPACE;
3116       ei->change->delay_fixed = 0;
3117       ei->change->delay_random = 0;
3118       ei->change->delay_frames = 1;
3119     }
3120
3121     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3122     {
3123       ei->has_change_event[j] = FALSE;
3124
3125       ei->event_page_nr[j] = 0;
3126       ei->event_page[j] = &ei->change_page[0];
3127     }
3128   }
3129
3130   /* add changing elements from pre-defined list */
3131   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3132   {
3133     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3134     struct ElementInfo *ei = &element_info[ch_delay->element];
3135
3136     ei->change->target_element       = ch_delay->target_element;
3137     ei->change->delay_fixed          = ch_delay->change_delay;
3138
3139     ei->change->pre_change_function  = ch_delay->pre_change_function;
3140     ei->change->change_function      = ch_delay->change_function;
3141     ei->change->post_change_function = ch_delay->post_change_function;
3142
3143     ei->change->can_change = TRUE;
3144     ei->change->can_change_or_has_action = TRUE;
3145
3146     ei->has_change_event[CE_DELAY] = TRUE;
3147
3148     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3149     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3150   }
3151
3152   /* ---------- initialize internal run-time variables ------------- */
3153
3154   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3155   {
3156     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3157
3158     for (j = 0; j < ei->num_change_pages; j++)
3159     {
3160       ei->change_page[j].can_change_or_has_action =
3161         (ei->change_page[j].can_change |
3162          ei->change_page[j].has_action);
3163     }
3164   }
3165
3166   /* add change events from custom element configuration */
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       if (!ei->change_page[j].can_change_or_has_action)
3174         continue;
3175
3176       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3177       {
3178         /* only add event page for the first page found with this event */
3179         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3180         {
3181           ei->has_change_event[k] = TRUE;
3182
3183           ei->event_page_nr[k] = j;
3184           ei->event_page[k] = &ei->change_page[j];
3185         }
3186       }
3187     }
3188   }
3189
3190   /* ---------- initialize run-time trigger player and element ------------- */
3191
3192   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3193   {
3194     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3195
3196     for (j = 0; j < ei->num_change_pages; j++)
3197     {
3198       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3199       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3200       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3201       ei->change_page[j].actual_trigger_ce_value = 0;
3202       ei->change_page[j].actual_trigger_ce_score = 0;
3203     }
3204   }
3205
3206   /* ---------- initialize trigger events ---------------------------------- */
3207
3208   /* initialize trigger events information */
3209   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3210     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3211       trigger_events[i][j] = FALSE;
3212
3213   /* add trigger events from element change event properties */
3214   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3215   {
3216     struct ElementInfo *ei = &element_info[i];
3217
3218     for (j = 0; j < ei->num_change_pages; j++)
3219     {
3220       if (!ei->change_page[j].can_change_or_has_action)
3221         continue;
3222
3223       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3224       {
3225         int trigger_element = ei->change_page[j].trigger_element;
3226
3227         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3228         {
3229           if (ei->change_page[j].has_event[k])
3230           {
3231             if (IS_GROUP_ELEMENT(trigger_element))
3232             {
3233               struct ElementGroupInfo *group =
3234                 element_info[trigger_element].group;
3235
3236               for (l = 0; l < group->num_elements_resolved; l++)
3237                 trigger_events[group->element_resolved[l]][k] = TRUE;
3238             }
3239             else if (trigger_element == EL_ANY_ELEMENT)
3240               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3241                 trigger_events[l][k] = TRUE;
3242             else
3243               trigger_events[trigger_element][k] = TRUE;
3244           }
3245         }
3246       }
3247     }
3248   }
3249
3250   /* ---------- initialize push delay -------------------------------------- */
3251
3252   /* initialize push delay values to default */
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254   {
3255     if (!IS_CUSTOM_ELEMENT(i))
3256     {
3257       /* set default push delay values (corrected since version 3.0.7-1) */
3258       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3259       {
3260         element_info[i].push_delay_fixed = 2;
3261         element_info[i].push_delay_random = 8;
3262       }
3263       else
3264       {
3265         element_info[i].push_delay_fixed = 8;
3266         element_info[i].push_delay_random = 8;
3267       }
3268     }
3269   }
3270
3271   /* set push delay value for certain elements from pre-defined list */
3272   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3273   {
3274     int e = push_delay_list[i].element;
3275
3276     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3277     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3278   }
3279
3280   /* set push delay value for Supaplex elements for newer engine versions */
3281   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3282   {
3283     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3284     {
3285       if (IS_SP_ELEMENT(i))
3286       {
3287         /* set SP push delay to just enough to push under a falling zonk */
3288         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3289
3290         element_info[i].push_delay_fixed  = delay;
3291         element_info[i].push_delay_random = 0;
3292       }
3293     }
3294   }
3295
3296   /* ---------- initialize move stepsize ----------------------------------- */
3297
3298   /* initialize move stepsize values to default */
3299   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3300     if (!IS_CUSTOM_ELEMENT(i))
3301       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3302
3303   /* set move stepsize value for certain elements from pre-defined list */
3304   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3305   {
3306     int e = move_stepsize_list[i].element;
3307
3308     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3309   }
3310
3311   /* ---------- initialize collect score ----------------------------------- */
3312
3313   /* initialize collect score values for custom elements from initial value */
3314   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3315     if (IS_CUSTOM_ELEMENT(i))
3316       element_info[i].collect_score = element_info[i].collect_score_initial;
3317
3318   /* ---------- initialize collect count ----------------------------------- */
3319
3320   /* initialize collect count values for non-custom elements */
3321   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3322     if (!IS_CUSTOM_ELEMENT(i))
3323       element_info[i].collect_count_initial = 0;
3324
3325   /* add collect count values for all elements from pre-defined list */
3326   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3327     element_info[collect_count_list[i].element].collect_count_initial =
3328       collect_count_list[i].count;
3329
3330   /* ---------- initialize access direction -------------------------------- */
3331
3332   /* initialize access direction values to default (access from every side) */
3333   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3334     if (!IS_CUSTOM_ELEMENT(i))
3335       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3336
3337   /* set access direction value for certain elements from pre-defined list */
3338   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3339     element_info[access_direction_list[i].element].access_direction =
3340       access_direction_list[i].direction;
3341
3342   /* ---------- initialize explosion content ------------------------------- */
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344   {
3345     if (IS_CUSTOM_ELEMENT(i))
3346       continue;
3347
3348     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3349     {
3350       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3351
3352       element_info[i].content.e[x][y] =
3353         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3354          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3355          i == EL_PLAYER_3 ? EL_EMERALD :
3356          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3357          i == EL_MOLE ? EL_EMERALD_RED :
3358          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3359          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3360          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3361          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3362          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3363          i == EL_WALL_EMERALD ? EL_EMERALD :
3364          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3365          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3366          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3367          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3368          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3369          i == EL_WALL_PEARL ? EL_PEARL :
3370          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3371          EL_EMPTY);
3372     }
3373   }
3374
3375   /* ---------- initialize recursion detection ------------------------------ */
3376   recursion_loop_depth = 0;
3377   recursion_loop_detected = FALSE;
3378   recursion_loop_element = EL_UNDEFINED;
3379
3380   /* ---------- initialize graphics engine ---------------------------------- */
3381   game.scroll_delay_value =
3382     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3383      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3384   game.scroll_delay_value =
3385     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3386 }
3387
3388 int get_num_special_action(int element, int action_first, int action_last)
3389 {
3390   int num_special_action = 0;
3391   int i, j;
3392
3393   for (i = action_first; i <= action_last; i++)
3394   {
3395     boolean found = FALSE;
3396
3397     for (j = 0; j < NUM_DIRECTIONS; j++)
3398       if (el_act_dir2img(element, i, j) !=
3399           el_act_dir2img(element, ACTION_DEFAULT, j))
3400         found = TRUE;
3401
3402     if (found)
3403       num_special_action++;
3404     else
3405       break;
3406   }
3407
3408   return num_special_action;
3409 }
3410
3411
3412 /*
3413   =============================================================================
3414   InitGame()
3415   -----------------------------------------------------------------------------
3416   initialize and start new game
3417   =============================================================================
3418 */
3419
3420 void InitGame()
3421 {
3422   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3423   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3424   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3425 #if 0
3426   boolean do_fading = (game_status == GAME_MODE_MAIN);
3427 #endif
3428   int i, j, x, y;
3429
3430   game_status = GAME_MODE_PLAYING;
3431
3432   InitGameEngine();
3433   InitGameControlValues();
3434
3435   /* don't play tapes over network */
3436   network_playing = (options.network && !tape.playing);
3437
3438   for (i = 0; i < MAX_PLAYERS; i++)
3439   {
3440     struct PlayerInfo *player = &stored_player[i];
3441
3442     player->index_nr = i;
3443     player->index_bit = (1 << i);
3444     player->element_nr = EL_PLAYER_1 + i;
3445
3446     player->present = FALSE;
3447     player->active = FALSE;
3448     player->killed = FALSE;
3449
3450     player->action = 0;
3451     player->effective_action = 0;
3452     player->programmed_action = 0;
3453
3454     player->score = 0;
3455     player->score_final = 0;
3456
3457     player->gems_still_needed = level.gems_needed;
3458     player->sokobanfields_still_needed = 0;
3459     player->lights_still_needed = 0;
3460     player->friends_still_needed = 0;
3461
3462     for (j = 0; j < MAX_NUM_KEYS; j++)
3463       player->key[j] = FALSE;
3464
3465     player->num_white_keys = 0;
3466
3467     player->dynabomb_count = 0;
3468     player->dynabomb_size = 1;
3469     player->dynabombs_left = 0;
3470     player->dynabomb_xl = FALSE;
3471
3472     player->MovDir = MV_NONE;
3473     player->MovPos = 0;
3474     player->GfxPos = 0;
3475     player->GfxDir = MV_NONE;
3476     player->GfxAction = ACTION_DEFAULT;
3477     player->Frame = 0;
3478     player->StepFrame = 0;
3479
3480     player->use_murphy = FALSE;
3481     player->artwork_element =
3482       (level.use_artwork_element[i] ? level.artwork_element[i] :
3483        player->element_nr);
3484
3485     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3486     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3487
3488     player->gravity = level.initial_player_gravity[i];
3489
3490     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3491
3492     player->actual_frame_counter = 0;
3493
3494     player->step_counter = 0;
3495
3496     player->last_move_dir = MV_NONE;
3497
3498     player->is_active = FALSE;
3499
3500     player->is_waiting = FALSE;
3501     player->is_moving = FALSE;
3502     player->is_auto_moving = FALSE;
3503     player->is_digging = FALSE;
3504     player->is_snapping = FALSE;
3505     player->is_collecting = FALSE;
3506     player->is_pushing = FALSE;
3507     player->is_switching = FALSE;
3508     player->is_dropping = FALSE;
3509     player->is_dropping_pressed = FALSE;
3510
3511     player->is_bored = FALSE;
3512     player->is_sleeping = FALSE;
3513
3514     player->frame_counter_bored = -1;
3515     player->frame_counter_sleeping = -1;
3516
3517     player->anim_delay_counter = 0;
3518     player->post_delay_counter = 0;
3519
3520     player->dir_waiting = MV_NONE;
3521     player->action_waiting = ACTION_DEFAULT;
3522     player->last_action_waiting = ACTION_DEFAULT;
3523     player->special_action_bored = ACTION_DEFAULT;
3524     player->special_action_sleeping = ACTION_DEFAULT;
3525
3526     player->switch_x = -1;
3527     player->switch_y = -1;
3528
3529     player->drop_x = -1;
3530     player->drop_y = -1;
3531
3532     player->show_envelope = 0;
3533
3534     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3535
3536     player->push_delay       = -1;      /* initialized when pushing starts */
3537     player->push_delay_value = game.initial_push_delay_value;
3538
3539     player->drop_delay = 0;
3540     player->drop_pressed_delay = 0;
3541
3542     player->last_jx = -1;
3543     player->last_jy = -1;
3544     player->jx = -1;
3545     player->jy = -1;
3546
3547     player->shield_normal_time_left = 0;
3548     player->shield_deadly_time_left = 0;
3549
3550     player->inventory_infinite_element = EL_UNDEFINED;
3551     player->inventory_size = 0;
3552
3553     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3554     SnapField(player, 0, 0);
3555
3556     player->LevelSolved = FALSE;
3557     player->GameOver = FALSE;
3558
3559     player->LevelSolved_GameWon = FALSE;
3560     player->LevelSolved_GameEnd = FALSE;
3561     player->LevelSolved_PanelOff = FALSE;
3562     player->LevelSolved_SaveTape = FALSE;
3563     player->LevelSolved_SaveScore = FALSE;
3564     player->LevelSolved_CountingTime = 0;
3565     player->LevelSolved_CountingScore = 0;
3566   }
3567
3568   network_player_action_received = FALSE;
3569
3570 #if defined(NETWORK_AVALIABLE)
3571   /* initial null action */
3572   if (network_playing)
3573     SendToServer_MovePlayer(MV_NONE);
3574 #endif
3575
3576   ZX = ZY = -1;
3577   ExitX = ExitY = -1;
3578
3579   FrameCounter = 0;
3580   TimeFrames = 0;
3581   TimePlayed = 0;
3582   TimeLeft = level.time;
3583   TapeTime = 0;
3584
3585   ScreenMovDir = MV_NONE;
3586   ScreenMovPos = 0;
3587   ScreenGfxPos = 0;
3588
3589   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3590
3591   AllPlayersGone = FALSE;
3592
3593   game.yamyam_content_nr = 0;
3594   game.robot_wheel_active = FALSE;
3595   game.magic_wall_active = FALSE;
3596   game.magic_wall_time_left = 0;
3597   game.light_time_left = 0;
3598   game.timegate_time_left = 0;
3599   game.switchgate_pos = 0;
3600   game.wind_direction = level.wind_direction_initial;
3601
3602 #if !USE_PLAYER_GRAVITY
3603   game.gravity = FALSE;
3604   game.explosions_delayed = TRUE;
3605 #endif
3606
3607   game.lenses_time_left = 0;
3608   game.magnify_time_left = 0;
3609
3610   game.ball_state = level.ball_state_initial;
3611   game.ball_content_nr = 0;
3612
3613   game.envelope_active = FALSE;
3614
3615   /* set focus to local player for network games, else to all players */
3616   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3617   game.centered_player_nr_next = game.centered_player_nr;
3618   game.set_centered_player = FALSE;
3619
3620   if (network_playing && tape.recording)
3621   {
3622     /* store client dependent player focus when recording network games */
3623     tape.centered_player_nr_next = game.centered_player_nr_next;
3624     tape.set_centered_player = TRUE;
3625   }
3626
3627   for (i = 0; i < NUM_BELTS; i++)
3628   {
3629     game.belt_dir[i] = MV_NONE;
3630     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3631   }
3632
3633   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3634     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3635
3636   SCAN_PLAYFIELD(x, y)
3637   {
3638     Feld[x][y] = level.field[x][y];
3639     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3640     ChangeDelay[x][y] = 0;
3641     ChangePage[x][y] = -1;
3642 #if USE_NEW_CUSTOM_VALUE
3643     CustomValue[x][y] = 0;              /* initialized in InitField() */
3644 #endif
3645     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3646     AmoebaNr[x][y] = 0;
3647     WasJustMoving[x][y] = 0;
3648     WasJustFalling[x][y] = 0;
3649     CheckCollision[x][y] = 0;
3650     CheckImpact[x][y] = 0;
3651     Stop[x][y] = FALSE;
3652     Pushed[x][y] = FALSE;
3653
3654     ChangeCount[x][y] = 0;
3655     ChangeEvent[x][y] = -1;
3656
3657     ExplodePhase[x][y] = 0;
3658     ExplodeDelay[x][y] = 0;
3659     ExplodeField[x][y] = EX_TYPE_NONE;
3660
3661     RunnerVisit[x][y] = 0;
3662     PlayerVisit[x][y] = 0;
3663
3664     GfxFrame[x][y] = 0;
3665     GfxRandom[x][y] = INIT_GFX_RANDOM();
3666     GfxElement[x][y] = EL_UNDEFINED;
3667     GfxAction[x][y] = ACTION_DEFAULT;
3668     GfxDir[x][y] = MV_NONE;
3669   }
3670
3671   SCAN_PLAYFIELD(x, y)
3672   {
3673     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3674       emulate_bd = FALSE;
3675     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3676       emulate_sb = FALSE;
3677     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3678       emulate_sp = FALSE;
3679
3680     InitField(x, y, TRUE);
3681
3682     ResetGfxAnimation(x, y);
3683   }
3684
3685   InitBeltMovement();
3686
3687   for (i = 0; i < MAX_PLAYERS; i++)
3688   {
3689     struct PlayerInfo *player = &stored_player[i];
3690
3691     /* set number of special actions for bored and sleeping animation */
3692     player->num_special_action_bored =
3693       get_num_special_action(player->artwork_element,
3694                              ACTION_BORING_1, ACTION_BORING_LAST);
3695     player->num_special_action_sleeping =
3696       get_num_special_action(player->artwork_element,
3697                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3698   }
3699
3700   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3701                     emulate_sb ? EMU_SOKOBAN :
3702                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3703
3704 #if USE_NEW_ALL_SLIPPERY
3705   /* initialize type of slippery elements */
3706   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3707   {
3708     if (!IS_CUSTOM_ELEMENT(i))
3709     {
3710       /* default: elements slip down either to the left or right randomly */
3711       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3712
3713       /* SP style elements prefer to slip down on the left side */
3714       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3715         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3716
3717       /* BD style elements prefer to slip down on the left side */
3718       if (game.emulation == EMU_BOULDERDASH)
3719         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3720     }
3721   }
3722 #endif
3723
3724   /* initialize explosion and ignition delay */
3725   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3726   {
3727     if (!IS_CUSTOM_ELEMENT(i))
3728     {
3729       int num_phase = 8;
3730       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3731                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3732                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3733       int last_phase = (num_phase + 1) * delay;
3734       int half_phase = (num_phase / 2) * delay;
3735
3736       element_info[i].explosion_delay = last_phase - 1;
3737       element_info[i].ignition_delay = half_phase;
3738
3739       if (i == EL_BLACK_ORB)
3740         element_info[i].ignition_delay = 1;
3741     }
3742
3743 #if 0
3744     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3745       element_info[i].explosion_delay = 1;
3746
3747     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3748       element_info[i].ignition_delay = 1;
3749 #endif
3750   }
3751
3752   /* correct non-moving belts to start moving left */
3753   for (i = 0; i < NUM_BELTS; i++)
3754     if (game.belt_dir[i] == MV_NONE)
3755       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3756
3757   /* check if any connected player was not found in playfield */
3758   for (i = 0; i < MAX_PLAYERS; i++)
3759   {
3760     struct PlayerInfo *player = &stored_player[i];
3761
3762     if (player->connected && !player->present)
3763     {
3764       for (j = 0; j < MAX_PLAYERS; j++)
3765       {
3766         struct PlayerInfo *some_player = &stored_player[j];
3767         int jx = some_player->jx, jy = some_player->jy;
3768
3769         /* assign first free player found that is present in the playfield */
3770         if (some_player->present && !some_player->connected)
3771         {
3772           player->present = TRUE;
3773           player->active = TRUE;
3774
3775           some_player->present = FALSE;
3776           some_player->active = FALSE;
3777
3778           player->artwork_element = some_player->artwork_element;
3779
3780           player->block_last_field       = some_player->block_last_field;
3781           player->block_delay_adjustment = some_player->block_delay_adjustment;
3782
3783           StorePlayer[jx][jy] = player->element_nr;
3784           player->jx = player->last_jx = jx;
3785           player->jy = player->last_jy = jy;
3786
3787           break;
3788         }
3789       }
3790     }
3791   }
3792
3793   if (tape.playing)
3794   {
3795     /* when playing a tape, eliminate all players who do not participate */
3796
3797     for (i = 0; i < MAX_PLAYERS; i++)
3798     {
3799       if (stored_player[i].active && !tape.player_participates[i])
3800       {
3801         struct PlayerInfo *player = &stored_player[i];
3802         int jx = player->jx, jy = player->jy;
3803
3804         player->active = FALSE;
3805         StorePlayer[jx][jy] = 0;
3806         Feld[jx][jy] = EL_EMPTY;
3807       }
3808     }
3809   }
3810   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3811   {
3812     /* when in single player mode, eliminate all but the first active player */
3813
3814     for (i = 0; i < MAX_PLAYERS; i++)
3815     {
3816       if (stored_player[i].active)
3817       {
3818         for (j = i + 1; j < MAX_PLAYERS; j++)
3819         {
3820           if (stored_player[j].active)
3821           {
3822             struct PlayerInfo *player = &stored_player[j];
3823             int jx = player->jx, jy = player->jy;
3824
3825             player->active = FALSE;
3826             player->present = FALSE;
3827
3828             StorePlayer[jx][jy] = 0;
3829             Feld[jx][jy] = EL_EMPTY;
3830           }
3831         }
3832       }
3833     }
3834   }
3835
3836   /* when recording the game, store which players take part in the game */
3837   if (tape.recording)
3838   {
3839     for (i = 0; i < MAX_PLAYERS; i++)
3840       if (stored_player[i].active)
3841         tape.player_participates[i] = TRUE;
3842   }
3843
3844   if (options.debug)
3845   {
3846     for (i = 0; i < MAX_PLAYERS; i++)
3847     {
3848       struct PlayerInfo *player = &stored_player[i];
3849
3850       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3851              i+1,
3852              player->present,
3853              player->connected,
3854              player->active);
3855       if (local_player == player)
3856         printf("Player  %d is local player.\n", i+1);
3857     }
3858   }
3859
3860   if (BorderElement == EL_EMPTY)
3861   {
3862     SBX_Left = 0;
3863     SBX_Right = lev_fieldx - SCR_FIELDX;
3864     SBY_Upper = 0;
3865     SBY_Lower = lev_fieldy - SCR_FIELDY;
3866   }
3867   else
3868   {
3869     SBX_Left = -1;
3870     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3871     SBY_Upper = -1;
3872     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3873   }
3874
3875   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3876     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3877
3878   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3879     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3880
3881   /* if local player not found, look for custom element that might create
3882      the player (make some assumptions about the right custom element) */
3883   if (!local_player->present)
3884   {
3885     int start_x = 0, start_y = 0;
3886     int found_rating = 0;
3887     int found_element = EL_UNDEFINED;
3888     int player_nr = local_player->index_nr;
3889
3890     SCAN_PLAYFIELD(x, y)
3891     {
3892       int element = Feld[x][y];
3893       int content;
3894       int xx, yy;
3895       boolean is_player;
3896
3897       if (level.use_start_element[player_nr] &&
3898           level.start_element[player_nr] == element &&
3899           found_rating < 4)
3900       {
3901         start_x = x;
3902         start_y = y;
3903
3904         found_rating = 4;
3905         found_element = element;
3906       }
3907
3908       if (!IS_CUSTOM_ELEMENT(element))
3909         continue;
3910
3911       if (CAN_CHANGE(element))
3912       {
3913         for (i = 0; i < element_info[element].num_change_pages; i++)
3914         {
3915           /* check for player created from custom element as single target */
3916           content = element_info[element].change_page[i].target_element;
3917           is_player = ELEM_IS_PLAYER(content);
3918
3919           if (is_player && (found_rating < 3 ||
3920                             (found_rating == 3 && element < found_element)))
3921           {
3922             start_x = x;
3923             start_y = y;
3924
3925             found_rating = 3;
3926             found_element = element;
3927           }
3928         }
3929       }
3930
3931       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3932       {
3933         /* check for player created from custom element as explosion content */
3934         content = element_info[element].content.e[xx][yy];
3935         is_player = ELEM_IS_PLAYER(content);
3936
3937         if (is_player && (found_rating < 2 ||
3938                           (found_rating == 2 && element < found_element)))
3939         {
3940           start_x = x + xx - 1;
3941           start_y = y + yy - 1;
3942
3943           found_rating = 2;
3944           found_element = element;
3945         }
3946
3947         if (!CAN_CHANGE(element))
3948           continue;
3949
3950         for (i = 0; i < element_info[element].num_change_pages; i++)
3951         {
3952           /* check for player created from custom element as extended target */
3953           content =
3954             element_info[element].change_page[i].target_content.e[xx][yy];
3955
3956           is_player = ELEM_IS_PLAYER(content);
3957
3958           if (is_player && (found_rating < 1 ||
3959                             (found_rating == 1 && element < found_element)))
3960           {
3961             start_x = x + xx - 1;
3962             start_y = y + yy - 1;
3963
3964             found_rating = 1;
3965             found_element = element;
3966           }
3967         }
3968       }
3969     }
3970
3971     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3972                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3973                 start_x - MIDPOSX);
3974
3975     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3976                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3977                 start_y - MIDPOSY);
3978   }
3979   else
3980   {
3981     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3982                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3983                 local_player->jx - MIDPOSX);
3984
3985     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3986                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3987                 local_player->jy - MIDPOSY);
3988   }
3989
3990   /* do not use PLAYING mask for fading out from main screen */
3991   game_status = GAME_MODE_MAIN;
3992
3993   StopAnimation();
3994
3995   if (!game.restart_level)
3996     CloseDoor(DOOR_CLOSE_1);
3997
3998 #if 1
3999   if (level_editor_test_game)
4000     FadeSkipNextFadeIn();
4001   else
4002     FadeSetEnterScreen();
4003 #else
4004   if (level_editor_test_game)
4005     fading = fading_none;
4006   else
4007     fading = menu.destination;
4008 #endif
4009
4010 #if 1
4011   FadeOut(REDRAW_FIELD);
4012 #else
4013   if (do_fading)
4014     FadeOut(REDRAW_FIELD);
4015 #endif
4016
4017   game_status = GAME_MODE_PLAYING;
4018
4019   /* !!! FIX THIS (START) !!! */
4020   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4021   {
4022     InitGameEngine_EM();
4023
4024     /* blit playfield from scroll buffer to normal back buffer for fading in */
4025     BlitScreenToBitmap_EM(backbuffer);
4026   }
4027   else
4028   {
4029     DrawLevel();
4030     DrawAllPlayers();
4031
4032     /* after drawing the level, correct some elements */
4033     if (game.timegate_time_left == 0)
4034       CloseAllOpenTimegates();
4035
4036     /* blit playfield from scroll buffer to normal back buffer for fading in */
4037     if (setup.soft_scrolling)
4038       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4039
4040     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4041   }
4042   /* !!! FIX THIS (END) !!! */
4043
4044 #if 1
4045   FadeIn(REDRAW_FIELD);
4046 #else
4047   if (do_fading)
4048     FadeIn(REDRAW_FIELD);
4049
4050   BackToFront();
4051 #endif
4052
4053   if (!game.restart_level)
4054   {
4055     /* copy default game door content to main double buffer */
4056     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4057                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4058   }
4059
4060   SetPanelBackground();
4061   SetDrawBackgroundMask(REDRAW_DOOR_1);
4062
4063   UpdateGameDoorValues();
4064   DrawGameDoorValues();
4065
4066   if (!game.restart_level)
4067   {
4068     UnmapGameButtons();
4069     UnmapTapeButtons();
4070     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4071     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4072     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4073     MapGameButtons();
4074     MapTapeButtons();
4075
4076     /* copy actual game door content to door double buffer for OpenDoor() */
4077     BlitBitmap(drawto, bitmap_db_door,
4078                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4079
4080     OpenDoor(DOOR_OPEN_ALL);
4081
4082     PlaySound(SND_GAME_STARTING);
4083
4084     if (setup.sound_music)
4085       PlayLevelMusic();
4086
4087     KeyboardAutoRepeatOffUnlessAutoplay();
4088
4089     if (options.debug)
4090     {
4091       for (i = 0; i < MAX_PLAYERS; i++)
4092         printf("Player %d %sactive.\n",
4093                i + 1, (stored_player[i].active ? "" : "not "));
4094     }
4095   }
4096
4097 #if 1
4098   UnmapAllGadgets();
4099
4100   MapGameButtons();
4101   MapTapeButtons();
4102 #endif
4103
4104   game.restart_level = FALSE;
4105 }
4106
4107 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4108 {
4109   /* this is used for non-R'n'D game engines to update certain engine values */
4110
4111   /* needed to determine if sounds are played within the visible screen area */
4112   scroll_x = actual_scroll_x;
4113   scroll_y = actual_scroll_y;
4114 }
4115
4116 void InitMovDir(int x, int y)
4117 {
4118   int i, element = Feld[x][y];
4119   static int xy[4][2] =
4120   {
4121     {  0, +1 },
4122     { +1,  0 },
4123     {  0, -1 },
4124     { -1,  0 }
4125   };
4126   static int direction[3][4] =
4127   {
4128     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4129     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4130     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4131   };
4132
4133   switch (element)
4134   {
4135     case EL_BUG_RIGHT:
4136     case EL_BUG_UP:
4137     case EL_BUG_LEFT:
4138     case EL_BUG_DOWN:
4139       Feld[x][y] = EL_BUG;
4140       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4141       break;
4142
4143     case EL_SPACESHIP_RIGHT:
4144     case EL_SPACESHIP_UP:
4145     case EL_SPACESHIP_LEFT:
4146     case EL_SPACESHIP_DOWN:
4147       Feld[x][y] = EL_SPACESHIP;
4148       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4149       break;
4150
4151     case EL_BD_BUTTERFLY_RIGHT:
4152     case EL_BD_BUTTERFLY_UP:
4153     case EL_BD_BUTTERFLY_LEFT:
4154     case EL_BD_BUTTERFLY_DOWN:
4155       Feld[x][y] = EL_BD_BUTTERFLY;
4156       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4157       break;
4158
4159     case EL_BD_FIREFLY_RIGHT:
4160     case EL_BD_FIREFLY_UP:
4161     case EL_BD_FIREFLY_LEFT:
4162     case EL_BD_FIREFLY_DOWN:
4163       Feld[x][y] = EL_BD_FIREFLY;
4164       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4165       break;
4166
4167     case EL_PACMAN_RIGHT:
4168     case EL_PACMAN_UP:
4169     case EL_PACMAN_LEFT:
4170     case EL_PACMAN_DOWN:
4171       Feld[x][y] = EL_PACMAN;
4172       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4173       break;
4174
4175     case EL_YAMYAM_LEFT:
4176     case EL_YAMYAM_RIGHT:
4177     case EL_YAMYAM_UP:
4178     case EL_YAMYAM_DOWN:
4179       Feld[x][y] = EL_YAMYAM;
4180       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4181       break;
4182
4183     case EL_SP_SNIKSNAK:
4184       MovDir[x][y] = MV_UP;
4185       break;
4186
4187     case EL_SP_ELECTRON:
4188       MovDir[x][y] = MV_LEFT;
4189       break;
4190
4191     case EL_MOLE_LEFT:
4192     case EL_MOLE_RIGHT:
4193     case EL_MOLE_UP:
4194     case EL_MOLE_DOWN:
4195       Feld[x][y] = EL_MOLE;
4196       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4197       break;
4198
4199     default:
4200       if (IS_CUSTOM_ELEMENT(element))
4201       {
4202         struct ElementInfo *ei = &element_info[element];
4203         int move_direction_initial = ei->move_direction_initial;
4204         int move_pattern = ei->move_pattern;
4205
4206         if (move_direction_initial == MV_START_PREVIOUS)
4207         {
4208           if (MovDir[x][y] != MV_NONE)
4209             return;
4210
4211           move_direction_initial = MV_START_AUTOMATIC;
4212         }
4213
4214         if (move_direction_initial == MV_START_RANDOM)
4215           MovDir[x][y] = 1 << RND(4);
4216         else if (move_direction_initial & MV_ANY_DIRECTION)
4217           MovDir[x][y] = move_direction_initial;
4218         else if (move_pattern == MV_ALL_DIRECTIONS ||
4219                  move_pattern == MV_TURNING_LEFT ||
4220                  move_pattern == MV_TURNING_RIGHT ||
4221                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4222                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4223                  move_pattern == MV_TURNING_RANDOM)
4224           MovDir[x][y] = 1 << RND(4);
4225         else if (move_pattern == MV_HORIZONTAL)
4226           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4227         else if (move_pattern == MV_VERTICAL)
4228           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4229         else if (move_pattern & MV_ANY_DIRECTION)
4230           MovDir[x][y] = element_info[element].move_pattern;
4231         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4232                  move_pattern == MV_ALONG_RIGHT_SIDE)
4233         {
4234           /* use random direction as default start direction */
4235           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4236             MovDir[x][y] = 1 << RND(4);
4237
4238           for (i = 0; i < NUM_DIRECTIONS; i++)
4239           {
4240             int x1 = x + xy[i][0];
4241             int y1 = y + xy[i][1];
4242
4243             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4244             {
4245               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4246                 MovDir[x][y] = direction[0][i];
4247               else
4248                 MovDir[x][y] = direction[1][i];
4249
4250               break;
4251             }
4252           }
4253         }                
4254       }
4255       else
4256       {
4257         MovDir[x][y] = 1 << RND(4);
4258
4259         if (element != EL_BUG &&
4260             element != EL_SPACESHIP &&
4261             element != EL_BD_BUTTERFLY &&
4262             element != EL_BD_FIREFLY)
4263           break;
4264
4265         for (i = 0; i < NUM_DIRECTIONS; i++)
4266         {
4267           int x1 = x + xy[i][0];
4268           int y1 = y + xy[i][1];
4269
4270           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4271           {
4272             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4273             {
4274               MovDir[x][y] = direction[0][i];
4275               break;
4276             }
4277             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4278                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4279             {
4280               MovDir[x][y] = direction[1][i];
4281               break;
4282             }
4283           }
4284         }
4285       }
4286       break;
4287   }
4288
4289   GfxDir[x][y] = MovDir[x][y];
4290 }
4291
4292 void InitAmoebaNr(int x, int y)
4293 {
4294   int i;
4295   int group_nr = AmoebeNachbarNr(x, y);
4296
4297   if (group_nr == 0)
4298   {
4299     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4300     {
4301       if (AmoebaCnt[i] == 0)
4302       {
4303         group_nr = i;
4304         break;
4305       }
4306     }
4307   }
4308
4309   AmoebaNr[x][y] = group_nr;
4310   AmoebaCnt[group_nr]++;
4311   AmoebaCnt2[group_nr]++;
4312 }
4313
4314 static void PlayerWins(struct PlayerInfo *player)
4315 {
4316   player->LevelSolved = TRUE;
4317   player->GameOver = TRUE;
4318
4319   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4320                          level.native_em_level->lev->score : player->score);
4321
4322   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4323   player->LevelSolved_CountingScore = player->score_final;
4324 }
4325
4326 void GameWon()
4327 {
4328   static int time, time_final;
4329   static int score, score_final;
4330   static int game_over_delay_1 = 0;
4331   static int game_over_delay_2 = 0;
4332   int game_over_delay_value_1 = 50;
4333   int game_over_delay_value_2 = 50;
4334
4335   if (!local_player->LevelSolved_GameWon)
4336   {
4337     int i;
4338
4339     /* do not start end game actions before the player stops moving (to exit) */
4340     if (local_player->MovPos)
4341       return;
4342
4343     local_player->LevelSolved_GameWon = TRUE;
4344     local_player->LevelSolved_SaveTape = tape.recording;
4345     local_player->LevelSolved_SaveScore = !tape.playing;
4346
4347     if (tape.auto_play)         /* tape might already be stopped here */
4348       tape.auto_play_level_solved = TRUE;
4349
4350 #if 1
4351     TapeStop();
4352 #endif
4353
4354     game_over_delay_1 = game_over_delay_value_1;
4355     game_over_delay_2 = game_over_delay_value_2;
4356
4357     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4358     score = score_final = local_player->score_final;
4359
4360     if (TimeLeft > 0)
4361     {
4362       time_final = 0;
4363       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4364     }
4365     else if (level.time == 0 && TimePlayed < 999)
4366     {
4367       time_final = 999;
4368       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4369     }
4370
4371     local_player->score_final = score_final;
4372
4373     if (level_editor_test_game)
4374     {
4375       time = time_final;
4376       score = score_final;
4377
4378 #if 1
4379       local_player->LevelSolved_CountingTime = time;
4380       local_player->LevelSolved_CountingScore = score;
4381
4382       game_panel_controls[GAME_PANEL_TIME].value = time;
4383       game_panel_controls[GAME_PANEL_SCORE].value = score;
4384
4385       DisplayGameControlValues();
4386 #else
4387       DrawGameValue_Time(time);
4388       DrawGameValue_Score(score);
4389 #endif
4390     }
4391
4392     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4393     {
4394       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4395       {
4396         /* close exit door after last player */
4397         if ((AllPlayersGone &&
4398              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4399               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4400               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4401             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4402             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4403         {
4404           int element = Feld[ExitX][ExitY];
4405
4406 #if 0
4407           if (element == EL_EM_EXIT_OPEN ||
4408               element == EL_EM_STEEL_EXIT_OPEN)
4409           {
4410             Bang(ExitX, ExitY);
4411           }
4412           else
4413 #endif
4414           {
4415             Feld[ExitX][ExitY] =
4416               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4417                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4418                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4419                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4420                EL_EM_STEEL_EXIT_CLOSING);
4421
4422             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4423           }
4424         }
4425
4426         /* player disappears */
4427         DrawLevelField(ExitX, ExitY);
4428       }
4429
4430       for (i = 0; i < MAX_PLAYERS; i++)
4431       {
4432         struct PlayerInfo *player = &stored_player[i];
4433
4434         if (player->present)
4435         {
4436           RemovePlayer(player);
4437
4438           /* player disappears */
4439           DrawLevelField(player->jx, player->jy);
4440         }
4441       }
4442     }
4443
4444     PlaySound(SND_GAME_WINNING);
4445   }
4446
4447   if (game_over_delay_1 > 0)
4448   {
4449     game_over_delay_1--;
4450
4451     return;
4452   }
4453
4454   if (time != time_final)
4455   {
4456     int time_to_go = ABS(time_final - time);
4457     int time_count_dir = (time < time_final ? +1 : -1);
4458     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4459
4460     time  += time_count_steps * time_count_dir;
4461     score += time_count_steps * level.score[SC_TIME_BONUS];
4462
4463 #if 1
4464     local_player->LevelSolved_CountingTime = time;
4465     local_player->LevelSolved_CountingScore = score;
4466
4467     game_panel_controls[GAME_PANEL_TIME].value = time;
4468     game_panel_controls[GAME_PANEL_SCORE].value = score;
4469
4470     DisplayGameControlValues();
4471 #else
4472     DrawGameValue_Time(time);
4473     DrawGameValue_Score(score);
4474 #endif
4475
4476     if (time == time_final)
4477       StopSound(SND_GAME_LEVELTIME_BONUS);
4478     else if (setup.sound_loops)
4479       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4480     else
4481       PlaySound(SND_GAME_LEVELTIME_BONUS);
4482
4483     return;
4484   }
4485
4486   local_player->LevelSolved_PanelOff = TRUE;
4487
4488   if (game_over_delay_2 > 0)
4489   {
4490     game_over_delay_2--;
4491
4492     return;
4493   }
4494
4495 #if 1
4496   GameEnd();
4497 #endif
4498 }
4499
4500 void GameEnd()
4501 {
4502   int hi_pos;
4503   boolean raise_level = FALSE;
4504
4505   local_player->LevelSolved_GameEnd = TRUE;
4506
4507   CloseDoor(DOOR_CLOSE_1);
4508
4509   if (local_player->LevelSolved_SaveTape)
4510   {
4511 #if 0
4512     TapeStop();
4513 #endif
4514
4515 #if 1
4516     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4517 #else
4518     SaveTape(tape.level_nr);            /* ask to save tape */
4519 #endif
4520   }
4521
4522   if (level_editor_test_game)
4523   {
4524     game_status = GAME_MODE_MAIN;
4525
4526 #if 1
4527     DrawAndFadeInMainMenu(REDRAW_FIELD);
4528 #else
4529     DrawMainMenu();
4530 #endif
4531
4532     return;
4533   }
4534
4535   if (!local_player->LevelSolved_SaveScore)
4536   {
4537 #if 1
4538     FadeOut(REDRAW_FIELD);
4539 #endif
4540
4541     game_status = GAME_MODE_MAIN;
4542
4543     DrawAndFadeInMainMenu(REDRAW_FIELD);
4544
4545     return;
4546   }
4547
4548   if (level_nr == leveldir_current->handicap_level)
4549   {
4550     leveldir_current->handicap_level++;
4551     SaveLevelSetup_SeriesInfo();
4552   }
4553
4554   if (level_nr < leveldir_current->last_level)
4555     raise_level = TRUE;                 /* advance to next level */
4556
4557   if ((hi_pos = NewHiScore()) >= 0) 
4558   {
4559     game_status = GAME_MODE_SCORES;
4560
4561     DrawHallOfFame(hi_pos);
4562
4563     if (raise_level)
4564     {
4565       level_nr++;
4566       TapeErase();
4567     }
4568   }
4569   else
4570   {
4571 #if 1
4572     FadeOut(REDRAW_FIELD);
4573 #endif
4574
4575     game_status = GAME_MODE_MAIN;
4576
4577     if (raise_level)
4578     {
4579       level_nr++;
4580       TapeErase();
4581     }
4582
4583     DrawAndFadeInMainMenu(REDRAW_FIELD);
4584   }
4585 }
4586
4587 int NewHiScore()
4588 {
4589   int k, l;
4590   int position = -1;
4591
4592   LoadScore(level_nr);
4593
4594   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4595       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4596     return -1;
4597
4598   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4599   {
4600     if (local_player->score_final > highscore[k].Score)
4601     {
4602       /* player has made it to the hall of fame */
4603
4604       if (k < MAX_SCORE_ENTRIES - 1)
4605       {
4606         int m = MAX_SCORE_ENTRIES - 1;
4607
4608 #ifdef ONE_PER_NAME
4609         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4610           if (strEqual(setup.player_name, highscore[l].Name))
4611             m = l;
4612         if (m == k)     /* player's new highscore overwrites his old one */
4613           goto put_into_list;
4614 #endif
4615
4616         for (l = m; l > k; l--)
4617         {
4618           strcpy(highscore[l].Name, highscore[l - 1].Name);
4619           highscore[l].Score = highscore[l - 1].Score;
4620         }
4621       }
4622
4623 #ifdef ONE_PER_NAME
4624       put_into_list:
4625 #endif
4626       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4627       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4628       highscore[k].Score = local_player->score_final; 
4629       position = k;
4630       break;
4631     }
4632
4633 #ifdef ONE_PER_NAME
4634     else if (!strncmp(setup.player_name, highscore[k].Name,
4635                       MAX_PLAYER_NAME_LEN))
4636       break;    /* player already there with a higher score */
4637 #endif
4638
4639   }
4640
4641   if (position >= 0) 
4642     SaveScore(level_nr);
4643
4644   return position;
4645 }
4646
4647 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4648 {
4649   int element = Feld[x][y];
4650   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4651   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4652   int horiz_move = (dx != 0);
4653   int sign = (horiz_move ? dx : dy);
4654   int step = sign * element_info[element].move_stepsize;
4655
4656   /* special values for move stepsize for spring and things on conveyor belt */
4657   if (horiz_move)
4658   {
4659     if (CAN_FALL(element) &&
4660         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4661       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4662     else if (element == EL_SPRING)
4663       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4664   }
4665
4666   return step;
4667 }
4668
4669 inline static int getElementMoveStepsize(int x, int y)
4670 {
4671   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4672 }
4673
4674 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4675 {
4676   if (player->GfxAction != action || player->GfxDir != dir)
4677   {
4678 #if 0
4679     printf("Player frame reset! (%d => %d, %d => %d)\n",
4680            player->GfxAction, action, player->GfxDir, dir);
4681 #endif
4682
4683     player->GfxAction = action;
4684     player->GfxDir = dir;
4685     player->Frame = 0;
4686     player->StepFrame = 0;
4687   }
4688 }
4689
4690 #if USE_GFX_RESET_GFX_ANIMATION
4691 static void ResetGfxFrame(int x, int y, boolean redraw)
4692 {
4693   int element = Feld[x][y];
4694   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4695   int last_gfx_frame = GfxFrame[x][y];
4696
4697   if (graphic_info[graphic].anim_global_sync)
4698     GfxFrame[x][y] = FrameCounter;
4699   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4700     GfxFrame[x][y] = CustomValue[x][y];
4701   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4702     GfxFrame[x][y] = element_info[element].collect_score;
4703   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4704     GfxFrame[x][y] = ChangeDelay[x][y];
4705
4706   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4707     DrawLevelGraphicAnimation(x, y, graphic);
4708 }
4709 #endif
4710
4711 static void ResetGfxAnimation(int x, int y)
4712 {
4713   GfxAction[x][y] = ACTION_DEFAULT;
4714   GfxDir[x][y] = MovDir[x][y];
4715   GfxFrame[x][y] = 0;
4716
4717 #if USE_GFX_RESET_GFX_ANIMATION
4718   ResetGfxFrame(x, y, FALSE);
4719 #endif
4720 }
4721
4722 static void ResetRandomAnimationValue(int x, int y)
4723 {
4724   GfxRandom[x][y] = INIT_GFX_RANDOM();
4725 }
4726
4727 void InitMovingField(int x, int y, int direction)
4728 {
4729   int element = Feld[x][y];
4730   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4731   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4732   int newx = x + dx;
4733   int newy = y + dy;
4734   boolean is_moving_before, is_moving_after;
4735 #if 0
4736   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4737 #endif
4738
4739   /* check if element was/is moving or being moved before/after mode change */
4740 #if 1
4741 #if 1
4742   is_moving_before = (WasJustMoving[x][y] != 0);
4743 #else
4744   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4745   is_moving_before = WasJustMoving[x][y];
4746 #endif
4747 #else
4748   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4749 #endif
4750   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4751
4752   /* reset animation only for moving elements which change direction of moving
4753      or which just started or stopped moving
4754      (else CEs with property "can move" / "not moving" are reset each frame) */
4755 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4756 #if 1
4757   if (is_moving_before != is_moving_after ||
4758       direction != MovDir[x][y])
4759     ResetGfxAnimation(x, y);
4760 #else
4761   if ((is_moving_before || is_moving_after) && !continues_moving)
4762     ResetGfxAnimation(x, y);
4763 #endif
4764 #else
4765   if (!continues_moving)
4766     ResetGfxAnimation(x, y);
4767 #endif
4768
4769   MovDir[x][y] = direction;
4770   GfxDir[x][y] = direction;
4771
4772 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4773   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4774                      direction == MV_DOWN && CAN_FALL(element) ?
4775                      ACTION_FALLING : ACTION_MOVING);
4776 #else
4777   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4778                      ACTION_FALLING : ACTION_MOVING);
4779 #endif
4780
4781   /* this is needed for CEs with property "can move" / "not moving" */
4782
4783   if (is_moving_after)
4784   {
4785     if (Feld[newx][newy] == EL_EMPTY)
4786       Feld[newx][newy] = EL_BLOCKED;
4787
4788     MovDir[newx][newy] = MovDir[x][y];
4789
4790 #if USE_NEW_CUSTOM_VALUE
4791     CustomValue[newx][newy] = CustomValue[x][y];
4792 #endif
4793
4794     GfxFrame[newx][newy] = GfxFrame[x][y];
4795     GfxRandom[newx][newy] = GfxRandom[x][y];
4796     GfxAction[newx][newy] = GfxAction[x][y];
4797     GfxDir[newx][newy] = GfxDir[x][y];
4798   }
4799 }
4800
4801 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4802 {
4803   int direction = MovDir[x][y];
4804   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4805   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4806
4807   *goes_to_x = newx;
4808   *goes_to_y = newy;
4809 }
4810
4811 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4812 {
4813   int oldx = x, oldy = y;
4814   int direction = MovDir[x][y];
4815
4816   if (direction == MV_LEFT)
4817     oldx++;
4818   else if (direction == MV_RIGHT)
4819     oldx--;
4820   else if (direction == MV_UP)
4821     oldy++;
4822   else if (direction == MV_DOWN)
4823     oldy--;
4824
4825   *comes_from_x = oldx;
4826   *comes_from_y = oldy;
4827 }
4828
4829 int MovingOrBlocked2Element(int x, int y)
4830 {
4831   int element = Feld[x][y];
4832
4833   if (element == EL_BLOCKED)
4834   {
4835     int oldx, oldy;
4836
4837     Blocked2Moving(x, y, &oldx, &oldy);
4838     return Feld[oldx][oldy];
4839   }
4840   else
4841     return element;
4842 }
4843
4844 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4845 {
4846   /* like MovingOrBlocked2Element(), but if element is moving
4847      and (x,y) is the field the moving element is just leaving,
4848      return EL_BLOCKED instead of the element value */
4849   int element = Feld[x][y];
4850
4851   if (IS_MOVING(x, y))
4852   {
4853     if (element == EL_BLOCKED)
4854     {
4855       int oldx, oldy;
4856
4857       Blocked2Moving(x, y, &oldx, &oldy);
4858       return Feld[oldx][oldy];
4859     }
4860     else
4861       return EL_BLOCKED;
4862   }
4863   else
4864     return element;
4865 }
4866
4867 static void RemoveField(int x, int y)
4868 {
4869   Feld[x][y] = EL_EMPTY;
4870
4871   MovPos[x][y] = 0;
4872   MovDir[x][y] = 0;
4873   MovDelay[x][y] = 0;
4874
4875 #if USE_NEW_CUSTOM_VALUE
4876   CustomValue[x][y] = 0;
4877 #endif
4878
4879   AmoebaNr[x][y] = 0;
4880   ChangeDelay[x][y] = 0;
4881   ChangePage[x][y] = -1;
4882   Pushed[x][y] = FALSE;
4883
4884 #if 0
4885   ExplodeField[x][y] = EX_TYPE_NONE;
4886 #endif
4887
4888   GfxElement[x][y] = EL_UNDEFINED;
4889   GfxAction[x][y] = ACTION_DEFAULT;
4890   GfxDir[x][y] = MV_NONE;
4891 }
4892
4893 void RemoveMovingField(int x, int y)
4894 {
4895   int oldx = x, oldy = y, newx = x, newy = y;
4896   int element = Feld[x][y];
4897   int next_element = EL_UNDEFINED;
4898
4899   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4900     return;
4901
4902   if (IS_MOVING(x, y))
4903   {
4904     Moving2Blocked(x, y, &newx, &newy);
4905
4906     if (Feld[newx][newy] != EL_BLOCKED)
4907     {
4908       /* element is moving, but target field is not free (blocked), but
4909          already occupied by something different (example: acid pool);
4910          in this case, only remove the moving field, but not the target */
4911
4912       RemoveField(oldx, oldy);
4913
4914       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4915
4916       DrawLevelField(oldx, oldy);
4917
4918       return;
4919     }
4920   }
4921   else if (element == EL_BLOCKED)
4922   {
4923     Blocked2Moving(x, y, &oldx, &oldy);
4924     if (!IS_MOVING(oldx, oldy))
4925       return;
4926   }
4927
4928   if (element == EL_BLOCKED &&
4929       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4930        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4931        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4932        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4933        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4934        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4935     next_element = get_next_element(Feld[oldx][oldy]);
4936
4937   RemoveField(oldx, oldy);
4938   RemoveField(newx, newy);
4939
4940   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4941
4942   if (next_element != EL_UNDEFINED)
4943     Feld[oldx][oldy] = next_element;
4944
4945   DrawLevelField(oldx, oldy);
4946   DrawLevelField(newx, newy);
4947 }
4948
4949 void DrawDynamite(int x, int y)
4950 {
4951   int sx = SCREENX(x), sy = SCREENY(y);
4952   int graphic = el2img(Feld[x][y]);
4953   int frame;
4954
4955   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4956     return;
4957
4958   if (IS_WALKABLE_INSIDE(Back[x][y]))
4959     return;
4960
4961   if (Back[x][y])
4962     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4963   else if (Store[x][y])
4964     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4965
4966   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4967
4968   if (Back[x][y] || Store[x][y])
4969     DrawGraphicThruMask(sx, sy, graphic, frame);
4970   else
4971     DrawGraphic(sx, sy, graphic, frame);
4972 }
4973
4974 void CheckDynamite(int x, int y)
4975 {
4976   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4977   {
4978     MovDelay[x][y]--;
4979
4980     if (MovDelay[x][y] != 0)
4981     {
4982       DrawDynamite(x, y);
4983       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4984
4985       return;
4986     }
4987   }
4988
4989   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4990
4991   Bang(x, y);
4992 }
4993
4994 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4995 {
4996   boolean num_checked_players = 0;
4997   int i;
4998
4999   for (i = 0; i < MAX_PLAYERS; i++)
5000   {
5001     if (stored_player[i].active)
5002     {
5003       int sx = stored_player[i].jx;
5004       int sy = stored_player[i].jy;
5005
5006       if (num_checked_players == 0)
5007       {
5008         *sx1 = *sx2 = sx;
5009         *sy1 = *sy2 = sy;
5010       }
5011       else
5012       {
5013         *sx1 = MIN(*sx1, sx);
5014         *sy1 = MIN(*sy1, sy);
5015         *sx2 = MAX(*sx2, sx);
5016         *sy2 = MAX(*sy2, sy);
5017       }
5018
5019       num_checked_players++;
5020     }
5021   }
5022 }
5023
5024 static boolean checkIfAllPlayersFitToScreen_RND()
5025 {
5026   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5027
5028   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5029
5030   return (sx2 - sx1 < SCR_FIELDX &&
5031           sy2 - sy1 < SCR_FIELDY);
5032 }
5033
5034 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5035 {
5036   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5037
5038   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5039
5040   *sx = (sx1 + sx2) / 2;
5041   *sy = (sy1 + sy2) / 2;
5042 }
5043
5044 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5045                         boolean center_screen, boolean quick_relocation)
5046 {
5047   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5048   boolean no_delay = (tape.warp_forward);
5049   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5050   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5051
5052   if (quick_relocation)
5053   {
5054     int offset = game.scroll_delay_value;
5055
5056     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5057     {
5058       if (!level.shifted_relocation || center_screen)
5059       {
5060         /* quick relocation (without scrolling), with centering of screen */
5061
5062         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5063                     x > SBX_Right + MIDPOSX ? SBX_Right :
5064                     x - MIDPOSX);
5065
5066         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5067                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5068                     y - MIDPOSY);
5069       }
5070       else
5071       {
5072         /* quick relocation (without scrolling), but do not center screen */
5073
5074         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5075                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5076                                old_x - MIDPOSX);
5077
5078         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5079                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5080                                old_y - MIDPOSY);
5081
5082         int offset_x = x + (scroll_x - center_scroll_x);
5083         int offset_y = y + (scroll_y - center_scroll_y);
5084
5085         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5086                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5087                     offset_x - MIDPOSX);
5088
5089         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5090                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5091                     offset_y - MIDPOSY);
5092       }
5093     }
5094     else
5095     {
5096       /* quick relocation (without scrolling), inside visible screen area */
5097
5098       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5099           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5100         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5101
5102       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5103           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5104         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5105
5106       /* don't scroll over playfield boundaries */
5107       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5108         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5109
5110       /* don't scroll over playfield boundaries */
5111       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5112         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5113     }
5114
5115     RedrawPlayfield(TRUE, 0,0,0,0);
5116   }
5117   else
5118   {
5119 #if 1
5120     int scroll_xx, scroll_yy;
5121
5122     if (!level.shifted_relocation || center_screen)
5123     {
5124       /* visible relocation (with scrolling), with centering of screen */
5125
5126       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5127                    x > SBX_Right + MIDPOSX ? SBX_Right :
5128                    x - MIDPOSX);
5129
5130       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5131                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5132                    y - MIDPOSY);
5133     }
5134     else
5135     {
5136       /* visible relocation (with scrolling), but do not center screen */
5137
5138       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5139                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5140                              old_x - MIDPOSX);
5141
5142       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5143                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5144                              old_y - MIDPOSY);
5145
5146       int offset_x = x + (scroll_x - center_scroll_x);
5147       int offset_y = y + (scroll_y - center_scroll_y);
5148
5149       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5150                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5151                    offset_x - MIDPOSX);
5152
5153       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5154                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5155                    offset_y - MIDPOSY);
5156     }
5157
5158 #else
5159
5160     /* visible relocation (with scrolling), with centering of screen */
5161
5162     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5163                      x > SBX_Right + MIDPOSX ? SBX_Right :
5164                      x - MIDPOSX);
5165
5166     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5167                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5168                      y - MIDPOSY);
5169 #endif
5170
5171     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5172
5173     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5174     {
5175       int dx = 0, dy = 0;
5176       int fx = FX, fy = FY;
5177
5178       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5179       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5180
5181       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5182         break;
5183
5184       scroll_x -= dx;
5185       scroll_y -= dy;
5186
5187       fx += dx * TILEX / 2;
5188       fy += dy * TILEY / 2;
5189
5190       ScrollLevel(dx, dy);
5191       DrawAllPlayers();
5192
5193       /* scroll in two steps of half tile size to make things smoother */
5194       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5195       FlushDisplay();
5196       Delay(wait_delay_value);
5197
5198       /* scroll second step to align at full tile size */
5199       BackToFront();
5200       Delay(wait_delay_value);
5201     }
5202
5203     DrawAllPlayers();
5204     BackToFront();
5205     Delay(wait_delay_value);
5206   }
5207 }
5208
5209 void RelocatePlayer(int jx, int jy, int el_player_raw)
5210 {
5211   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5212   int player_nr = GET_PLAYER_NR(el_player);
5213   struct PlayerInfo *player = &stored_player[player_nr];
5214   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5215   boolean no_delay = (tape.warp_forward);
5216   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5217   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5218   int old_jx = player->jx;
5219   int old_jy = player->jy;
5220   int old_element = Feld[old_jx][old_jy];
5221   int element = Feld[jx][jy];
5222   boolean player_relocated = (old_jx != jx || old_jy != jy);
5223
5224   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5225   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5226   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5227   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5228   int leave_side_horiz = move_dir_horiz;
5229   int leave_side_vert  = move_dir_vert;
5230   int enter_side = enter_side_horiz | enter_side_vert;
5231   int leave_side = leave_side_horiz | leave_side_vert;
5232
5233   if (player->GameOver)         /* do not reanimate dead player */
5234     return;
5235
5236   if (!player_relocated)        /* no need to relocate the player */
5237     return;
5238
5239   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5240   {
5241     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5242     DrawLevelField(jx, jy);
5243   }
5244
5245   if (player->present)
5246   {
5247     while (player->MovPos)
5248     {
5249       ScrollPlayer(player, SCROLL_GO_ON);
5250       ScrollScreen(NULL, SCROLL_GO_ON);
5251
5252       AdvanceFrameAndPlayerCounters(player->index_nr);
5253
5254       DrawPlayer(player);
5255
5256       BackToFront();
5257       Delay(wait_delay_value);
5258     }
5259
5260     DrawPlayer(player);         /* needed here only to cleanup last field */
5261     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5262
5263     player->is_moving = FALSE;
5264   }
5265
5266   if (IS_CUSTOM_ELEMENT(old_element))
5267     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5268                                CE_LEFT_BY_PLAYER,
5269                                player->index_bit, leave_side);
5270
5271   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5272                                       CE_PLAYER_LEAVES_X,
5273                                       player->index_bit, leave_side);
5274
5275   Feld[jx][jy] = el_player;
5276   InitPlayerField(jx, jy, el_player, TRUE);
5277
5278   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5279   {
5280     Feld[jx][jy] = element;
5281     InitField(jx, jy, FALSE);
5282   }
5283
5284   /* only visually relocate centered player */
5285   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5286                      FALSE, level.instant_relocation);
5287
5288   TestIfPlayerTouchesBadThing(jx, jy);
5289   TestIfPlayerTouchesCustomElement(jx, jy);
5290
5291   if (IS_CUSTOM_ELEMENT(element))
5292     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5293                                player->index_bit, enter_side);
5294
5295   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5296                                       player->index_bit, enter_side);
5297 }
5298
5299 void Explode(int ex, int ey, int phase, int mode)
5300 {
5301   int x, y;
5302   int last_phase;
5303   int border_element;
5304
5305   /* !!! eliminate this variable !!! */
5306   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5307
5308   if (game.explosions_delayed)
5309   {
5310     ExplodeField[ex][ey] = mode;
5311     return;
5312   }
5313
5314   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5315   {
5316     int center_element = Feld[ex][ey];
5317     int artwork_element, explosion_element;     /* set these values later */
5318
5319 #if 0
5320     /* --- This is only really needed (and now handled) in "Impact()". --- */
5321     /* do not explode moving elements that left the explode field in time */
5322     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5323         center_element == EL_EMPTY &&
5324         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5325       return;
5326 #endif
5327
5328 #if 0
5329     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5330     if (mode == EX_TYPE_NORMAL ||
5331         mode == EX_TYPE_CENTER ||
5332         mode == EX_TYPE_CROSS)
5333       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5334 #endif
5335
5336     /* remove things displayed in background while burning dynamite */
5337     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5338       Back[ex][ey] = 0;
5339
5340     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5341     {
5342       /* put moving element to center field (and let it explode there) */
5343       center_element = MovingOrBlocked2Element(ex, ey);
5344       RemoveMovingField(ex, ey);
5345       Feld[ex][ey] = center_element;
5346     }
5347
5348     /* now "center_element" is finally determined -- set related values now */
5349     artwork_element = center_element;           /* for custom player artwork */
5350     explosion_element = center_element;         /* for custom player artwork */
5351
5352     if (IS_PLAYER(ex, ey))
5353     {
5354       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5355
5356       artwork_element = stored_player[player_nr].artwork_element;
5357
5358       if (level.use_explosion_element[player_nr])
5359       {
5360         explosion_element = level.explosion_element[player_nr];
5361         artwork_element = explosion_element;
5362       }
5363     }
5364
5365 #if 1
5366     if (mode == EX_TYPE_NORMAL ||
5367         mode == EX_TYPE_CENTER ||
5368         mode == EX_TYPE_CROSS)
5369       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5370 #endif
5371
5372     last_phase = element_info[explosion_element].explosion_delay + 1;
5373
5374     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5375     {
5376       int xx = x - ex + 1;
5377       int yy = y - ey + 1;
5378       int element;
5379
5380       if (!IN_LEV_FIELD(x, y) ||
5381           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5382           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5383         continue;
5384
5385       element = Feld[x][y];
5386
5387       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5388       {
5389         element = MovingOrBlocked2Element(x, y);
5390
5391         if (!IS_EXPLOSION_PROOF(element))
5392           RemoveMovingField(x, y);
5393       }
5394
5395       /* indestructible elements can only explode in center (but not flames) */
5396       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5397                                            mode == EX_TYPE_BORDER)) ||
5398           element == EL_FLAMES)
5399         continue;
5400
5401       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5402          behaviour, for example when touching a yamyam that explodes to rocks
5403          with active deadly shield, a rock is created under the player !!! */
5404       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5405 #if 0
5406       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5407           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5408            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5409 #else
5410       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5411 #endif
5412       {
5413         if (IS_ACTIVE_BOMB(element))
5414         {
5415           /* re-activate things under the bomb like gate or penguin */
5416           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5417           Back[x][y] = 0;
5418         }
5419
5420         continue;
5421       }
5422
5423       /* save walkable background elements while explosion on same tile */
5424       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5425           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5426         Back[x][y] = element;
5427
5428       /* ignite explodable elements reached by other explosion */
5429       if (element == EL_EXPLOSION)
5430         element = Store2[x][y];
5431
5432       if (AmoebaNr[x][y] &&
5433           (element == EL_AMOEBA_FULL ||
5434            element == EL_BD_AMOEBA ||
5435            element == EL_AMOEBA_GROWING))
5436       {
5437         AmoebaCnt[AmoebaNr[x][y]]--;
5438         AmoebaCnt2[AmoebaNr[x][y]]--;
5439       }
5440
5441       RemoveField(x, y);
5442
5443       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5444       {
5445         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5446
5447         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5448
5449         if (PLAYERINFO(ex, ey)->use_murphy)
5450           Store[x][y] = EL_EMPTY;
5451       }
5452
5453       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5454          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5455       else if (ELEM_IS_PLAYER(center_element))
5456         Store[x][y] = EL_EMPTY;
5457       else if (center_element == EL_YAMYAM)
5458         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5459       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5460         Store[x][y] = element_info[center_element].content.e[xx][yy];
5461 #if 1
5462       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5463          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5464          otherwise) -- FIX THIS !!! */
5465       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5466         Store[x][y] = element_info[element].content.e[1][1];
5467 #else
5468       else if (!CAN_EXPLODE(element))
5469         Store[x][y] = element_info[element].content.e[1][1];
5470 #endif
5471       else
5472         Store[x][y] = EL_EMPTY;
5473
5474       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5475           center_element == EL_AMOEBA_TO_DIAMOND)
5476         Store2[x][y] = element;
5477
5478       Feld[x][y] = EL_EXPLOSION;
5479       GfxElement[x][y] = artwork_element;
5480
5481       ExplodePhase[x][y] = 1;
5482       ExplodeDelay[x][y] = last_phase;
5483
5484       Stop[x][y] = TRUE;
5485     }
5486
5487     if (center_element == EL_YAMYAM)
5488       game.yamyam_content_nr =
5489         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5490
5491     return;
5492   }
5493
5494   if (Stop[ex][ey])
5495     return;
5496
5497   x = ex;
5498   y = ey;
5499
5500   if (phase == 1)
5501     GfxFrame[x][y] = 0;         /* restart explosion animation */
5502
5503   last_phase = ExplodeDelay[x][y];
5504
5505   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5506
5507 #ifdef DEBUG
5508
5509   /* activate this even in non-DEBUG version until cause for crash in
5510      getGraphicAnimationFrame() (see below) is found and eliminated */
5511
5512 #endif
5513 #if 1
5514
5515 #if 1
5516   /* this can happen if the player leaves an explosion just in time */
5517   if (GfxElement[x][y] == EL_UNDEFINED)
5518     GfxElement[x][y] = EL_EMPTY;
5519 #else
5520   if (GfxElement[x][y] == EL_UNDEFINED)
5521   {
5522     printf("\n\n");
5523     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5524     printf("Explode(): This should never happen!\n");
5525     printf("\n\n");
5526
5527     GfxElement[x][y] = EL_EMPTY;
5528   }
5529 #endif
5530
5531 #endif
5532
5533   border_element = Store2[x][y];
5534   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5535     border_element = StorePlayer[x][y];
5536
5537   if (phase == element_info[border_element].ignition_delay ||
5538       phase == last_phase)
5539   {
5540     boolean border_explosion = FALSE;
5541
5542     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5543         !PLAYER_EXPLOSION_PROTECTED(x, y))
5544     {
5545       KillPlayerUnlessExplosionProtected(x, y);
5546       border_explosion = TRUE;
5547     }
5548     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5549     {
5550       Feld[x][y] = Store2[x][y];
5551       Store2[x][y] = 0;
5552       Bang(x, y);
5553       border_explosion = TRUE;
5554     }
5555     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5556     {
5557       AmoebeUmwandeln(x, y);
5558       Store2[x][y] = 0;
5559       border_explosion = TRUE;
5560     }
5561
5562     /* if an element just explodes due to another explosion (chain-reaction),
5563        do not immediately end the new explosion when it was the last frame of
5564        the explosion (as it would be done in the following "if"-statement!) */
5565     if (border_explosion && phase == last_phase)
5566       return;
5567   }
5568
5569   if (phase == last_phase)
5570   {
5571     int element;
5572
5573     element = Feld[x][y] = Store[x][y];
5574     Store[x][y] = Store2[x][y] = 0;
5575     GfxElement[x][y] = EL_UNDEFINED;
5576
5577     /* player can escape from explosions and might therefore be still alive */
5578     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5579         element <= EL_PLAYER_IS_EXPLODING_4)
5580     {
5581       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5582       int explosion_element = EL_PLAYER_1 + player_nr;
5583       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5584       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5585
5586       if (level.use_explosion_element[player_nr])
5587         explosion_element = level.explosion_element[player_nr];
5588
5589       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5590                     element_info[explosion_element].content.e[xx][yy]);
5591     }
5592
5593     /* restore probably existing indestructible background element */
5594     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5595       element = Feld[x][y] = Back[x][y];
5596     Back[x][y] = 0;
5597
5598     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5599     GfxDir[x][y] = MV_NONE;
5600     ChangeDelay[x][y] = 0;
5601     ChangePage[x][y] = -1;
5602
5603 #if USE_NEW_CUSTOM_VALUE
5604     CustomValue[x][y] = 0;
5605 #endif
5606
5607     InitField_WithBug2(x, y, FALSE);
5608
5609     DrawLevelField(x, y);
5610
5611     TestIfElementTouchesCustomElement(x, y);
5612
5613     if (GFX_CRUMBLED(element))
5614       DrawLevelFieldCrumbledSandNeighbours(x, y);
5615
5616     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5617       StorePlayer[x][y] = 0;
5618
5619     if (ELEM_IS_PLAYER(element))
5620       RelocatePlayer(x, y, element);
5621   }
5622   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5623   {
5624     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5625     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5626
5627     if (phase == delay)
5628       DrawLevelFieldCrumbledSand(x, y);
5629
5630     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5631     {
5632       DrawLevelElement(x, y, Back[x][y]);
5633       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5634     }
5635     else if (IS_WALKABLE_UNDER(Back[x][y]))
5636     {
5637       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5638       DrawLevelElementThruMask(x, y, Back[x][y]);
5639     }
5640     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5641       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5642   }
5643 }
5644
5645 void DynaExplode(int ex, int ey)
5646 {
5647   int i, j;
5648   int dynabomb_element = Feld[ex][ey];
5649   int dynabomb_size = 1;
5650   boolean dynabomb_xl = FALSE;
5651   struct PlayerInfo *player;
5652   static int xy[4][2] =
5653   {
5654     { 0, -1 },
5655     { -1, 0 },
5656     { +1, 0 },
5657     { 0, +1 }
5658   };
5659
5660   if (IS_ACTIVE_BOMB(dynabomb_element))
5661   {
5662     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5663     dynabomb_size = player->dynabomb_size;
5664     dynabomb_xl = player->dynabomb_xl;
5665     player->dynabombs_left++;
5666   }
5667
5668   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5669
5670   for (i = 0; i < NUM_DIRECTIONS; i++)
5671   {
5672     for (j = 1; j <= dynabomb_size; j++)
5673     {
5674       int x = ex + j * xy[i][0];
5675       int y = ey + j * xy[i][1];
5676       int element;
5677
5678       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5679         break;
5680
5681       element = Feld[x][y];
5682
5683       /* do not restart explosions of fields with active bombs */
5684       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5685         continue;
5686
5687       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5688
5689       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5690           !IS_DIGGABLE(element) && !dynabomb_xl)
5691         break;
5692     }
5693   }
5694 }
5695
5696 void Bang(int x, int y)
5697 {
5698   int element = MovingOrBlocked2Element(x, y);
5699   int explosion_type = EX_TYPE_NORMAL;
5700
5701   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5702   {
5703     struct PlayerInfo *player = PLAYERINFO(x, y);
5704
5705     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5706                             player->element_nr);
5707
5708     if (level.use_explosion_element[player->index_nr])
5709     {
5710       int explosion_element = level.explosion_element[player->index_nr];
5711
5712       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5713         explosion_type = EX_TYPE_CROSS;
5714       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5715         explosion_type = EX_TYPE_CENTER;
5716     }
5717   }
5718
5719   switch (element)
5720   {
5721     case EL_BUG:
5722     case EL_SPACESHIP:
5723     case EL_BD_BUTTERFLY:
5724     case EL_BD_FIREFLY:
5725     case EL_YAMYAM:
5726     case EL_DARK_YAMYAM:
5727     case EL_ROBOT:
5728     case EL_PACMAN:
5729     case EL_MOLE:
5730       RaiseScoreElement(element);
5731       break;
5732
5733     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5734     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5735     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5736     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5737     case EL_DYNABOMB_INCREASE_NUMBER:
5738     case EL_DYNABOMB_INCREASE_SIZE:
5739     case EL_DYNABOMB_INCREASE_POWER:
5740       explosion_type = EX_TYPE_DYNA;
5741       break;
5742
5743     case EL_DC_LANDMINE:
5744 #if 0
5745     case EL_EM_EXIT_OPEN:
5746     case EL_EM_STEEL_EXIT_OPEN:
5747 #endif
5748       explosion_type = EX_TYPE_CENTER;
5749       break;
5750
5751     case EL_PENGUIN:
5752     case EL_LAMP:
5753     case EL_LAMP_ACTIVE:
5754     case EL_AMOEBA_TO_DIAMOND:
5755       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5756         explosion_type = EX_TYPE_CENTER;
5757       break;
5758
5759     default:
5760       if (element_info[element].explosion_type == EXPLODES_CROSS)
5761         explosion_type = EX_TYPE_CROSS;
5762       else if (element_info[element].explosion_type == EXPLODES_1X1)
5763         explosion_type = EX_TYPE_CENTER;
5764       break;
5765   }
5766
5767   if (explosion_type == EX_TYPE_DYNA)
5768     DynaExplode(x, y);
5769   else
5770     Explode(x, y, EX_PHASE_START, explosion_type);
5771
5772   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5773 }
5774
5775 void SplashAcid(int x, int y)
5776 {
5777   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5778       (!IN_LEV_FIELD(x - 1, y - 2) ||
5779        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5780     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5781
5782   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5783       (!IN_LEV_FIELD(x + 1, y - 2) ||
5784        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5785     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5786
5787   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5788 }
5789
5790 static void InitBeltMovement()
5791 {
5792   static int belt_base_element[4] =
5793   {
5794     EL_CONVEYOR_BELT_1_LEFT,
5795     EL_CONVEYOR_BELT_2_LEFT,
5796     EL_CONVEYOR_BELT_3_LEFT,
5797     EL_CONVEYOR_BELT_4_LEFT
5798   };
5799   static int belt_base_active_element[4] =
5800   {
5801     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5802     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5803     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5804     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5805   };
5806
5807   int x, y, i, j;
5808
5809   /* set frame order for belt animation graphic according to belt direction */
5810   for (i = 0; i < NUM_BELTS; i++)
5811   {
5812     int belt_nr = i;
5813
5814     for (j = 0; j < NUM_BELT_PARTS; j++)
5815     {
5816       int element = belt_base_active_element[belt_nr] + j;
5817       int graphic_1 = el2img(element);
5818       int graphic_2 = el2panelimg(element);
5819
5820       if (game.belt_dir[i] == MV_LEFT)
5821       {
5822         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5823         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5824       }
5825       else
5826       {
5827         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5828         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5829       }
5830     }
5831   }
5832
5833   SCAN_PLAYFIELD(x, y)
5834   {
5835     int element = Feld[x][y];
5836
5837     for (i = 0; i < NUM_BELTS; i++)
5838     {
5839       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5840       {
5841         int e_belt_nr = getBeltNrFromBeltElement(element);
5842         int belt_nr = i;
5843
5844         if (e_belt_nr == belt_nr)
5845         {
5846           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5847
5848           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5849         }
5850       }
5851     }
5852   }
5853 }
5854
5855 static void ToggleBeltSwitch(int x, int y)
5856 {
5857   static int belt_base_element[4] =
5858   {
5859     EL_CONVEYOR_BELT_1_LEFT,
5860     EL_CONVEYOR_BELT_2_LEFT,
5861     EL_CONVEYOR_BELT_3_LEFT,
5862     EL_CONVEYOR_BELT_4_LEFT
5863   };
5864   static int belt_base_active_element[4] =
5865   {
5866     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5867     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5870   };
5871   static int belt_base_switch_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5874     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5875     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5876     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5877   };
5878   static int belt_move_dir[4] =
5879   {
5880     MV_LEFT,
5881     MV_NONE,
5882     MV_RIGHT,
5883     MV_NONE,
5884   };
5885
5886   int element = Feld[x][y];
5887   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5888   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5889   int belt_dir = belt_move_dir[belt_dir_nr];
5890   int xx, yy, i;
5891
5892   if (!IS_BELT_SWITCH(element))
5893     return;
5894
5895   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5896   game.belt_dir[belt_nr] = belt_dir;
5897
5898   if (belt_dir_nr == 3)
5899     belt_dir_nr = 1;
5900
5901   /* set frame order for belt animation graphic according to belt direction */
5902   for (i = 0; i < NUM_BELT_PARTS; i++)
5903   {
5904     int element = belt_base_active_element[belt_nr] + i;
5905     int graphic_1 = el2img(element);
5906     int graphic_2 = el2panelimg(element);
5907
5908     if (belt_dir == MV_LEFT)
5909     {
5910       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5911       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5912     }
5913     else
5914     {
5915       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5916       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5917     }
5918   }
5919
5920   SCAN_PLAYFIELD(xx, yy)
5921   {
5922     int element = Feld[xx][yy];
5923
5924     if (IS_BELT_SWITCH(element))
5925     {
5926       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5927
5928       if (e_belt_nr == belt_nr)
5929       {
5930         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5931         DrawLevelField(xx, yy);
5932       }
5933     }
5934     else if (IS_BELT(element) && belt_dir != MV_NONE)
5935     {
5936       int e_belt_nr = getBeltNrFromBeltElement(element);
5937
5938       if (e_belt_nr == belt_nr)
5939       {
5940         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5941
5942         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5943         DrawLevelField(xx, yy);
5944       }
5945     }
5946     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5947     {
5948       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5949
5950       if (e_belt_nr == belt_nr)
5951       {
5952         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5953
5954         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5955         DrawLevelField(xx, yy);
5956       }
5957     }
5958   }
5959 }
5960
5961 static void ToggleSwitchgateSwitch(int x, int y)
5962 {
5963   int xx, yy;
5964
5965   game.switchgate_pos = !game.switchgate_pos;
5966
5967   SCAN_PLAYFIELD(xx, yy)
5968   {
5969     int element = Feld[xx][yy];
5970
5971 #if !USE_BOTH_SWITCHGATE_SWITCHES
5972     if (element == EL_SWITCHGATE_SWITCH_UP ||
5973         element == EL_SWITCHGATE_SWITCH_DOWN)
5974     {
5975       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5976       DrawLevelField(xx, yy);
5977     }
5978     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5979              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5980     {
5981       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5982       DrawLevelField(xx, yy);
5983     }
5984 #else
5985     if (element == EL_SWITCHGATE_SWITCH_UP)
5986     {
5987       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5988       DrawLevelField(xx, yy);
5989     }
5990     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5991     {
5992       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5993       DrawLevelField(xx, yy);
5994     }
5995     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5996     {
5997       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5998       DrawLevelField(xx, yy);
5999     }
6000     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6001     {
6002       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6003       DrawLevelField(xx, yy);
6004     }
6005 #endif
6006     else if (element == EL_SWITCHGATE_OPEN ||
6007              element == EL_SWITCHGATE_OPENING)
6008     {
6009       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6010
6011       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6012     }
6013     else if (element == EL_SWITCHGATE_CLOSED ||
6014              element == EL_SWITCHGATE_CLOSING)
6015     {
6016       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6017
6018       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6019     }
6020   }
6021 }
6022
6023 static int getInvisibleActiveFromInvisibleElement(int element)
6024 {
6025   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6026           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6027           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6028           element);
6029 }
6030
6031 static int getInvisibleFromInvisibleActiveElement(int element)
6032 {
6033   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6034           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6035           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6036           element);
6037 }
6038
6039 static void RedrawAllLightSwitchesAndInvisibleElements()
6040 {
6041   int x, y;
6042
6043   SCAN_PLAYFIELD(x, y)
6044   {
6045     int element = Feld[x][y];
6046
6047     if (element == EL_LIGHT_SWITCH &&
6048         game.light_time_left > 0)
6049     {
6050       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6051       DrawLevelField(x, y);
6052     }
6053     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6054              game.light_time_left == 0)
6055     {
6056       Feld[x][y] = EL_LIGHT_SWITCH;
6057       DrawLevelField(x, y);
6058     }
6059     else if (element == EL_EMC_DRIPPER &&
6060              game.light_time_left > 0)
6061     {
6062       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6063       DrawLevelField(x, y);
6064     }
6065     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6066              game.light_time_left == 0)
6067     {
6068       Feld[x][y] = EL_EMC_DRIPPER;
6069       DrawLevelField(x, y);
6070     }
6071     else if (element == EL_INVISIBLE_STEELWALL ||
6072              element == EL_INVISIBLE_WALL ||
6073              element == EL_INVISIBLE_SAND)
6074     {
6075       if (game.light_time_left > 0)
6076         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6077
6078       DrawLevelField(x, y);
6079
6080       /* uncrumble neighbour fields, if needed */
6081       if (element == EL_INVISIBLE_SAND)
6082         DrawLevelFieldCrumbledSandNeighbours(x, y);
6083     }
6084     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6085              element == EL_INVISIBLE_WALL_ACTIVE ||
6086              element == EL_INVISIBLE_SAND_ACTIVE)
6087     {
6088       if (game.light_time_left == 0)
6089         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6090
6091       DrawLevelField(x, y);
6092
6093       /* re-crumble neighbour fields, if needed */
6094       if (element == EL_INVISIBLE_SAND)
6095         DrawLevelFieldCrumbledSandNeighbours(x, y);
6096     }
6097   }
6098 }
6099
6100 static void RedrawAllInvisibleElementsForLenses()
6101 {
6102   int x, y;
6103
6104   SCAN_PLAYFIELD(x, y)
6105   {
6106     int element = Feld[x][y];
6107
6108     if (element == EL_EMC_DRIPPER &&
6109         game.lenses_time_left > 0)
6110     {
6111       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6112       DrawLevelField(x, y);
6113     }
6114     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6115              game.lenses_time_left == 0)
6116     {
6117       Feld[x][y] = EL_EMC_DRIPPER;
6118       DrawLevelField(x, y);
6119     }
6120     else if (element == EL_INVISIBLE_STEELWALL ||
6121              element == EL_INVISIBLE_WALL ||
6122              element == EL_INVISIBLE_SAND)
6123     {
6124       if (game.lenses_time_left > 0)
6125         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6126
6127       DrawLevelField(x, y);
6128
6129       /* uncrumble neighbour fields, if needed */
6130       if (element == EL_INVISIBLE_SAND)
6131         DrawLevelFieldCrumbledSandNeighbours(x, y);
6132     }
6133     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6134              element == EL_INVISIBLE_WALL_ACTIVE ||
6135              element == EL_INVISIBLE_SAND_ACTIVE)
6136     {
6137       if (game.lenses_time_left == 0)
6138         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6139
6140       DrawLevelField(x, y);
6141
6142       /* re-crumble neighbour fields, if needed */
6143       if (element == EL_INVISIBLE_SAND)
6144         DrawLevelFieldCrumbledSandNeighbours(x, y);
6145     }
6146   }
6147 }
6148
6149 static void RedrawAllInvisibleElementsForMagnifier()
6150 {
6151   int x, y;
6152
6153   SCAN_PLAYFIELD(x, y)
6154   {
6155     int element = Feld[x][y];
6156
6157     if (element == EL_EMC_FAKE_GRASS &&
6158         game.magnify_time_left > 0)
6159     {
6160       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6161       DrawLevelField(x, y);
6162     }
6163     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6164              game.magnify_time_left == 0)
6165     {
6166       Feld[x][y] = EL_EMC_FAKE_GRASS;
6167       DrawLevelField(x, y);
6168     }
6169     else if (IS_GATE_GRAY(element) &&
6170              game.magnify_time_left > 0)
6171     {
6172       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6173                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6174                     IS_EM_GATE_GRAY(element) ?
6175                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6176                     IS_EMC_GATE_GRAY(element) ?
6177                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6178                     element);
6179       DrawLevelField(x, y);
6180     }
6181     else if (IS_GATE_GRAY_ACTIVE(element) &&
6182              game.magnify_time_left == 0)
6183     {
6184       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6185                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6186                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6187                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6188                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6189                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6190                     element);
6191       DrawLevelField(x, y);
6192     }
6193   }
6194 }
6195
6196 static void ToggleLightSwitch(int x, int y)
6197 {
6198   int element = Feld[x][y];
6199
6200   game.light_time_left =
6201     (element == EL_LIGHT_SWITCH ?
6202      level.time_light * FRAMES_PER_SECOND : 0);
6203
6204   RedrawAllLightSwitchesAndInvisibleElements();
6205 }
6206
6207 static void ActivateTimegateSwitch(int x, int y)
6208 {
6209   int xx, yy;
6210
6211   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6212
6213   SCAN_PLAYFIELD(xx, yy)
6214   {
6215     int element = Feld[xx][yy];
6216
6217     if (element == EL_TIMEGATE_CLOSED ||
6218         element == EL_TIMEGATE_CLOSING)
6219     {
6220       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6221       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6222     }
6223
6224     /*
6225     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6226     {
6227       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6228       DrawLevelField(xx, yy);
6229     }
6230     */
6231
6232   }
6233
6234 #if 1
6235   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6236                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6237 #else
6238   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6239 #endif
6240 }
6241
6242 void Impact(int x, int y)
6243 {
6244   boolean last_line = (y == lev_fieldy - 1);
6245   boolean object_hit = FALSE;
6246   boolean impact = (last_line || object_hit);
6247   int element = Feld[x][y];
6248   int smashed = EL_STEELWALL;
6249
6250   if (!last_line)       /* check if element below was hit */
6251   {
6252     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6253       return;
6254
6255     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6256                                          MovDir[x][y + 1] != MV_DOWN ||
6257                                          MovPos[x][y + 1] <= TILEY / 2));
6258
6259     /* do not smash moving elements that left the smashed field in time */
6260     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6261         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6262       object_hit = FALSE;
6263
6264 #if USE_QUICKSAND_IMPACT_BUGFIX
6265     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6266     {
6267       RemoveMovingField(x, y + 1);
6268       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6269       Feld[x][y + 2] = EL_ROCK;
6270       DrawLevelField(x, y + 2);
6271
6272       object_hit = TRUE;
6273     }
6274
6275     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6276     {
6277       RemoveMovingField(x, y + 1);
6278       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6279       Feld[x][y + 2] = EL_ROCK;
6280       DrawLevelField(x, y + 2);
6281
6282       object_hit = TRUE;
6283     }
6284 #endif
6285
6286     if (object_hit)
6287       smashed = MovingOrBlocked2Element(x, y + 1);
6288
6289     impact = (last_line || object_hit);
6290   }
6291
6292   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6293   {
6294     SplashAcid(x, y + 1);
6295     return;
6296   }
6297
6298   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6299   /* only reset graphic animation if graphic really changes after impact */
6300   if (impact &&
6301       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6302   {
6303     ResetGfxAnimation(x, y);
6304     DrawLevelField(x, y);
6305   }
6306
6307   if (impact && CAN_EXPLODE_IMPACT(element))
6308   {
6309     Bang(x, y);
6310     return;
6311   }
6312   else if (impact && element == EL_PEARL &&
6313            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6314   {
6315     ResetGfxAnimation(x, y);
6316
6317     Feld[x][y] = EL_PEARL_BREAKING;
6318     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6319     return;
6320   }
6321   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6322   {
6323     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6324
6325     return;
6326   }
6327
6328   if (impact && element == EL_AMOEBA_DROP)
6329   {
6330     if (object_hit && IS_PLAYER(x, y + 1))
6331       KillPlayerUnlessEnemyProtected(x, y + 1);
6332     else if (object_hit && smashed == EL_PENGUIN)
6333       Bang(x, y + 1);
6334     else
6335     {
6336       Feld[x][y] = EL_AMOEBA_GROWING;
6337       Store[x][y] = EL_AMOEBA_WET;
6338
6339       ResetRandomAnimationValue(x, y);
6340     }
6341     return;
6342   }
6343
6344   if (object_hit)               /* check which object was hit */
6345   {
6346     if ((CAN_PASS_MAGIC_WALL(element) && 
6347          (smashed == EL_MAGIC_WALL ||
6348           smashed == EL_BD_MAGIC_WALL)) ||
6349         (CAN_PASS_DC_MAGIC_WALL(element) &&
6350          smashed == EL_DC_MAGIC_WALL))
6351     {
6352       int xx, yy;
6353       int activated_magic_wall =
6354         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6355          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6356          EL_DC_MAGIC_WALL_ACTIVE);
6357
6358       /* activate magic wall / mill */
6359       SCAN_PLAYFIELD(xx, yy)
6360       {
6361         if (Feld[xx][yy] == smashed)
6362           Feld[xx][yy] = activated_magic_wall;
6363       }
6364
6365       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6366       game.magic_wall_active = TRUE;
6367
6368       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6369                             SND_MAGIC_WALL_ACTIVATING :
6370                             smashed == EL_BD_MAGIC_WALL ?
6371                             SND_BD_MAGIC_WALL_ACTIVATING :
6372                             SND_DC_MAGIC_WALL_ACTIVATING));
6373     }
6374
6375     if (IS_PLAYER(x, y + 1))
6376     {
6377       if (CAN_SMASH_PLAYER(element))
6378       {
6379         KillPlayerUnlessEnemyProtected(x, y + 1);
6380         return;
6381       }
6382     }
6383     else if (smashed == EL_PENGUIN)
6384     {
6385       if (CAN_SMASH_PLAYER(element))
6386       {
6387         Bang(x, y + 1);
6388         return;
6389       }
6390     }
6391     else if (element == EL_BD_DIAMOND)
6392     {
6393       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6394       {
6395         Bang(x, y + 1);
6396         return;
6397       }
6398     }
6399     else if (((element == EL_SP_INFOTRON ||
6400                element == EL_SP_ZONK) &&
6401               (smashed == EL_SP_SNIKSNAK ||
6402                smashed == EL_SP_ELECTRON ||
6403                smashed == EL_SP_DISK_ORANGE)) ||
6404              (element == EL_SP_INFOTRON &&
6405               smashed == EL_SP_DISK_YELLOW))
6406     {
6407       Bang(x, y + 1);
6408       return;
6409     }
6410     else if (CAN_SMASH_EVERYTHING(element))
6411     {
6412       if (IS_CLASSIC_ENEMY(smashed) ||
6413           CAN_EXPLODE_SMASHED(smashed))
6414       {
6415         Bang(x, y + 1);
6416         return;
6417       }
6418       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6419       {
6420         if (smashed == EL_LAMP ||
6421             smashed == EL_LAMP_ACTIVE)
6422         {
6423           Bang(x, y + 1);
6424           return;
6425         }
6426         else if (smashed == EL_NUT)
6427         {
6428           Feld[x][y + 1] = EL_NUT_BREAKING;
6429           PlayLevelSound(x, y, SND_NUT_BREAKING);
6430           RaiseScoreElement(EL_NUT);
6431           return;
6432         }
6433         else if (smashed == EL_PEARL)
6434         {
6435           ResetGfxAnimation(x, y);
6436
6437           Feld[x][y + 1] = EL_PEARL_BREAKING;
6438           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6439           return;
6440         }
6441         else if (smashed == EL_DIAMOND)
6442         {
6443           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6444           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6445           return;
6446         }
6447         else if (IS_BELT_SWITCH(smashed))
6448         {
6449           ToggleBeltSwitch(x, y + 1);
6450         }
6451         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6452                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6453                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6454                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6455         {
6456           ToggleSwitchgateSwitch(x, y + 1);
6457         }
6458         else if (smashed == EL_LIGHT_SWITCH ||
6459                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6460         {
6461           ToggleLightSwitch(x, y + 1);
6462         }
6463         else
6464         {
6465 #if 0
6466           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6467 #endif
6468
6469           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6470
6471           CheckElementChangeBySide(x, y + 1, smashed, element,
6472                                    CE_SWITCHED, CH_SIDE_TOP);
6473           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6474                                             CH_SIDE_TOP);
6475         }
6476       }
6477       else
6478       {
6479         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6480       }
6481     }
6482   }
6483
6484   /* play sound of magic wall / mill */
6485   if (!last_line &&
6486       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6487        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6488        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6489   {
6490     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6491       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6492     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6493       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6494     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6495       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6496
6497     return;
6498   }
6499
6500   /* play sound of object that hits the ground */
6501   if (last_line || object_hit)
6502     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6503 }
6504
6505 inline static void TurnRoundExt(int x, int y)
6506 {
6507   static struct
6508   {
6509     int dx, dy;
6510   } move_xy[] =
6511   {
6512     {  0,  0 },
6513     { -1,  0 },
6514     { +1,  0 },
6515     {  0,  0 },
6516     {  0, -1 },
6517     {  0,  0 }, { 0, 0 }, { 0, 0 },
6518     {  0, +1 }
6519   };
6520   static struct
6521   {
6522     int left, right, back;
6523   } turn[] =
6524   {
6525     { 0,        0,              0        },
6526     { MV_DOWN,  MV_UP,          MV_RIGHT },
6527     { MV_UP,    MV_DOWN,        MV_LEFT  },
6528     { 0,        0,              0        },
6529     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6530     { 0,        0,              0        },
6531     { 0,        0,              0        },
6532     { 0,        0,              0        },
6533     { MV_RIGHT, MV_LEFT,        MV_UP    }
6534   };
6535
6536   int element = Feld[x][y];
6537   int move_pattern = element_info[element].move_pattern;
6538
6539   int old_move_dir = MovDir[x][y];
6540   int left_dir  = turn[old_move_dir].left;
6541   int right_dir = turn[old_move_dir].right;
6542   int back_dir  = turn[old_move_dir].back;
6543
6544   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6545   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6546   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6547   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6548
6549   int left_x  = x + left_dx,  left_y  = y + left_dy;
6550   int right_x = x + right_dx, right_y = y + right_dy;
6551   int move_x  = x + move_dx,  move_y  = y + move_dy;
6552
6553   int xx, yy;
6554
6555   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6556   {
6557     TestIfBadThingTouchesOtherBadThing(x, y);
6558
6559     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6560       MovDir[x][y] = right_dir;
6561     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6562       MovDir[x][y] = left_dir;
6563
6564     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6565       MovDelay[x][y] = 9;
6566     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6567       MovDelay[x][y] = 1;
6568   }
6569   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6570   {
6571     TestIfBadThingTouchesOtherBadThing(x, y);
6572
6573     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6574       MovDir[x][y] = left_dir;
6575     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6576       MovDir[x][y] = right_dir;
6577
6578     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6579       MovDelay[x][y] = 9;
6580     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6581       MovDelay[x][y] = 1;
6582   }
6583   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6584   {
6585     TestIfBadThingTouchesOtherBadThing(x, y);
6586
6587     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6588       MovDir[x][y] = left_dir;
6589     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6590       MovDir[x][y] = right_dir;
6591
6592     if (MovDir[x][y] != old_move_dir)
6593       MovDelay[x][y] = 9;
6594   }
6595   else if (element == EL_YAMYAM)
6596   {
6597     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6598     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6599
6600     if (can_turn_left && can_turn_right)
6601       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6602     else if (can_turn_left)
6603       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6604     else if (can_turn_right)
6605       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6606     else
6607       MovDir[x][y] = back_dir;
6608
6609     MovDelay[x][y] = 16 + 16 * RND(3);
6610   }
6611   else if (element == EL_DARK_YAMYAM)
6612   {
6613     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6614                                                          left_x, left_y);
6615     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6616                                                          right_x, right_y);
6617
6618     if (can_turn_left && can_turn_right)
6619       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6620     else if (can_turn_left)
6621       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6622     else if (can_turn_right)
6623       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6624     else
6625       MovDir[x][y] = back_dir;
6626
6627     MovDelay[x][y] = 16 + 16 * RND(3);
6628   }
6629   else if (element == EL_PACMAN)
6630   {
6631     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6632     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6633
6634     if (can_turn_left && can_turn_right)
6635       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6636     else if (can_turn_left)
6637       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6638     else if (can_turn_right)
6639       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6640     else
6641       MovDir[x][y] = back_dir;
6642
6643     MovDelay[x][y] = 6 + RND(40);
6644   }
6645   else if (element == EL_PIG)
6646   {
6647     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6648     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6649     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6650     boolean should_turn_left, should_turn_right, should_move_on;
6651     int rnd_value = 24;
6652     int rnd = RND(rnd_value);
6653
6654     should_turn_left = (can_turn_left &&
6655                         (!can_move_on ||
6656                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6657                                                    y + back_dy + left_dy)));
6658     should_turn_right = (can_turn_right &&
6659                          (!can_move_on ||
6660                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6661                                                     y + back_dy + right_dy)));
6662     should_move_on = (can_move_on &&
6663                       (!can_turn_left ||
6664                        !can_turn_right ||
6665                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6666                                                  y + move_dy + left_dy) ||
6667                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6668                                                  y + move_dy + right_dy)));
6669
6670     if (should_turn_left || should_turn_right || should_move_on)
6671     {
6672       if (should_turn_left && should_turn_right && should_move_on)
6673         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6674                         rnd < 2 * rnd_value / 3 ? right_dir :
6675                         old_move_dir);
6676       else if (should_turn_left && should_turn_right)
6677         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6678       else if (should_turn_left && should_move_on)
6679         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6680       else if (should_turn_right && should_move_on)
6681         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6682       else if (should_turn_left)
6683         MovDir[x][y] = left_dir;
6684       else if (should_turn_right)
6685         MovDir[x][y] = right_dir;
6686       else if (should_move_on)
6687         MovDir[x][y] = old_move_dir;
6688     }
6689     else if (can_move_on && rnd > rnd_value / 8)
6690       MovDir[x][y] = old_move_dir;
6691     else if (can_turn_left && can_turn_right)
6692       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6693     else if (can_turn_left && rnd > rnd_value / 8)
6694       MovDir[x][y] = left_dir;
6695     else if (can_turn_right && rnd > rnd_value/8)
6696       MovDir[x][y] = right_dir;
6697     else
6698       MovDir[x][y] = back_dir;
6699
6700     xx = x + move_xy[MovDir[x][y]].dx;
6701     yy = y + move_xy[MovDir[x][y]].dy;
6702
6703     if (!IN_LEV_FIELD(xx, yy) ||
6704         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6705       MovDir[x][y] = old_move_dir;
6706
6707     MovDelay[x][y] = 0;
6708   }
6709   else if (element == EL_DRAGON)
6710   {
6711     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6712     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6713     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6714     int rnd_value = 24;
6715     int rnd = RND(rnd_value);
6716
6717     if (can_move_on && rnd > rnd_value / 8)
6718       MovDir[x][y] = old_move_dir;
6719     else if (can_turn_left && can_turn_right)
6720       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6721     else if (can_turn_left && rnd > rnd_value / 8)
6722       MovDir[x][y] = left_dir;
6723     else if (can_turn_right && rnd > rnd_value / 8)
6724       MovDir[x][y] = right_dir;
6725     else
6726       MovDir[x][y] = back_dir;
6727
6728     xx = x + move_xy[MovDir[x][y]].dx;
6729     yy = y + move_xy[MovDir[x][y]].dy;
6730
6731     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6732       MovDir[x][y] = old_move_dir;
6733
6734     MovDelay[x][y] = 0;
6735   }
6736   else if (element == EL_MOLE)
6737   {
6738     boolean can_move_on =
6739       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6740                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6741                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6742     if (!can_move_on)
6743     {
6744       boolean can_turn_left =
6745         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6746                               IS_AMOEBOID(Feld[left_x][left_y])));
6747
6748       boolean can_turn_right =
6749         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6750                               IS_AMOEBOID(Feld[right_x][right_y])));
6751
6752       if (can_turn_left && can_turn_right)
6753         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6754       else if (can_turn_left)
6755         MovDir[x][y] = left_dir;
6756       else
6757         MovDir[x][y] = right_dir;
6758     }
6759
6760     if (MovDir[x][y] != old_move_dir)
6761       MovDelay[x][y] = 9;
6762   }
6763   else if (element == EL_BALLOON)
6764   {
6765     MovDir[x][y] = game.wind_direction;
6766     MovDelay[x][y] = 0;
6767   }
6768   else if (element == EL_SPRING)
6769   {
6770 #if USE_NEW_SPRING_BUMPER
6771     if (MovDir[x][y] & MV_HORIZONTAL)
6772     {
6773       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6774           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6775       {
6776         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6777         ResetGfxAnimation(move_x, move_y);
6778         DrawLevelField(move_x, move_y);
6779
6780         MovDir[x][y] = back_dir;
6781       }
6782       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6783                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6784         MovDir[x][y] = MV_NONE;
6785     }
6786 #else
6787     if (MovDir[x][y] & MV_HORIZONTAL &&
6788         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6789          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6790       MovDir[x][y] = MV_NONE;
6791 #endif
6792
6793     MovDelay[x][y] = 0;
6794   }
6795   else if (element == EL_ROBOT ||
6796            element == EL_SATELLITE ||
6797            element == EL_PENGUIN ||
6798            element == EL_EMC_ANDROID)
6799   {
6800     int attr_x = -1, attr_y = -1;
6801
6802     if (AllPlayersGone)
6803     {
6804       attr_x = ExitX;
6805       attr_y = ExitY;
6806     }
6807     else
6808     {
6809       int i;
6810
6811       for (i = 0; i < MAX_PLAYERS; i++)
6812       {
6813         struct PlayerInfo *player = &stored_player[i];
6814         int jx = player->jx, jy = player->jy;
6815
6816         if (!player->active)
6817           continue;
6818
6819         if (attr_x == -1 ||
6820             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6821         {
6822           attr_x = jx;
6823           attr_y = jy;
6824         }
6825       }
6826     }
6827
6828     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6829         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6830          game.engine_version < VERSION_IDENT(3,1,0,0)))
6831     {
6832       attr_x = ZX;
6833       attr_y = ZY;
6834     }
6835
6836     if (element == EL_PENGUIN)
6837     {
6838       int i;
6839       static int xy[4][2] =
6840       {
6841         { 0, -1 },
6842         { -1, 0 },
6843         { +1, 0 },
6844         { 0, +1 }
6845       };
6846
6847       for (i = 0; i < NUM_DIRECTIONS; i++)
6848       {
6849         int ex = x + xy[i][0];
6850         int ey = y + xy[i][1];
6851
6852         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6853                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6854                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6855                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6856         {
6857           attr_x = ex;
6858           attr_y = ey;
6859           break;
6860         }
6861       }
6862     }
6863
6864     MovDir[x][y] = MV_NONE;
6865     if (attr_x < x)
6866       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6867     else if (attr_x > x)
6868       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6869     if (attr_y < y)
6870       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6871     else if (attr_y > y)
6872       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6873
6874     if (element == EL_ROBOT)
6875     {
6876       int newx, newy;
6877
6878       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6879         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6880       Moving2Blocked(x, y, &newx, &newy);
6881
6882       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6883         MovDelay[x][y] = 8 + 8 * !RND(3);
6884       else
6885         MovDelay[x][y] = 16;
6886     }
6887     else if (element == EL_PENGUIN)
6888     {
6889       int newx, newy;
6890
6891       MovDelay[x][y] = 1;
6892
6893       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6894       {
6895         boolean first_horiz = RND(2);
6896         int new_move_dir = MovDir[x][y];
6897
6898         MovDir[x][y] =
6899           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6900         Moving2Blocked(x, y, &newx, &newy);
6901
6902         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6903           return;
6904
6905         MovDir[x][y] =
6906           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6907         Moving2Blocked(x, y, &newx, &newy);
6908
6909         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6910           return;
6911
6912         MovDir[x][y] = old_move_dir;
6913         return;
6914       }
6915     }
6916     else if (element == EL_SATELLITE)
6917     {
6918       int newx, newy;
6919
6920       MovDelay[x][y] = 1;
6921
6922       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6923       {
6924         boolean first_horiz = RND(2);
6925         int new_move_dir = MovDir[x][y];
6926
6927         MovDir[x][y] =
6928           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6929         Moving2Blocked(x, y, &newx, &newy);
6930
6931         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6932           return;
6933
6934         MovDir[x][y] =
6935           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6936         Moving2Blocked(x, y, &newx, &newy);
6937
6938         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6939           return;
6940
6941         MovDir[x][y] = old_move_dir;
6942         return;
6943       }
6944     }
6945     else if (element == EL_EMC_ANDROID)
6946     {
6947       static int check_pos[16] =
6948       {
6949         -1,             /*  0 => (invalid)          */
6950         7,              /*  1 => MV_LEFT            */
6951         3,              /*  2 => MV_RIGHT           */
6952         -1,             /*  3 => (invalid)          */
6953         1,              /*  4 =>            MV_UP   */
6954         0,              /*  5 => MV_LEFT  | MV_UP   */
6955         2,              /*  6 => MV_RIGHT | MV_UP   */
6956         -1,             /*  7 => (invalid)          */
6957         5,              /*  8 =>            MV_DOWN */
6958         6,              /*  9 => MV_LEFT  | MV_DOWN */
6959         4,              /* 10 => MV_RIGHT | MV_DOWN */
6960         -1,             /* 11 => (invalid)          */
6961         -1,             /* 12 => (invalid)          */
6962         -1,             /* 13 => (invalid)          */
6963         -1,             /* 14 => (invalid)          */
6964         -1,             /* 15 => (invalid)          */
6965       };
6966       static struct
6967       {
6968         int dx, dy;
6969         int dir;
6970       } check_xy[8] =
6971       {
6972         { -1, -1,       MV_LEFT  | MV_UP   },
6973         {  0, -1,                  MV_UP   },
6974         { +1, -1,       MV_RIGHT | MV_UP   },
6975         { +1,  0,       MV_RIGHT           },
6976         { +1, +1,       MV_RIGHT | MV_DOWN },
6977         {  0, +1,                  MV_DOWN },
6978         { -1, +1,       MV_LEFT  | MV_DOWN },
6979         { -1,  0,       MV_LEFT            },
6980       };
6981       int start_pos, check_order;
6982       boolean can_clone = FALSE;
6983       int i;
6984
6985       /* check if there is any free field around current position */
6986       for (i = 0; i < 8; i++)
6987       {
6988         int newx = x + check_xy[i].dx;
6989         int newy = y + check_xy[i].dy;
6990
6991         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6992         {
6993           can_clone = TRUE;
6994
6995           break;
6996         }
6997       }
6998
6999       if (can_clone)            /* randomly find an element to clone */
7000       {
7001         can_clone = FALSE;
7002
7003         start_pos = check_pos[RND(8)];
7004         check_order = (RND(2) ? -1 : +1);
7005
7006         for (i = 0; i < 8; i++)
7007         {
7008           int pos_raw = start_pos + i * check_order;
7009           int pos = (pos_raw + 8) % 8;
7010           int newx = x + check_xy[pos].dx;
7011           int newy = y + check_xy[pos].dy;
7012
7013           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7014           {
7015             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7016             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7017
7018             Store[x][y] = Feld[newx][newy];
7019
7020             can_clone = TRUE;
7021
7022             break;
7023           }
7024         }
7025       }
7026
7027       if (can_clone)            /* randomly find a direction to move */
7028       {
7029         can_clone = FALSE;
7030
7031         start_pos = check_pos[RND(8)];
7032         check_order = (RND(2) ? -1 : +1);
7033
7034         for (i = 0; i < 8; i++)
7035         {
7036           int pos_raw = start_pos + i * check_order;
7037           int pos = (pos_raw + 8) % 8;
7038           int newx = x + check_xy[pos].dx;
7039           int newy = y + check_xy[pos].dy;
7040           int new_move_dir = check_xy[pos].dir;
7041
7042           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7043           {
7044             MovDir[x][y] = new_move_dir;
7045             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7046
7047             can_clone = TRUE;
7048
7049             break;
7050           }
7051         }
7052       }
7053
7054       if (can_clone)            /* cloning and moving successful */
7055         return;
7056
7057       /* cannot clone -- try to move towards player */
7058
7059       start_pos = check_pos[MovDir[x][y] & 0x0f];
7060       check_order = (RND(2) ? -1 : +1);
7061
7062       for (i = 0; i < 3; i++)
7063       {
7064         /* first check start_pos, then previous/next or (next/previous) pos */
7065         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7066         int pos = (pos_raw + 8) % 8;
7067         int newx = x + check_xy[pos].dx;
7068         int newy = y + check_xy[pos].dy;
7069         int new_move_dir = check_xy[pos].dir;
7070
7071         if (IS_PLAYER(newx, newy))
7072           break;
7073
7074         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7075         {
7076           MovDir[x][y] = new_move_dir;
7077           MovDelay[x][y] = level.android_move_time * 8 + 1;
7078
7079           break;
7080         }
7081       }
7082     }
7083   }
7084   else if (move_pattern == MV_TURNING_LEFT ||
7085            move_pattern == MV_TURNING_RIGHT ||
7086            move_pattern == MV_TURNING_LEFT_RIGHT ||
7087            move_pattern == MV_TURNING_RIGHT_LEFT ||
7088            move_pattern == MV_TURNING_RANDOM ||
7089            move_pattern == MV_ALL_DIRECTIONS)
7090   {
7091     boolean can_turn_left =
7092       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7093     boolean can_turn_right =
7094       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7095
7096     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7097       return;
7098
7099     if (move_pattern == MV_TURNING_LEFT)
7100       MovDir[x][y] = left_dir;
7101     else if (move_pattern == MV_TURNING_RIGHT)
7102       MovDir[x][y] = right_dir;
7103     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7104       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7105     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7106       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7107     else if (move_pattern == MV_TURNING_RANDOM)
7108       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7109                       can_turn_right && !can_turn_left ? right_dir :
7110                       RND(2) ? left_dir : right_dir);
7111     else if (can_turn_left && can_turn_right)
7112       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7113     else if (can_turn_left)
7114       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7115     else if (can_turn_right)
7116       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7117     else
7118       MovDir[x][y] = back_dir;
7119
7120     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7121   }
7122   else if (move_pattern == MV_HORIZONTAL ||
7123            move_pattern == MV_VERTICAL)
7124   {
7125     if (move_pattern & old_move_dir)
7126       MovDir[x][y] = back_dir;
7127     else if (move_pattern == MV_HORIZONTAL)
7128       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7129     else if (move_pattern == MV_VERTICAL)
7130       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7131
7132     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133   }
7134   else if (move_pattern & MV_ANY_DIRECTION)
7135   {
7136     MovDir[x][y] = move_pattern;
7137     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138   }
7139   else if (move_pattern & MV_WIND_DIRECTION)
7140   {
7141     MovDir[x][y] = game.wind_direction;
7142     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143   }
7144   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7145   {
7146     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7147       MovDir[x][y] = left_dir;
7148     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7149       MovDir[x][y] = right_dir;
7150
7151     if (MovDir[x][y] != old_move_dir)
7152       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153   }
7154   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7155   {
7156     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7157       MovDir[x][y] = right_dir;
7158     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7159       MovDir[x][y] = left_dir;
7160
7161     if (MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7163   }
7164   else if (move_pattern == MV_TOWARDS_PLAYER ||
7165            move_pattern == MV_AWAY_FROM_PLAYER)
7166   {
7167     int attr_x = -1, attr_y = -1;
7168     int newx, newy;
7169     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7170
7171     if (AllPlayersGone)
7172     {
7173       attr_x = ExitX;
7174       attr_y = ExitY;
7175     }
7176     else
7177     {
7178       int i;
7179
7180       for (i = 0; i < MAX_PLAYERS; i++)
7181       {
7182         struct PlayerInfo *player = &stored_player[i];
7183         int jx = player->jx, jy = player->jy;
7184
7185         if (!player->active)
7186           continue;
7187
7188         if (attr_x == -1 ||
7189             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7190         {
7191           attr_x = jx;
7192           attr_y = jy;
7193         }
7194       }
7195     }
7196
7197     MovDir[x][y] = MV_NONE;
7198     if (attr_x < x)
7199       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7200     else if (attr_x > x)
7201       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7202     if (attr_y < y)
7203       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7204     else if (attr_y > y)
7205       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7206
7207     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7208
7209     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7210     {
7211       boolean first_horiz = RND(2);
7212       int new_move_dir = MovDir[x][y];
7213
7214       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7215       {
7216         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7217         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7218
7219         return;
7220       }
7221
7222       MovDir[x][y] =
7223         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7224       Moving2Blocked(x, y, &newx, &newy);
7225
7226       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7227         return;
7228
7229       MovDir[x][y] =
7230         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7231       Moving2Blocked(x, y, &newx, &newy);
7232
7233       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7234         return;
7235
7236       MovDir[x][y] = old_move_dir;
7237     }
7238   }
7239   else if (move_pattern == MV_WHEN_PUSHED ||
7240            move_pattern == MV_WHEN_DROPPED)
7241   {
7242     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7243       MovDir[x][y] = MV_NONE;
7244
7245     MovDelay[x][y] = 0;
7246   }
7247   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7248   {
7249     static int test_xy[7][2] =
7250     {
7251       { 0, -1 },
7252       { -1, 0 },
7253       { +1, 0 },
7254       { 0, +1 },
7255       { 0, -1 },
7256       { -1, 0 },
7257       { +1, 0 },
7258     };
7259     static int test_dir[7] =
7260     {
7261       MV_UP,
7262       MV_LEFT,
7263       MV_RIGHT,
7264       MV_DOWN,
7265       MV_UP,
7266       MV_LEFT,
7267       MV_RIGHT,
7268     };
7269     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7270     int move_preference = -1000000;     /* start with very low preference */
7271     int new_move_dir = MV_NONE;
7272     int start_test = RND(4);
7273     int i;
7274
7275     for (i = 0; i < NUM_DIRECTIONS; i++)
7276     {
7277       int move_dir = test_dir[start_test + i];
7278       int move_dir_preference;
7279
7280       xx = x + test_xy[start_test + i][0];
7281       yy = y + test_xy[start_test + i][1];
7282
7283       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7284           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7285       {
7286         new_move_dir = move_dir;
7287
7288         break;
7289       }
7290
7291       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7292         continue;
7293
7294       move_dir_preference = -1 * RunnerVisit[xx][yy];
7295       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7296         move_dir_preference = PlayerVisit[xx][yy];
7297
7298       if (move_dir_preference > move_preference)
7299       {
7300         /* prefer field that has not been visited for the longest time */
7301         move_preference = move_dir_preference;
7302         new_move_dir = move_dir;
7303       }
7304       else if (move_dir_preference == move_preference &&
7305                move_dir == old_move_dir)
7306       {
7307         /* prefer last direction when all directions are preferred equally */
7308         move_preference = move_dir_preference;
7309         new_move_dir = move_dir;
7310       }
7311     }
7312
7313     MovDir[x][y] = new_move_dir;
7314     if (old_move_dir != new_move_dir)
7315       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317 }
7318
7319 static void TurnRound(int x, int y)
7320 {
7321   int direction = MovDir[x][y];
7322
7323   TurnRoundExt(x, y);
7324
7325   GfxDir[x][y] = MovDir[x][y];
7326
7327   if (direction != MovDir[x][y])
7328     GfxFrame[x][y] = 0;
7329
7330   if (MovDelay[x][y])
7331     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7332
7333   ResetGfxFrame(x, y, FALSE);
7334 }
7335
7336 static boolean JustBeingPushed(int x, int y)
7337 {
7338   int i;
7339
7340   for (i = 0; i < MAX_PLAYERS; i++)
7341   {
7342     struct PlayerInfo *player = &stored_player[i];
7343
7344     if (player->active && player->is_pushing && player->MovPos)
7345     {
7346       int next_jx = player->jx + (player->jx - player->last_jx);
7347       int next_jy = player->jy + (player->jy - player->last_jy);
7348
7349       if (x == next_jx && y == next_jy)
7350         return TRUE;
7351     }
7352   }
7353
7354   return FALSE;
7355 }
7356
7357 void StartMoving(int x, int y)
7358 {
7359   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7360   int element = Feld[x][y];
7361
7362   if (Stop[x][y])
7363     return;
7364
7365   if (MovDelay[x][y] == 0)
7366     GfxAction[x][y] = ACTION_DEFAULT;
7367
7368   if (CAN_FALL(element) && y < lev_fieldy - 1)
7369   {
7370     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7371         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7372       if (JustBeingPushed(x, y))
7373         return;
7374
7375     if (element == EL_QUICKSAND_FULL)
7376     {
7377       if (IS_FREE(x, y + 1))
7378       {
7379         InitMovingField(x, y, MV_DOWN);
7380         started_moving = TRUE;
7381
7382         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7383 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7384         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7385           Store[x][y] = EL_ROCK;
7386 #else
7387         Store[x][y] = EL_ROCK;
7388 #endif
7389
7390         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7391       }
7392       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7393       {
7394         if (!MovDelay[x][y])
7395           MovDelay[x][y] = TILEY + 1;
7396
7397         if (MovDelay[x][y])
7398         {
7399           MovDelay[x][y]--;
7400           if (MovDelay[x][y])
7401             return;
7402         }
7403
7404         Feld[x][y] = EL_QUICKSAND_EMPTY;
7405         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7406         Store[x][y + 1] = Store[x][y];
7407         Store[x][y] = 0;
7408
7409         PlayLevelSoundAction(x, y, ACTION_FILLING);
7410       }
7411     }
7412     else if (element == EL_QUICKSAND_FAST_FULL)
7413     {
7414       if (IS_FREE(x, y + 1))
7415       {
7416         InitMovingField(x, y, MV_DOWN);
7417         started_moving = TRUE;
7418
7419         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7420 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7421         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7422           Store[x][y] = EL_ROCK;
7423 #else
7424         Store[x][y] = EL_ROCK;
7425 #endif
7426
7427         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7428       }
7429       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7430       {
7431         if (!MovDelay[x][y])
7432           MovDelay[x][y] = TILEY + 1;
7433
7434         if (MovDelay[x][y])
7435         {
7436           MovDelay[x][y]--;
7437           if (MovDelay[x][y])
7438             return;
7439         }
7440
7441         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7442         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7443         Store[x][y + 1] = Store[x][y];
7444         Store[x][y] = 0;
7445
7446         PlayLevelSoundAction(x, y, ACTION_FILLING);
7447       }
7448     }
7449     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7450              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7451     {
7452       InitMovingField(x, y, MV_DOWN);
7453       started_moving = TRUE;
7454
7455       Feld[x][y] = EL_QUICKSAND_FILLING;
7456       Store[x][y] = element;
7457
7458       PlayLevelSoundAction(x, y, ACTION_FILLING);
7459     }
7460     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7461              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7462     {
7463       InitMovingField(x, y, MV_DOWN);
7464       started_moving = TRUE;
7465
7466       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7467       Store[x][y] = element;
7468
7469       PlayLevelSoundAction(x, y, ACTION_FILLING);
7470     }
7471     else if (element == EL_MAGIC_WALL_FULL)
7472     {
7473       if (IS_FREE(x, y + 1))
7474       {
7475         InitMovingField(x, y, MV_DOWN);
7476         started_moving = TRUE;
7477
7478         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7479         Store[x][y] = EL_CHANGED(Store[x][y]);
7480       }
7481       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7482       {
7483         if (!MovDelay[x][y])
7484           MovDelay[x][y] = TILEY/4 + 1;
7485
7486         if (MovDelay[x][y])
7487         {
7488           MovDelay[x][y]--;
7489           if (MovDelay[x][y])
7490             return;
7491         }
7492
7493         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7494         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7495         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7496         Store[x][y] = 0;
7497       }
7498     }
7499     else if (element == EL_BD_MAGIC_WALL_FULL)
7500     {
7501       if (IS_FREE(x, y + 1))
7502       {
7503         InitMovingField(x, y, MV_DOWN);
7504         started_moving = TRUE;
7505
7506         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7507         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7508       }
7509       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7510       {
7511         if (!MovDelay[x][y])
7512           MovDelay[x][y] = TILEY/4 + 1;
7513
7514         if (MovDelay[x][y])
7515         {
7516           MovDelay[x][y]--;
7517           if (MovDelay[x][y])
7518             return;
7519         }
7520
7521         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7522         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7523         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7524         Store[x][y] = 0;
7525       }
7526     }
7527     else if (element == EL_DC_MAGIC_WALL_FULL)
7528     {
7529       if (IS_FREE(x, y + 1))
7530       {
7531         InitMovingField(x, y, MV_DOWN);
7532         started_moving = TRUE;
7533
7534         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7535         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7536       }
7537       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7538       {
7539         if (!MovDelay[x][y])
7540           MovDelay[x][y] = TILEY/4 + 1;
7541
7542         if (MovDelay[x][y])
7543         {
7544           MovDelay[x][y]--;
7545           if (MovDelay[x][y])
7546             return;
7547         }
7548
7549         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7550         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7551         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7552         Store[x][y] = 0;
7553       }
7554     }
7555     else if ((CAN_PASS_MAGIC_WALL(element) &&
7556               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7557                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7558              (CAN_PASS_DC_MAGIC_WALL(element) &&
7559               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7560
7561     {
7562       InitMovingField(x, y, MV_DOWN);
7563       started_moving = TRUE;
7564
7565       Feld[x][y] =
7566         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7567          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7568          EL_DC_MAGIC_WALL_FILLING);
7569       Store[x][y] = element;
7570     }
7571     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7572     {
7573       SplashAcid(x, y + 1);
7574
7575       InitMovingField(x, y, MV_DOWN);
7576       started_moving = TRUE;
7577
7578       Store[x][y] = EL_ACID;
7579     }
7580     else if (
7581 #if USE_FIX_IMPACT_COLLISION
7582              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7583               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7584 #else
7585              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7586               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7587 #endif
7588              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7589               CAN_FALL(element) && WasJustFalling[x][y] &&
7590               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7591
7592              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7593               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7594               (Feld[x][y + 1] == EL_BLOCKED)))
7595     {
7596       /* this is needed for a special case not covered by calling "Impact()"
7597          from "ContinueMoving()": if an element moves to a tile directly below
7598          another element which was just falling on that tile (which was empty
7599          in the previous frame), the falling element above would just stop
7600          instead of smashing the element below (in previous version, the above
7601          element was just checked for "moving" instead of "falling", resulting
7602          in incorrect smashes caused by horizontal movement of the above
7603          element; also, the case of the player being the element to smash was
7604          simply not covered here... :-/ ) */
7605
7606       CheckCollision[x][y] = 0;
7607       CheckImpact[x][y] = 0;
7608
7609       Impact(x, y);
7610     }
7611     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7612     {
7613       if (MovDir[x][y] == MV_NONE)
7614       {
7615         InitMovingField(x, y, MV_DOWN);
7616         started_moving = TRUE;
7617       }
7618     }
7619     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7620     {
7621       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7622         MovDir[x][y] = MV_DOWN;
7623
7624       InitMovingField(x, y, MV_DOWN);
7625       started_moving = TRUE;
7626     }
7627     else if (element == EL_AMOEBA_DROP)
7628     {
7629       Feld[x][y] = EL_AMOEBA_GROWING;
7630       Store[x][y] = EL_AMOEBA_WET;
7631     }
7632     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7633               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7634              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7635              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7636     {
7637       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7638                                 (IS_FREE(x - 1, y + 1) ||
7639                                  Feld[x - 1][y + 1] == EL_ACID));
7640       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7641                                 (IS_FREE(x + 1, y + 1) ||
7642                                  Feld[x + 1][y + 1] == EL_ACID));
7643       boolean can_fall_any  = (can_fall_left || can_fall_right);
7644       boolean can_fall_both = (can_fall_left && can_fall_right);
7645       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7646
7647 #if USE_NEW_ALL_SLIPPERY
7648       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7649       {
7650         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7651           can_fall_right = FALSE;
7652         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7653           can_fall_left = FALSE;
7654         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7655           can_fall_right = FALSE;
7656         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7657           can_fall_left = FALSE;
7658
7659         can_fall_any  = (can_fall_left || can_fall_right);
7660         can_fall_both = FALSE;
7661       }
7662 #else
7663       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7664       {
7665         if (slippery_type == SLIPPERY_ONLY_LEFT)
7666           can_fall_right = FALSE;
7667         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7668           can_fall_left = FALSE;
7669         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7670           can_fall_right = FALSE;
7671         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7672           can_fall_left = FALSE;
7673
7674         can_fall_any  = (can_fall_left || can_fall_right);
7675         can_fall_both = (can_fall_left && can_fall_right);
7676       }
7677 #endif
7678
7679 #if USE_NEW_ALL_SLIPPERY
7680 #else
7681 #if USE_NEW_SP_SLIPPERY
7682       /* !!! better use the same properties as for custom elements here !!! */
7683       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7684                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7685       {
7686         can_fall_right = FALSE;         /* slip down on left side */
7687         can_fall_both = FALSE;
7688       }
7689 #endif
7690 #endif
7691
7692 #if USE_NEW_ALL_SLIPPERY
7693       if (can_fall_both)
7694       {
7695         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7696           can_fall_right = FALSE;       /* slip down on left side */
7697         else
7698           can_fall_left = !(can_fall_right = RND(2));
7699
7700         can_fall_both = FALSE;
7701       }
7702 #else
7703       if (can_fall_both)
7704       {
7705         if (game.emulation == EMU_BOULDERDASH ||
7706             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7707           can_fall_right = FALSE;       /* slip down on left side */
7708         else
7709           can_fall_left = !(can_fall_right = RND(2));
7710
7711         can_fall_both = FALSE;
7712       }
7713 #endif
7714
7715       if (can_fall_any)
7716       {
7717         /* if not determined otherwise, prefer left side for slipping down */
7718         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7719         started_moving = TRUE;
7720       }
7721     }
7722 #if 0
7723     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7724 #else
7725     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7726 #endif
7727     {
7728       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7729       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7730       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7731       int belt_dir = game.belt_dir[belt_nr];
7732
7733       if ((belt_dir == MV_LEFT  && left_is_free) ||
7734           (belt_dir == MV_RIGHT && right_is_free))
7735       {
7736         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7737
7738         InitMovingField(x, y, belt_dir);
7739         started_moving = TRUE;
7740
7741         Pushed[x][y] = TRUE;
7742         Pushed[nextx][y] = TRUE;
7743
7744         GfxAction[x][y] = ACTION_DEFAULT;
7745       }
7746       else
7747       {
7748         MovDir[x][y] = 0;       /* if element was moving, stop it */
7749       }
7750     }
7751   }
7752
7753   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7754 #if 0
7755   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7756 #else
7757   if (CAN_MOVE(element) && !started_moving)
7758 #endif
7759   {
7760     int move_pattern = element_info[element].move_pattern;
7761     int newx, newy;
7762
7763 #if 0
7764 #if DEBUG
7765     if (MovDir[x][y] == MV_NONE)
7766     {
7767       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7768              x, y, element, element_info[element].token_name);
7769       printf("StartMoving(): This should never happen!\n");
7770     }
7771 #endif
7772 #endif
7773
7774     Moving2Blocked(x, y, &newx, &newy);
7775
7776     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7777       return;
7778
7779     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7780         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7781     {
7782       WasJustMoving[x][y] = 0;
7783       CheckCollision[x][y] = 0;
7784
7785       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7786
7787       if (Feld[x][y] != element)        /* element has changed */
7788         return;
7789     }
7790
7791     if (!MovDelay[x][y])        /* start new movement phase */
7792     {
7793       /* all objects that can change their move direction after each step
7794          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7795
7796       if (element != EL_YAMYAM &&
7797           element != EL_DARK_YAMYAM &&
7798           element != EL_PACMAN &&
7799           !(move_pattern & MV_ANY_DIRECTION) &&
7800           move_pattern != MV_TURNING_LEFT &&
7801           move_pattern != MV_TURNING_RIGHT &&
7802           move_pattern != MV_TURNING_LEFT_RIGHT &&
7803           move_pattern != MV_TURNING_RIGHT_LEFT &&
7804           move_pattern != MV_TURNING_RANDOM)
7805       {
7806         TurnRound(x, y);
7807
7808         if (MovDelay[x][y] && (element == EL_BUG ||
7809                                element == EL_SPACESHIP ||
7810                                element == EL_SP_SNIKSNAK ||
7811                                element == EL_SP_ELECTRON ||
7812                                element == EL_MOLE))
7813           DrawLevelField(x, y);
7814       }
7815     }
7816
7817     if (MovDelay[x][y])         /* wait some time before next movement */
7818     {
7819       MovDelay[x][y]--;
7820
7821       if (element == EL_ROBOT ||
7822           element == EL_YAMYAM ||
7823           element == EL_DARK_YAMYAM)
7824       {
7825         DrawLevelElementAnimationIfNeeded(x, y, element);
7826         PlayLevelSoundAction(x, y, ACTION_WAITING);
7827       }
7828       else if (element == EL_SP_ELECTRON)
7829         DrawLevelElementAnimationIfNeeded(x, y, element);
7830       else if (element == EL_DRAGON)
7831       {
7832         int i;
7833         int dir = MovDir[x][y];
7834         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7835         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7836         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7837                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7838                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7839                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7840         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7841
7842         GfxAction[x][y] = ACTION_ATTACKING;
7843
7844         if (IS_PLAYER(x, y))
7845           DrawPlayerField(x, y);
7846         else
7847           DrawLevelField(x, y);
7848
7849         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7850
7851         for (i = 1; i <= 3; i++)
7852         {
7853           int xx = x + i * dx;
7854           int yy = y + i * dy;
7855           int sx = SCREENX(xx);
7856           int sy = SCREENY(yy);
7857           int flame_graphic = graphic + (i - 1);
7858
7859           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7860             break;
7861
7862           if (MovDelay[x][y])
7863           {
7864             int flamed = MovingOrBlocked2Element(xx, yy);
7865
7866             /* !!! */
7867 #if 0
7868             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7869               Bang(xx, yy);
7870             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7871               RemoveMovingField(xx, yy);
7872             else
7873               RemoveField(xx, yy);
7874 #else
7875             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7876               Bang(xx, yy);
7877             else
7878               RemoveMovingField(xx, yy);
7879 #endif
7880
7881             ChangeDelay[xx][yy] = 0;
7882
7883             Feld[xx][yy] = EL_FLAMES;
7884
7885             if (IN_SCR_FIELD(sx, sy))
7886             {
7887               DrawLevelFieldCrumbledSand(xx, yy);
7888               DrawGraphic(sx, sy, flame_graphic, frame);
7889             }
7890           }
7891           else
7892           {
7893             if (Feld[xx][yy] == EL_FLAMES)
7894               Feld[xx][yy] = EL_EMPTY;
7895             DrawLevelField(xx, yy);
7896           }
7897         }
7898       }
7899
7900       if (MovDelay[x][y])       /* element still has to wait some time */
7901       {
7902         PlayLevelSoundAction(x, y, ACTION_WAITING);
7903
7904         return;
7905       }
7906     }
7907
7908     /* now make next step */
7909
7910     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7911
7912     if (DONT_COLLIDE_WITH(element) &&
7913         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7914         !PLAYER_ENEMY_PROTECTED(newx, newy))
7915     {
7916       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7917
7918       return;
7919     }
7920
7921     else if (CAN_MOVE_INTO_ACID(element) &&
7922              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7923              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7924              (MovDir[x][y] == MV_DOWN ||
7925               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7926     {
7927       SplashAcid(newx, newy);
7928       Store[x][y] = EL_ACID;
7929     }
7930     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7931     {
7932       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7933           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7934           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7935           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7936       {
7937         RemoveField(x, y);
7938         DrawLevelField(x, y);
7939
7940         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7941         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7942           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7943
7944         local_player->friends_still_needed--;
7945         if (!local_player->friends_still_needed &&
7946             !local_player->GameOver && AllPlayersGone)
7947           PlayerWins(local_player);
7948
7949         return;
7950       }
7951       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7952       {
7953         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7954           DrawLevelField(newx, newy);
7955         else
7956           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7957       }
7958       else if (!IS_FREE(newx, newy))
7959       {
7960         GfxAction[x][y] = ACTION_WAITING;
7961
7962         if (IS_PLAYER(x, y))
7963           DrawPlayerField(x, y);
7964         else
7965           DrawLevelField(x, y);
7966
7967         return;
7968       }
7969     }
7970     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7971     {
7972       if (IS_FOOD_PIG(Feld[newx][newy]))
7973       {
7974         if (IS_MOVING(newx, newy))
7975           RemoveMovingField(newx, newy);
7976         else
7977         {
7978           Feld[newx][newy] = EL_EMPTY;
7979           DrawLevelField(newx, newy);
7980         }
7981
7982         PlayLevelSound(x, y, SND_PIG_DIGGING);
7983       }
7984       else if (!IS_FREE(newx, newy))
7985       {
7986         if (IS_PLAYER(x, y))
7987           DrawPlayerField(x, y);
7988         else
7989           DrawLevelField(x, y);
7990
7991         return;
7992       }
7993     }
7994     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7995     {
7996       if (Store[x][y] != EL_EMPTY)
7997       {
7998         boolean can_clone = FALSE;
7999         int xx, yy;
8000
8001         /* check if element to clone is still there */
8002         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8003         {
8004           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8005           {
8006             can_clone = TRUE;
8007
8008             break;
8009           }
8010         }
8011
8012         /* cannot clone or target field not free anymore -- do not clone */
8013         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8014           Store[x][y] = EL_EMPTY;
8015       }
8016
8017       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8018       {
8019         if (IS_MV_DIAGONAL(MovDir[x][y]))
8020         {
8021           int diagonal_move_dir = MovDir[x][y];
8022           int stored = Store[x][y];
8023           int change_delay = 8;
8024           int graphic;
8025
8026           /* android is moving diagonally */
8027
8028           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8029
8030           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8031           GfxElement[x][y] = EL_EMC_ANDROID;
8032           GfxAction[x][y] = ACTION_SHRINKING;
8033           GfxDir[x][y] = diagonal_move_dir;
8034           ChangeDelay[x][y] = change_delay;
8035
8036           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8037                                    GfxDir[x][y]);
8038
8039           DrawLevelGraphicAnimation(x, y, graphic);
8040           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8041
8042           if (Feld[newx][newy] == EL_ACID)
8043           {
8044             SplashAcid(newx, newy);
8045
8046             return;
8047           }
8048
8049           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8050
8051           Store[newx][newy] = EL_EMC_ANDROID;
8052           GfxElement[newx][newy] = EL_EMC_ANDROID;
8053           GfxAction[newx][newy] = ACTION_GROWING;
8054           GfxDir[newx][newy] = diagonal_move_dir;
8055           ChangeDelay[newx][newy] = change_delay;
8056
8057           graphic = el_act_dir2img(GfxElement[newx][newy],
8058                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8059
8060           DrawLevelGraphicAnimation(newx, newy, graphic);
8061           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8062
8063           return;
8064         }
8065         else
8066         {
8067           Feld[newx][newy] = EL_EMPTY;
8068           DrawLevelField(newx, newy);
8069
8070           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8071         }
8072       }
8073       else if (!IS_FREE(newx, newy))
8074       {
8075 #if 0
8076         if (IS_PLAYER(x, y))
8077           DrawPlayerField(x, y);
8078         else
8079           DrawLevelField(x, y);
8080 #endif
8081
8082         return;
8083       }
8084     }
8085     else if (IS_CUSTOM_ELEMENT(element) &&
8086              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8087     {
8088       int new_element = Feld[newx][newy];
8089
8090       if (!IS_FREE(newx, newy))
8091       {
8092         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8093                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8094                       ACTION_BREAKING);
8095
8096         /* no element can dig solid indestructible elements */
8097         if (IS_INDESTRUCTIBLE(new_element) &&
8098             !IS_DIGGABLE(new_element) &&
8099             !IS_COLLECTIBLE(new_element))
8100           return;
8101
8102         if (AmoebaNr[newx][newy] &&
8103             (new_element == EL_AMOEBA_FULL ||
8104              new_element == EL_BD_AMOEBA ||
8105              new_element == EL_AMOEBA_GROWING))
8106         {
8107           AmoebaCnt[AmoebaNr[newx][newy]]--;
8108           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8109         }
8110
8111         if (IS_MOVING(newx, newy))
8112           RemoveMovingField(newx, newy);
8113         else
8114         {
8115           RemoveField(newx, newy);
8116           DrawLevelField(newx, newy);
8117         }
8118
8119         /* if digged element was about to explode, prevent the explosion */
8120         ExplodeField[newx][newy] = EX_TYPE_NONE;
8121
8122         PlayLevelSoundAction(x, y, action);
8123       }
8124
8125       Store[newx][newy] = EL_EMPTY;
8126 #if 1
8127       /* this makes it possible to leave the removed element again */
8128       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8129         Store[newx][newy] = new_element;
8130 #else
8131       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8132       {
8133         int move_leave_element = element_info[element].move_leave_element;
8134
8135         /* this makes it possible to leave the removed element again */
8136         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8137                              new_element : move_leave_element);
8138       }
8139 #endif
8140
8141       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8142       {
8143         RunnerVisit[x][y] = FrameCounter;
8144         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8145       }
8146     }
8147     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8148     {
8149       if (!IS_FREE(newx, newy))
8150       {
8151         if (IS_PLAYER(x, y))
8152           DrawPlayerField(x, y);
8153         else
8154           DrawLevelField(x, y);
8155
8156         return;
8157       }
8158       else
8159       {
8160         boolean wanna_flame = !RND(10);
8161         int dx = newx - x, dy = newy - y;
8162         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8163         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8164         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8165                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8166         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8167                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8168
8169         if ((wanna_flame ||
8170              IS_CLASSIC_ENEMY(element1) ||
8171              IS_CLASSIC_ENEMY(element2)) &&
8172             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8173             element1 != EL_FLAMES && element2 != EL_FLAMES)
8174         {
8175           ResetGfxAnimation(x, y);
8176           GfxAction[x][y] = ACTION_ATTACKING;
8177
8178           if (IS_PLAYER(x, y))
8179             DrawPlayerField(x, y);
8180           else
8181             DrawLevelField(x, y);
8182
8183           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8184
8185           MovDelay[x][y] = 50;
8186
8187           /* !!! */
8188 #if 0
8189           RemoveField(newx, newy);
8190 #endif
8191           Feld[newx][newy] = EL_FLAMES;
8192           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8193           {
8194 #if 0
8195             RemoveField(newx1, newy1);
8196 #endif
8197             Feld[newx1][newy1] = EL_FLAMES;
8198           }
8199           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8200           {
8201 #if 0
8202             RemoveField(newx2, newy2);
8203 #endif
8204             Feld[newx2][newy2] = EL_FLAMES;
8205           }
8206
8207           return;
8208         }
8209       }
8210     }
8211     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8212              Feld[newx][newy] == EL_DIAMOND)
8213     {
8214       if (IS_MOVING(newx, newy))
8215         RemoveMovingField(newx, newy);
8216       else
8217       {
8218         Feld[newx][newy] = EL_EMPTY;
8219         DrawLevelField(newx, newy);
8220       }
8221
8222       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8223     }
8224     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8225              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8226     {
8227       if (AmoebaNr[newx][newy])
8228       {
8229         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8230         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8231             Feld[newx][newy] == EL_BD_AMOEBA)
8232           AmoebaCnt[AmoebaNr[newx][newy]]--;
8233       }
8234
8235 #if 0
8236       /* !!! test !!! */
8237       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8238       {
8239         RemoveMovingField(newx, newy);
8240       }
8241 #else
8242       if (IS_MOVING(newx, newy))
8243       {
8244         RemoveMovingField(newx, newy);
8245       }
8246 #endif
8247       else
8248       {
8249         Feld[newx][newy] = EL_EMPTY;
8250         DrawLevelField(newx, newy);
8251       }
8252
8253       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8254     }
8255     else if ((element == EL_PACMAN || element == EL_MOLE)
8256              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8257     {
8258       if (AmoebaNr[newx][newy])
8259       {
8260         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8261         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8262             Feld[newx][newy] == EL_BD_AMOEBA)
8263           AmoebaCnt[AmoebaNr[newx][newy]]--;
8264       }
8265
8266       if (element == EL_MOLE)
8267       {
8268         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8269         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8270
8271         ResetGfxAnimation(x, y);
8272         GfxAction[x][y] = ACTION_DIGGING;
8273         DrawLevelField(x, y);
8274
8275         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8276
8277         return;                         /* wait for shrinking amoeba */
8278       }
8279       else      /* element == EL_PACMAN */
8280       {
8281         Feld[newx][newy] = EL_EMPTY;
8282         DrawLevelField(newx, newy);
8283         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8284       }
8285     }
8286     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8287              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8288               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8289     {
8290       /* wait for shrinking amoeba to completely disappear */
8291       return;
8292     }
8293     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8294     {
8295       /* object was running against a wall */
8296
8297       TurnRound(x, y);
8298
8299 #if 0
8300       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8301       if (move_pattern & MV_ANY_DIRECTION &&
8302           move_pattern == MovDir[x][y])
8303       {
8304         int blocking_element =
8305           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8306
8307         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8308                                  MovDir[x][y]);
8309
8310         element = Feld[x][y];   /* element might have changed */
8311       }
8312 #endif
8313
8314       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8315         DrawLevelElementAnimation(x, y, element);
8316
8317       if (DONT_TOUCH(element))
8318         TestIfBadThingTouchesPlayer(x, y);
8319
8320       return;
8321     }
8322
8323     InitMovingField(x, y, MovDir[x][y]);
8324
8325     PlayLevelSoundAction(x, y, ACTION_MOVING);
8326   }
8327
8328   if (MovDir[x][y])
8329     ContinueMoving(x, y);
8330 }
8331
8332 void ContinueMoving(int x, int y)
8333 {
8334   int element = Feld[x][y];
8335   struct ElementInfo *ei = &element_info[element];
8336   int direction = MovDir[x][y];
8337   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8338   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8339   int newx = x + dx, newy = y + dy;
8340   int stored = Store[x][y];
8341   int stored_new = Store[newx][newy];
8342   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8343   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8344   boolean last_line = (newy == lev_fieldy - 1);
8345
8346   MovPos[x][y] += getElementMoveStepsize(x, y);
8347
8348   if (pushed_by_player) /* special case: moving object pushed by player */
8349     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8350
8351   if (ABS(MovPos[x][y]) < TILEX)
8352   {
8353 #if 0
8354     int ee = Feld[x][y];
8355     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8356     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8357
8358     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8359            x, y, ABS(MovPos[x][y]),
8360            ee, gg, ff,
8361            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8362 #endif
8363
8364     DrawLevelField(x, y);
8365
8366     return;     /* element is still moving */
8367   }
8368
8369   /* element reached destination field */
8370
8371   Feld[x][y] = EL_EMPTY;
8372   Feld[newx][newy] = element;
8373   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8374
8375   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8376   {
8377     element = Feld[newx][newy] = EL_ACID;
8378   }
8379   else if (element == EL_MOLE)
8380   {
8381     Feld[x][y] = EL_SAND;
8382
8383     DrawLevelFieldCrumbledSandNeighbours(x, y);
8384   }
8385   else if (element == EL_QUICKSAND_FILLING)
8386   {
8387     element = Feld[newx][newy] = get_next_element(element);
8388     Store[newx][newy] = Store[x][y];
8389   }
8390   else if (element == EL_QUICKSAND_EMPTYING)
8391   {
8392     Feld[x][y] = get_next_element(element);
8393     element = Feld[newx][newy] = Store[x][y];
8394   }
8395   else if (element == EL_QUICKSAND_FAST_FILLING)
8396   {
8397     element = Feld[newx][newy] = get_next_element(element);
8398     Store[newx][newy] = Store[x][y];
8399   }
8400   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8401   {
8402     Feld[x][y] = get_next_element(element);
8403     element = Feld[newx][newy] = Store[x][y];
8404   }
8405   else if (element == EL_MAGIC_WALL_FILLING)
8406   {
8407     element = Feld[newx][newy] = get_next_element(element);
8408     if (!game.magic_wall_active)
8409       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8410     Store[newx][newy] = Store[x][y];
8411   }
8412   else if (element == EL_MAGIC_WALL_EMPTYING)
8413   {
8414     Feld[x][y] = get_next_element(element);
8415     if (!game.magic_wall_active)
8416       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8417     element = Feld[newx][newy] = Store[x][y];
8418
8419 #if USE_NEW_CUSTOM_VALUE
8420     InitField(newx, newy, FALSE);
8421 #endif
8422   }
8423   else if (element == EL_BD_MAGIC_WALL_FILLING)
8424   {
8425     element = Feld[newx][newy] = get_next_element(element);
8426     if (!game.magic_wall_active)
8427       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8428     Store[newx][newy] = Store[x][y];
8429   }
8430   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8431   {
8432     Feld[x][y] = get_next_element(element);
8433     if (!game.magic_wall_active)
8434       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8435     element = Feld[newx][newy] = Store[x][y];
8436
8437 #if USE_NEW_CUSTOM_VALUE
8438     InitField(newx, newy, FALSE);
8439 #endif
8440   }
8441   else if (element == EL_DC_MAGIC_WALL_FILLING)
8442   {
8443     element = Feld[newx][newy] = get_next_element(element);
8444     if (!game.magic_wall_active)
8445       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8446     Store[newx][newy] = Store[x][y];
8447   }
8448   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8449   {
8450     Feld[x][y] = get_next_element(element);
8451     if (!game.magic_wall_active)
8452       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8453     element = Feld[newx][newy] = Store[x][y];
8454
8455 #if USE_NEW_CUSTOM_VALUE
8456     InitField(newx, newy, FALSE);
8457 #endif
8458   }
8459   else if (element == EL_AMOEBA_DROPPING)
8460   {
8461     Feld[x][y] = get_next_element(element);
8462     element = Feld[newx][newy] = Store[x][y];
8463   }
8464   else if (element == EL_SOKOBAN_OBJECT)
8465   {
8466     if (Back[x][y])
8467       Feld[x][y] = Back[x][y];
8468
8469     if (Back[newx][newy])
8470       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8471
8472     Back[x][y] = Back[newx][newy] = 0;
8473   }
8474
8475   Store[x][y] = EL_EMPTY;
8476   MovPos[x][y] = 0;
8477   MovDir[x][y] = 0;
8478   MovDelay[x][y] = 0;
8479
8480   MovDelay[newx][newy] = 0;
8481
8482   if (CAN_CHANGE_OR_HAS_ACTION(element))
8483   {
8484     /* copy element change control values to new field */
8485     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8486     ChangePage[newx][newy]  = ChangePage[x][y];
8487     ChangeCount[newx][newy] = ChangeCount[x][y];
8488     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8489   }
8490
8491 #if USE_NEW_CUSTOM_VALUE
8492     CustomValue[newx][newy] = CustomValue[x][y];
8493 #endif
8494
8495   ChangeDelay[x][y] = 0;
8496   ChangePage[x][y] = -1;
8497   ChangeCount[x][y] = 0;
8498   ChangeEvent[x][y] = -1;
8499
8500 #if USE_NEW_CUSTOM_VALUE
8501   CustomValue[x][y] = 0;
8502 #endif
8503
8504   /* copy animation control values to new field */
8505   GfxFrame[newx][newy]  = GfxFrame[x][y];
8506   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8507   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8508   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8509
8510   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8511
8512   /* some elements can leave other elements behind after moving */
8513 #if 1
8514   if (ei->move_leave_element != EL_EMPTY &&
8515       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8516       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8517 #else
8518   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8519       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8520       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8521 #endif
8522   {
8523     int move_leave_element = ei->move_leave_element;
8524
8525 #if 1
8526 #if 1
8527     /* this makes it possible to leave the removed element again */
8528     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8529       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8530 #else
8531     /* this makes it possible to leave the removed element again */
8532     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8533       move_leave_element = stored;
8534 #endif
8535 #else
8536     /* this makes it possible to leave the removed element again */
8537     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8538         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8539       move_leave_element = stored;
8540 #endif
8541
8542     Feld[x][y] = move_leave_element;
8543
8544     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8545       MovDir[x][y] = direction;
8546
8547     InitField(x, y, FALSE);
8548
8549     if (GFX_CRUMBLED(Feld[x][y]))
8550       DrawLevelFieldCrumbledSandNeighbours(x, y);
8551
8552     if (ELEM_IS_PLAYER(move_leave_element))
8553       RelocatePlayer(x, y, move_leave_element);
8554   }
8555
8556   /* do this after checking for left-behind element */
8557   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8558
8559   if (!CAN_MOVE(element) ||
8560       (CAN_FALL(element) && direction == MV_DOWN &&
8561        (element == EL_SPRING ||
8562         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8563         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8564     GfxDir[x][y] = MovDir[newx][newy] = 0;
8565
8566   DrawLevelField(x, y);
8567   DrawLevelField(newx, newy);
8568
8569   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8570
8571   /* prevent pushed element from moving on in pushed direction */
8572   if (pushed_by_player && CAN_MOVE(element) &&
8573       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8574       !(element_info[element].move_pattern & direction))
8575     TurnRound(newx, newy);
8576
8577   /* prevent elements on conveyor belt from moving on in last direction */
8578   if (pushed_by_conveyor && CAN_FALL(element) &&
8579       direction & MV_HORIZONTAL)
8580     MovDir[newx][newy] = 0;
8581
8582   if (!pushed_by_player)
8583   {
8584     int nextx = newx + dx, nexty = newy + dy;
8585     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8586
8587     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8588
8589     if (CAN_FALL(element) && direction == MV_DOWN)
8590       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8591
8592     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8593       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8594
8595 #if USE_FIX_IMPACT_COLLISION
8596     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8597       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8598 #endif
8599   }
8600
8601   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8602   {
8603     TestIfBadThingTouchesPlayer(newx, newy);
8604     TestIfBadThingTouchesFriend(newx, newy);
8605
8606     if (!IS_CUSTOM_ELEMENT(element))
8607       TestIfBadThingTouchesOtherBadThing(newx, newy);
8608   }
8609   else if (element == EL_PENGUIN)
8610     TestIfFriendTouchesBadThing(newx, newy);
8611
8612   /* give the player one last chance (one more frame) to move away */
8613   if (CAN_FALL(element) && direction == MV_DOWN &&
8614       (last_line || (!IS_FREE(x, newy + 1) &&
8615                      (!IS_PLAYER(x, newy + 1) ||
8616                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8617     Impact(x, newy);
8618
8619   if (pushed_by_player && !game.use_change_when_pushing_bug)
8620   {
8621     int push_side = MV_DIR_OPPOSITE(direction);
8622     struct PlayerInfo *player = PLAYERINFO(x, y);
8623
8624     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8625                                player->index_bit, push_side);
8626     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8627                                         player->index_bit, push_side);
8628   }
8629
8630   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8631     MovDelay[newx][newy] = 1;
8632
8633   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8634
8635   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8636
8637 #if 0
8638   if (ChangePage[newx][newy] != -1)             /* delayed change */
8639   {
8640     int page = ChangePage[newx][newy];
8641     struct ElementChangeInfo *change = &ei->change_page[page];
8642
8643     ChangePage[newx][newy] = -1;
8644
8645     if (change->can_change)
8646     {
8647       if (ChangeElement(newx, newy, element, page))
8648       {
8649         if (change->post_change_function)
8650           change->post_change_function(newx, newy);
8651       }
8652     }
8653
8654     if (change->has_action)
8655       ExecuteCustomElementAction(newx, newy, element, page);
8656   }
8657 #endif
8658
8659   TestIfElementHitsCustomElement(newx, newy, direction);
8660   TestIfPlayerTouchesCustomElement(newx, newy);
8661   TestIfElementTouchesCustomElement(newx, newy);
8662
8663   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8664       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8665     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8666                              MV_DIR_OPPOSITE(direction));
8667 }
8668
8669 int AmoebeNachbarNr(int ax, int ay)
8670 {
8671   int i;
8672   int element = Feld[ax][ay];
8673   int group_nr = 0;
8674   static int xy[4][2] =
8675   {
8676     { 0, -1 },
8677     { -1, 0 },
8678     { +1, 0 },
8679     { 0, +1 }
8680   };
8681
8682   for (i = 0; i < NUM_DIRECTIONS; i++)
8683   {
8684     int x = ax + xy[i][0];
8685     int y = ay + xy[i][1];
8686
8687     if (!IN_LEV_FIELD(x, y))
8688       continue;
8689
8690     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8691       group_nr = AmoebaNr[x][y];
8692   }
8693
8694   return group_nr;
8695 }
8696
8697 void AmoebenVereinigen(int ax, int ay)
8698 {
8699   int i, x, y, xx, yy;
8700   int new_group_nr = AmoebaNr[ax][ay];
8701   static int xy[4][2] =
8702   {
8703     { 0, -1 },
8704     { -1, 0 },
8705     { +1, 0 },
8706     { 0, +1 }
8707   };
8708
8709   if (new_group_nr == 0)
8710     return;
8711
8712   for (i = 0; i < NUM_DIRECTIONS; i++)
8713   {
8714     x = ax + xy[i][0];
8715     y = ay + xy[i][1];
8716
8717     if (!IN_LEV_FIELD(x, y))
8718       continue;
8719
8720     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8721          Feld[x][y] == EL_BD_AMOEBA ||
8722          Feld[x][y] == EL_AMOEBA_DEAD) &&
8723         AmoebaNr[x][y] != new_group_nr)
8724     {
8725       int old_group_nr = AmoebaNr[x][y];
8726
8727       if (old_group_nr == 0)
8728         return;
8729
8730       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8731       AmoebaCnt[old_group_nr] = 0;
8732       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8733       AmoebaCnt2[old_group_nr] = 0;
8734
8735       SCAN_PLAYFIELD(xx, yy)
8736       {
8737         if (AmoebaNr[xx][yy] == old_group_nr)
8738           AmoebaNr[xx][yy] = new_group_nr;
8739       }
8740     }
8741   }
8742 }
8743
8744 void AmoebeUmwandeln(int ax, int ay)
8745 {
8746   int i, x, y;
8747
8748   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8749   {
8750     int group_nr = AmoebaNr[ax][ay];
8751
8752 #ifdef DEBUG
8753     if (group_nr == 0)
8754     {
8755       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8756       printf("AmoebeUmwandeln(): This should never happen!\n");
8757       return;
8758     }
8759 #endif
8760
8761     SCAN_PLAYFIELD(x, y)
8762     {
8763       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8764       {
8765         AmoebaNr[x][y] = 0;
8766         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8767       }
8768     }
8769
8770     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8771                             SND_AMOEBA_TURNING_TO_GEM :
8772                             SND_AMOEBA_TURNING_TO_ROCK));
8773     Bang(ax, ay);
8774   }
8775   else
8776   {
8777     static int xy[4][2] =
8778     {
8779       { 0, -1 },
8780       { -1, 0 },
8781       { +1, 0 },
8782       { 0, +1 }
8783     };
8784
8785     for (i = 0; i < NUM_DIRECTIONS; i++)
8786     {
8787       x = ax + xy[i][0];
8788       y = ay + xy[i][1];
8789
8790       if (!IN_LEV_FIELD(x, y))
8791         continue;
8792
8793       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8794       {
8795         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8796                               SND_AMOEBA_TURNING_TO_GEM :
8797                               SND_AMOEBA_TURNING_TO_ROCK));
8798         Bang(x, y);
8799       }
8800     }
8801   }
8802 }
8803
8804 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8805 {
8806   int x, y;
8807   int group_nr = AmoebaNr[ax][ay];
8808   boolean done = FALSE;
8809
8810 #ifdef DEBUG
8811   if (group_nr == 0)
8812   {
8813     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8814     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8815     return;
8816   }
8817 #endif
8818
8819   SCAN_PLAYFIELD(x, y)
8820   {
8821     if (AmoebaNr[x][y] == group_nr &&
8822         (Feld[x][y] == EL_AMOEBA_DEAD ||
8823          Feld[x][y] == EL_BD_AMOEBA ||
8824          Feld[x][y] == EL_AMOEBA_GROWING))
8825     {
8826       AmoebaNr[x][y] = 0;
8827       Feld[x][y] = new_element;
8828       InitField(x, y, FALSE);
8829       DrawLevelField(x, y);
8830       done = TRUE;
8831     }
8832   }
8833
8834   if (done)
8835     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8836                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8837                             SND_BD_AMOEBA_TURNING_TO_GEM));
8838 }
8839
8840 void AmoebeWaechst(int x, int y)
8841 {
8842   static unsigned long sound_delay = 0;
8843   static unsigned long sound_delay_value = 0;
8844
8845   if (!MovDelay[x][y])          /* start new growing cycle */
8846   {
8847     MovDelay[x][y] = 7;
8848
8849     if (DelayReached(&sound_delay, sound_delay_value))
8850     {
8851       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8852       sound_delay_value = 30;
8853     }
8854   }
8855
8856   if (MovDelay[x][y])           /* wait some time before growing bigger */
8857   {
8858     MovDelay[x][y]--;
8859     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8860     {
8861       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8862                                            6 - MovDelay[x][y]);
8863
8864       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8865     }
8866
8867     if (!MovDelay[x][y])
8868     {
8869       Feld[x][y] = Store[x][y];
8870       Store[x][y] = 0;
8871       DrawLevelField(x, y);
8872     }
8873   }
8874 }
8875
8876 void AmoebaDisappearing(int x, int y)
8877 {
8878   static unsigned long sound_delay = 0;
8879   static unsigned long sound_delay_value = 0;
8880
8881   if (!MovDelay[x][y])          /* start new shrinking cycle */
8882   {
8883     MovDelay[x][y] = 7;
8884
8885     if (DelayReached(&sound_delay, sound_delay_value))
8886       sound_delay_value = 30;
8887   }
8888
8889   if (MovDelay[x][y])           /* wait some time before shrinking */
8890   {
8891     MovDelay[x][y]--;
8892     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8893     {
8894       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8895                                            6 - MovDelay[x][y]);
8896
8897       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8898     }
8899
8900     if (!MovDelay[x][y])
8901     {
8902       Feld[x][y] = EL_EMPTY;
8903       DrawLevelField(x, y);
8904
8905       /* don't let mole enter this field in this cycle;
8906          (give priority to objects falling to this field from above) */
8907       Stop[x][y] = TRUE;
8908     }
8909   }
8910 }
8911
8912 void AmoebeAbleger(int ax, int ay)
8913 {
8914   int i;
8915   int element = Feld[ax][ay];
8916   int graphic = el2img(element);
8917   int newax = ax, neway = ay;
8918   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8919   static int xy[4][2] =
8920   {
8921     { 0, -1 },
8922     { -1, 0 },
8923     { +1, 0 },
8924     { 0, +1 }
8925   };
8926
8927   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8928   {
8929     Feld[ax][ay] = EL_AMOEBA_DEAD;
8930     DrawLevelField(ax, ay);
8931     return;
8932   }
8933
8934   if (IS_ANIMATED(graphic))
8935     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8936
8937   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8938     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8939
8940   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8941   {
8942     MovDelay[ax][ay]--;
8943     if (MovDelay[ax][ay])
8944       return;
8945   }
8946
8947   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8948   {
8949     int start = RND(4);
8950     int x = ax + xy[start][0];
8951     int y = ay + xy[start][1];
8952
8953     if (!IN_LEV_FIELD(x, y))
8954       return;
8955
8956     if (IS_FREE(x, y) ||
8957         CAN_GROW_INTO(Feld[x][y]) ||
8958         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8959         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8960     {
8961       newax = x;
8962       neway = y;
8963     }
8964
8965     if (newax == ax && neway == ay)
8966       return;
8967   }
8968   else                          /* normal or "filled" (BD style) amoeba */
8969   {
8970     int start = RND(4);
8971     boolean waiting_for_player = FALSE;
8972
8973     for (i = 0; i < NUM_DIRECTIONS; i++)
8974     {
8975       int j = (start + i) % 4;
8976       int x = ax + xy[j][0];
8977       int y = ay + xy[j][1];
8978
8979       if (!IN_LEV_FIELD(x, y))
8980         continue;
8981
8982       if (IS_FREE(x, y) ||
8983           CAN_GROW_INTO(Feld[x][y]) ||
8984           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8985           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8986       {
8987         newax = x;
8988         neway = y;
8989         break;
8990       }
8991       else if (IS_PLAYER(x, y))
8992         waiting_for_player = TRUE;
8993     }
8994
8995     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8996     {
8997       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8998       {
8999         Feld[ax][ay] = EL_AMOEBA_DEAD;
9000         DrawLevelField(ax, ay);
9001         AmoebaCnt[AmoebaNr[ax][ay]]--;
9002
9003         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9004         {
9005           if (element == EL_AMOEBA_FULL)
9006             AmoebeUmwandeln(ax, ay);
9007           else if (element == EL_BD_AMOEBA)
9008             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9009         }
9010       }
9011       return;
9012     }
9013     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9014     {
9015       /* amoeba gets larger by growing in some direction */
9016
9017       int new_group_nr = AmoebaNr[ax][ay];
9018
9019 #ifdef DEBUG
9020   if (new_group_nr == 0)
9021   {
9022     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9023     printf("AmoebeAbleger(): This should never happen!\n");
9024     return;
9025   }
9026 #endif
9027
9028       AmoebaNr[newax][neway] = new_group_nr;
9029       AmoebaCnt[new_group_nr]++;
9030       AmoebaCnt2[new_group_nr]++;
9031
9032       /* if amoeba touches other amoeba(s) after growing, unify them */
9033       AmoebenVereinigen(newax, neway);
9034
9035       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9036       {
9037         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9038         return;
9039       }
9040     }
9041   }
9042
9043   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9044       (neway == lev_fieldy - 1 && newax != ax))
9045   {
9046     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9047     Store[newax][neway] = element;
9048   }
9049   else if (neway == ay || element == EL_EMC_DRIPPER)
9050   {
9051     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9052
9053     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9054   }
9055   else
9056   {
9057     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9058     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9059     Store[ax][ay] = EL_AMOEBA_DROP;
9060     ContinueMoving(ax, ay);
9061     return;
9062   }
9063
9064   DrawLevelField(newax, neway);
9065 }
9066
9067 void Life(int ax, int ay)
9068 {
9069   int x1, y1, x2, y2;
9070   int life_time = 40;
9071   int element = Feld[ax][ay];
9072   int graphic = el2img(element);
9073   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9074                          level.biomaze);
9075   boolean changed = FALSE;
9076
9077   if (IS_ANIMATED(graphic))
9078     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9079
9080   if (Stop[ax][ay])
9081     return;
9082
9083   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9084     MovDelay[ax][ay] = life_time;
9085
9086   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9087   {
9088     MovDelay[ax][ay]--;
9089     if (MovDelay[ax][ay])
9090       return;
9091   }
9092
9093   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9094   {
9095     int xx = ax+x1, yy = ay+y1;
9096     int nachbarn = 0;
9097
9098     if (!IN_LEV_FIELD(xx, yy))
9099       continue;
9100
9101     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9102     {
9103       int x = xx+x2, y = yy+y2;
9104
9105       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9106         continue;
9107
9108       if (((Feld[x][y] == element ||
9109             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9110            !Stop[x][y]) ||
9111           (IS_FREE(x, y) && Stop[x][y]))
9112         nachbarn++;
9113     }
9114
9115     if (xx == ax && yy == ay)           /* field in the middle */
9116     {
9117       if (nachbarn < life_parameter[0] ||
9118           nachbarn > life_parameter[1])
9119       {
9120         Feld[xx][yy] = EL_EMPTY;
9121         if (!Stop[xx][yy])
9122           DrawLevelField(xx, yy);
9123         Stop[xx][yy] = TRUE;
9124         changed = TRUE;
9125       }
9126     }
9127     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9128     {                                   /* free border field */
9129       if (nachbarn >= life_parameter[2] &&
9130           nachbarn <= life_parameter[3])
9131       {
9132         Feld[xx][yy] = element;
9133         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9134         if (!Stop[xx][yy])
9135           DrawLevelField(xx, yy);
9136         Stop[xx][yy] = TRUE;
9137         changed = TRUE;
9138       }
9139     }
9140   }
9141
9142   if (changed)
9143     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9144                    SND_GAME_OF_LIFE_GROWING);
9145 }
9146
9147 static void InitRobotWheel(int x, int y)
9148 {
9149   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9150 }
9151
9152 static void RunRobotWheel(int x, int y)
9153 {
9154   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9155 }
9156
9157 static void StopRobotWheel(int x, int y)
9158 {
9159   if (ZX == x && ZY == y)
9160   {
9161     ZX = ZY = -1;
9162
9163     game.robot_wheel_active = FALSE;
9164   }
9165 }
9166
9167 static void InitTimegateWheel(int x, int y)
9168 {
9169   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9170 }
9171
9172 static void RunTimegateWheel(int x, int y)
9173 {
9174   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9175 }
9176
9177 static void InitMagicBallDelay(int x, int y)
9178 {
9179 #if 1
9180   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9181 #else
9182   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9183 #endif
9184 }
9185
9186 static void ActivateMagicBall(int bx, int by)
9187 {
9188   int x, y;
9189
9190   if (level.ball_random)
9191   {
9192     int pos_border = RND(8);    /* select one of the eight border elements */
9193     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9194     int xx = pos_content % 3;
9195     int yy = pos_content / 3;
9196
9197     x = bx - 1 + xx;
9198     y = by - 1 + yy;
9199
9200     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9201       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9202   }
9203   else
9204   {
9205     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9206     {
9207       int xx = x - bx + 1;
9208       int yy = y - by + 1;
9209
9210       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9211         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9212     }
9213   }
9214
9215   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9216 }
9217
9218 void CheckExit(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_EXIT_OPENING;
9237
9238   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9239 }
9240
9241 void CheckExitEM(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_EXIT_OPENING;
9260
9261   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9262 }
9263
9264 void CheckExitSteel(int x, int y)
9265 {
9266   if (local_player->gems_still_needed > 0 ||
9267       local_player->sokobanfields_still_needed > 0 ||
9268       local_player->lights_still_needed > 0)
9269   {
9270     int element = Feld[x][y];
9271     int graphic = el2img(element);
9272
9273     if (IS_ANIMATED(graphic))
9274       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9275
9276     return;
9277   }
9278
9279   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9280     return;
9281
9282   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9283
9284   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9285 }
9286
9287 void CheckExitSteelEM(int x, int y)
9288 {
9289   if (local_player->gems_still_needed > 0 ||
9290       local_player->sokobanfields_still_needed > 0 ||
9291       local_player->lights_still_needed > 0)
9292   {
9293     int element = Feld[x][y];
9294     int graphic = el2img(element);
9295
9296     if (IS_ANIMATED(graphic))
9297       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9298
9299     return;
9300   }
9301
9302   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9303     return;
9304
9305   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9306
9307   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9308 }
9309
9310 void CheckExitSP(int x, int y)
9311 {
9312   if (local_player->gems_still_needed > 0)
9313   {
9314     int element = Feld[x][y];
9315     int graphic = el2img(element);
9316
9317     if (IS_ANIMATED(graphic))
9318       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9319
9320     return;
9321   }
9322
9323   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9324     return;
9325
9326   Feld[x][y] = EL_SP_EXIT_OPENING;
9327
9328   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9329 }
9330
9331 static void CloseAllOpenTimegates()
9332 {
9333   int x, y;
9334
9335   SCAN_PLAYFIELD(x, y)
9336   {
9337     int element = Feld[x][y];
9338
9339     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9340     {
9341       Feld[x][y] = EL_TIMEGATE_CLOSING;
9342
9343       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9344     }
9345   }
9346 }
9347
9348 void DrawTwinkleOnField(int x, int y)
9349 {
9350   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9351     return;
9352
9353   if (Feld[x][y] == EL_BD_DIAMOND)
9354     return;
9355
9356   if (MovDelay[x][y] == 0)      /* next animation frame */
9357     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9358
9359   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9360   {
9361     MovDelay[x][y]--;
9362
9363     if (setup.direct_draw && MovDelay[x][y])
9364       SetDrawtoField(DRAW_BUFFERED);
9365
9366     DrawLevelElementAnimation(x, y, Feld[x][y]);
9367
9368     if (MovDelay[x][y] != 0)
9369     {
9370       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9371                                            10 - MovDelay[x][y]);
9372
9373       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9374
9375       if (setup.direct_draw)
9376       {
9377         int dest_x, dest_y;
9378
9379         dest_x = FX + SCREENX(x) * TILEX;
9380         dest_y = FY + SCREENY(y) * TILEY;
9381
9382         BlitBitmap(drawto_field, window,
9383                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9384         SetDrawtoField(DRAW_DIRECT);
9385       }
9386     }
9387   }
9388 }
9389
9390 void MauerWaechst(int x, int y)
9391 {
9392   int delay = 6;
9393
9394   if (!MovDelay[x][y])          /* next animation frame */
9395     MovDelay[x][y] = 3 * delay;
9396
9397   if (MovDelay[x][y])           /* wait some time before next frame */
9398   {
9399     MovDelay[x][y]--;
9400
9401     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9402     {
9403       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9404       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9405
9406       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9407     }
9408
9409     if (!MovDelay[x][y])
9410     {
9411       if (MovDir[x][y] == MV_LEFT)
9412       {
9413         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9414           DrawLevelField(x - 1, y);
9415       }
9416       else if (MovDir[x][y] == MV_RIGHT)
9417       {
9418         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9419           DrawLevelField(x + 1, y);
9420       }
9421       else if (MovDir[x][y] == MV_UP)
9422       {
9423         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9424           DrawLevelField(x, y - 1);
9425       }
9426       else
9427       {
9428         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9429           DrawLevelField(x, y + 1);
9430       }
9431
9432       Feld[x][y] = Store[x][y];
9433       Store[x][y] = 0;
9434       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9435       DrawLevelField(x, y);
9436     }
9437   }
9438 }
9439
9440 void MauerAbleger(int ax, int ay)
9441 {
9442   int element = Feld[ax][ay];
9443   int graphic = el2img(element);
9444   boolean oben_frei = FALSE, unten_frei = FALSE;
9445   boolean links_frei = FALSE, rechts_frei = FALSE;
9446   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9447   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9448   boolean new_wall = FALSE;
9449
9450   if (IS_ANIMATED(graphic))
9451     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9452
9453   if (!MovDelay[ax][ay])        /* start building new wall */
9454     MovDelay[ax][ay] = 6;
9455
9456   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9457   {
9458     MovDelay[ax][ay]--;
9459     if (MovDelay[ax][ay])
9460       return;
9461   }
9462
9463   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9464     oben_frei = TRUE;
9465   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9466     unten_frei = TRUE;
9467   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9468     links_frei = TRUE;
9469   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9470     rechts_frei = TRUE;
9471
9472   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9473       element == EL_EXPANDABLE_WALL_ANY)
9474   {
9475     if (oben_frei)
9476     {
9477       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9478       Store[ax][ay-1] = element;
9479       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9480       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9481         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9482                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9483       new_wall = TRUE;
9484     }
9485     if (unten_frei)
9486     {
9487       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9488       Store[ax][ay+1] = element;
9489       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9490       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9491         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9492                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9493       new_wall = TRUE;
9494     }
9495   }
9496
9497   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9498       element == EL_EXPANDABLE_WALL_ANY ||
9499       element == EL_EXPANDABLE_WALL ||
9500       element == EL_BD_EXPANDABLE_WALL)
9501   {
9502     if (links_frei)
9503     {
9504       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9505       Store[ax-1][ay] = element;
9506       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9507       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9508         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9509                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9510       new_wall = TRUE;
9511     }
9512
9513     if (rechts_frei)
9514     {
9515       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9516       Store[ax+1][ay] = element;
9517       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9518       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9519         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9520                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9521       new_wall = TRUE;
9522     }
9523   }
9524
9525   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9526     DrawLevelField(ax, ay);
9527
9528   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9529     oben_massiv = TRUE;
9530   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9531     unten_massiv = TRUE;
9532   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9533     links_massiv = TRUE;
9534   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9535     rechts_massiv = TRUE;
9536
9537   if (((oben_massiv && unten_massiv) ||
9538        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9539        element == EL_EXPANDABLE_WALL) &&
9540       ((links_massiv && rechts_massiv) ||
9541        element == EL_EXPANDABLE_WALL_VERTICAL))
9542     Feld[ax][ay] = EL_WALL;
9543
9544   if (new_wall)
9545     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9546 }
9547
9548 void MauerAblegerStahl(int ax, int ay)
9549 {
9550   int element = Feld[ax][ay];
9551   int graphic = el2img(element);
9552   boolean oben_frei = FALSE, unten_frei = FALSE;
9553   boolean links_frei = FALSE, rechts_frei = FALSE;
9554   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9555   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9556   boolean new_wall = FALSE;
9557
9558   if (IS_ANIMATED(graphic))
9559     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9560
9561   if (!MovDelay[ax][ay])        /* start building new wall */
9562     MovDelay[ax][ay] = 6;
9563
9564   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9565   {
9566     MovDelay[ax][ay]--;
9567     if (MovDelay[ax][ay])
9568       return;
9569   }
9570
9571   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9572     oben_frei = TRUE;
9573   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9574     unten_frei = TRUE;
9575   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9576     links_frei = TRUE;
9577   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9578     rechts_frei = TRUE;
9579
9580   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9581       element == EL_EXPANDABLE_STEELWALL_ANY)
9582   {
9583     if (oben_frei)
9584     {
9585       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9586       Store[ax][ay-1] = element;
9587       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9588       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9589         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9590                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9591       new_wall = TRUE;
9592     }
9593     if (unten_frei)
9594     {
9595       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9596       Store[ax][ay+1] = element;
9597       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9598       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9599         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9600                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9601       new_wall = TRUE;
9602     }
9603   }
9604
9605   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9606       element == EL_EXPANDABLE_STEELWALL_ANY)
9607   {
9608     if (links_frei)
9609     {
9610       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9611       Store[ax-1][ay] = element;
9612       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9613       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9614         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9615                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9616       new_wall = TRUE;
9617     }
9618
9619     if (rechts_frei)
9620     {
9621       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9622       Store[ax+1][ay] = element;
9623       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9624       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9625         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9626                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9627       new_wall = TRUE;
9628     }
9629   }
9630
9631   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9632     oben_massiv = TRUE;
9633   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9634     unten_massiv = TRUE;
9635   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9636     links_massiv = TRUE;
9637   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9638     rechts_massiv = TRUE;
9639
9640   if (((oben_massiv && unten_massiv) ||
9641        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9642       ((links_massiv && rechts_massiv) ||
9643        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9644     Feld[ax][ay] = EL_WALL;
9645
9646   if (new_wall)
9647     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9648 }
9649
9650 void CheckForDragon(int x, int y)
9651 {
9652   int i, j;
9653   boolean dragon_found = FALSE;
9654   static int xy[4][2] =
9655   {
9656     { 0, -1 },
9657     { -1, 0 },
9658     { +1, 0 },
9659     { 0, +1 }
9660   };
9661
9662   for (i = 0; i < NUM_DIRECTIONS; i++)
9663   {
9664     for (j = 0; j < 4; j++)
9665     {
9666       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9667
9668       if (IN_LEV_FIELD(xx, yy) &&
9669           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9670       {
9671         if (Feld[xx][yy] == EL_DRAGON)
9672           dragon_found = TRUE;
9673       }
9674       else
9675         break;
9676     }
9677   }
9678
9679   if (!dragon_found)
9680   {
9681     for (i = 0; i < NUM_DIRECTIONS; i++)
9682     {
9683       for (j = 0; j < 3; j++)
9684       {
9685         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9686   
9687         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9688         {
9689           Feld[xx][yy] = EL_EMPTY;
9690           DrawLevelField(xx, yy);
9691         }
9692         else
9693           break;
9694       }
9695     }
9696   }
9697 }
9698
9699 static void InitBuggyBase(int x, int y)
9700 {
9701   int element = Feld[x][y];
9702   int activating_delay = FRAMES_PER_SECOND / 4;
9703
9704   ChangeDelay[x][y] =
9705     (element == EL_SP_BUGGY_BASE ?
9706      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9707      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9708      activating_delay :
9709      element == EL_SP_BUGGY_BASE_ACTIVE ?
9710      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9711 }
9712
9713 static void WarnBuggyBase(int x, int y)
9714 {
9715   int i;
9716   static int xy[4][2] =
9717   {
9718     { 0, -1 },
9719     { -1, 0 },
9720     { +1, 0 },
9721     { 0, +1 }
9722   };
9723
9724   for (i = 0; i < NUM_DIRECTIONS; i++)
9725   {
9726     int xx = x + xy[i][0];
9727     int yy = y + xy[i][1];
9728
9729     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9730     {
9731       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9732
9733       break;
9734     }
9735   }
9736 }
9737
9738 static void InitTrap(int x, int y)
9739 {
9740   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9741 }
9742
9743 static void ActivateTrap(int x, int y)
9744 {
9745   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9746 }
9747
9748 static void ChangeActiveTrap(int x, int y)
9749 {
9750   int graphic = IMG_TRAP_ACTIVE;
9751
9752   /* if new animation frame was drawn, correct crumbled sand border */
9753   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9754     DrawLevelFieldCrumbledSand(x, y);
9755 }
9756
9757 static int getSpecialActionElement(int element, int number, int base_element)
9758 {
9759   return (element != EL_EMPTY ? element :
9760           number != -1 ? base_element + number - 1 :
9761           EL_EMPTY);
9762 }
9763
9764 static int getModifiedActionNumber(int value_old, int operator, int operand,
9765                                    int value_min, int value_max)
9766 {
9767   int value_new = (operator == CA_MODE_SET      ? operand :
9768                    operator == CA_MODE_ADD      ? value_old + operand :
9769                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9770                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9771                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9772                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9773                    value_old);
9774
9775   return (value_new < value_min ? value_min :
9776           value_new > value_max ? value_max :
9777           value_new);
9778 }
9779
9780 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9781 {
9782   struct ElementInfo *ei = &element_info[element];
9783   struct ElementChangeInfo *change = &ei->change_page[page];
9784   int target_element = change->target_element;
9785   int action_type = change->action_type;
9786   int action_mode = change->action_mode;
9787   int action_arg = change->action_arg;
9788   int i;
9789
9790   if (!change->has_action)
9791     return;
9792
9793   /* ---------- determine action paramater values -------------------------- */
9794
9795   int level_time_value =
9796     (level.time > 0 ? TimeLeft :
9797      TimePlayed);
9798
9799   int action_arg_element =
9800     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9801      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9802      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9803      EL_EMPTY);
9804
9805   int action_arg_direction =
9806     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9807      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9808      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9809      change->actual_trigger_side :
9810      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9811      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9812      MV_NONE);
9813
9814   int action_arg_number_min =
9815     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9816      CA_ARG_MIN);
9817
9818   int action_arg_number_max =
9819     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9820      action_type == CA_SET_LEVEL_GEMS ? 999 :
9821      action_type == CA_SET_LEVEL_TIME ? 9999 :
9822      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9823      action_type == CA_SET_CE_VALUE ? 9999 :
9824      action_type == CA_SET_CE_SCORE ? 9999 :
9825      CA_ARG_MAX);
9826
9827   int action_arg_number_reset =
9828     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9829      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9830      action_type == CA_SET_LEVEL_TIME ? level.time :
9831      action_type == CA_SET_LEVEL_SCORE ? 0 :
9832 #if USE_NEW_CUSTOM_VALUE
9833      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9834 #else
9835      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9836 #endif
9837      action_type == CA_SET_CE_SCORE ? 0 :
9838      0);
9839
9840   int action_arg_number =
9841     (action_arg <= CA_ARG_MAX ? action_arg :
9842      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9843      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9844      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9845      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9846      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9847      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9848 #if USE_NEW_CUSTOM_VALUE
9849      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9850 #else
9851      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9852 #endif
9853      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9854      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9855      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9856      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9857      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9858      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9859      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9860      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9861      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9862      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9863      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9864      -1);
9865
9866   int action_arg_number_old =
9867     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9868      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9869      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9870      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9871      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9872      0);
9873
9874   int action_arg_number_new =
9875     getModifiedActionNumber(action_arg_number_old,
9876                             action_mode, action_arg_number,
9877                             action_arg_number_min, action_arg_number_max);
9878
9879   int trigger_player_bits =
9880     (change->actual_trigger_player >= EL_PLAYER_1 &&
9881      change->actual_trigger_player <= EL_PLAYER_4 ?
9882      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9883      PLAYER_BITS_ANY);
9884
9885   int action_arg_player_bits =
9886     (action_arg >= CA_ARG_PLAYER_1 &&
9887      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9888      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9889      PLAYER_BITS_ANY);
9890
9891   /* ---------- execute action  -------------------------------------------- */
9892
9893   switch (action_type)
9894   {
9895     case CA_NO_ACTION:
9896     {
9897       return;
9898     }
9899
9900     /* ---------- level actions  ------------------------------------------- */
9901
9902     case CA_RESTART_LEVEL:
9903     {
9904       game.restart_level = TRUE;
9905
9906       break;
9907     }
9908
9909     case CA_SHOW_ENVELOPE:
9910     {
9911       int element = getSpecialActionElement(action_arg_element,
9912                                             action_arg_number, EL_ENVELOPE_1);
9913
9914       if (IS_ENVELOPE(element))
9915         local_player->show_envelope = element;
9916
9917       break;
9918     }
9919
9920     case CA_SET_LEVEL_TIME:
9921     {
9922       if (level.time > 0)       /* only modify limited time value */
9923       {
9924         TimeLeft = action_arg_number_new;
9925
9926 #if 1
9927         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9928
9929         DisplayGameControlValues();
9930 #else
9931         DrawGameValue_Time(TimeLeft);
9932 #endif
9933
9934         if (!TimeLeft && setup.time_limit)
9935           for (i = 0; i < MAX_PLAYERS; i++)
9936             KillPlayer(&stored_player[i]);
9937       }
9938
9939       break;
9940     }
9941
9942     case CA_SET_LEVEL_SCORE:
9943     {
9944       local_player->score = action_arg_number_new;
9945
9946 #if 1
9947       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9948
9949       DisplayGameControlValues();
9950 #else
9951       DrawGameValue_Score(local_player->score);
9952 #endif
9953
9954       break;
9955     }
9956
9957     case CA_SET_LEVEL_GEMS:
9958     {
9959       local_player->gems_still_needed = action_arg_number_new;
9960
9961 #if 1
9962       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9963
9964       DisplayGameControlValues();
9965 #else
9966       DrawGameValue_Emeralds(local_player->gems_still_needed);
9967 #endif
9968
9969       break;
9970     }
9971
9972 #if !USE_PLAYER_GRAVITY
9973     case CA_SET_LEVEL_GRAVITY:
9974     {
9975       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9976                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9977                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9978                       game.gravity);
9979       break;
9980     }
9981 #endif
9982
9983     case CA_SET_LEVEL_WIND:
9984     {
9985       game.wind_direction = action_arg_direction;
9986
9987       break;
9988     }
9989
9990     /* ---------- player actions  ------------------------------------------ */
9991
9992     case CA_MOVE_PLAYER:
9993     {
9994       /* automatically move to the next field in specified direction */
9995       for (i = 0; i < MAX_PLAYERS; i++)
9996         if (trigger_player_bits & (1 << i))
9997           stored_player[i].programmed_action = action_arg_direction;
9998
9999       break;
10000     }
10001
10002     case CA_EXIT_PLAYER:
10003     {
10004       for (i = 0; i < MAX_PLAYERS; i++)
10005         if (action_arg_player_bits & (1 << i))
10006           PlayerWins(&stored_player[i]);
10007
10008       break;
10009     }
10010
10011     case CA_KILL_PLAYER:
10012     {
10013       for (i = 0; i < MAX_PLAYERS; i++)
10014         if (action_arg_player_bits & (1 << i))
10015           KillPlayer(&stored_player[i]);
10016
10017       break;
10018     }
10019
10020     case CA_SET_PLAYER_KEYS:
10021     {
10022       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10023       int element = getSpecialActionElement(action_arg_element,
10024                                             action_arg_number, EL_KEY_1);
10025
10026       if (IS_KEY(element))
10027       {
10028         for (i = 0; i < MAX_PLAYERS; i++)
10029         {
10030           if (trigger_player_bits & (1 << i))
10031           {
10032             stored_player[i].key[KEY_NR(element)] = key_state;
10033
10034             DrawGameDoorValues();
10035           }
10036         }
10037       }
10038
10039       break;
10040     }
10041
10042     case CA_SET_PLAYER_SPEED:
10043     {
10044       for (i = 0; i < MAX_PLAYERS; i++)
10045       {
10046         if (trigger_player_bits & (1 << i))
10047         {
10048           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10049
10050           if (action_arg == CA_ARG_SPEED_FASTER &&
10051               stored_player[i].cannot_move)
10052           {
10053             action_arg_number = STEPSIZE_VERY_SLOW;
10054           }
10055           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10056                    action_arg == CA_ARG_SPEED_FASTER)
10057           {
10058             action_arg_number = 2;
10059             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10060                            CA_MODE_MULTIPLY);
10061           }
10062           else if (action_arg == CA_ARG_NUMBER_RESET)
10063           {
10064             action_arg_number = level.initial_player_stepsize[i];
10065           }
10066
10067           move_stepsize =
10068             getModifiedActionNumber(move_stepsize,
10069                                     action_mode,
10070                                     action_arg_number,
10071                                     action_arg_number_min,
10072                                     action_arg_number_max);
10073
10074           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10075         }
10076       }
10077
10078       break;
10079     }
10080
10081     case CA_SET_PLAYER_SHIELD:
10082     {
10083       for (i = 0; i < MAX_PLAYERS; i++)
10084       {
10085         if (trigger_player_bits & (1 << i))
10086         {
10087           if (action_arg == CA_ARG_SHIELD_OFF)
10088           {
10089             stored_player[i].shield_normal_time_left = 0;
10090             stored_player[i].shield_deadly_time_left = 0;
10091           }
10092           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10093           {
10094             stored_player[i].shield_normal_time_left = 999999;
10095           }
10096           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10097           {
10098             stored_player[i].shield_normal_time_left = 999999;
10099             stored_player[i].shield_deadly_time_left = 999999;
10100           }
10101         }
10102       }
10103
10104       break;
10105     }
10106
10107 #if USE_PLAYER_GRAVITY
10108     case CA_SET_PLAYER_GRAVITY:
10109     {
10110       for (i = 0; i < MAX_PLAYERS; i++)
10111       {
10112         if (trigger_player_bits & (1 << i))
10113         {
10114           stored_player[i].gravity =
10115             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10116              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10117              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10118              stored_player[i].gravity);
10119         }
10120       }
10121
10122       break;
10123     }
10124 #endif
10125
10126     case CA_SET_PLAYER_ARTWORK:
10127     {
10128       for (i = 0; i < MAX_PLAYERS; i++)
10129       {
10130         if (trigger_player_bits & (1 << i))
10131         {
10132           int artwork_element = action_arg_element;
10133
10134           if (action_arg == CA_ARG_ELEMENT_RESET)
10135             artwork_element =
10136               (level.use_artwork_element[i] ? level.artwork_element[i] :
10137                stored_player[i].element_nr);
10138
10139 #if USE_GFX_RESET_PLAYER_ARTWORK
10140           if (stored_player[i].artwork_element != artwork_element)
10141             stored_player[i].Frame = 0;
10142 #endif
10143
10144           stored_player[i].artwork_element = artwork_element;
10145
10146           SetPlayerWaiting(&stored_player[i], FALSE);
10147
10148           /* set number of special actions for bored and sleeping animation */
10149           stored_player[i].num_special_action_bored =
10150             get_num_special_action(artwork_element,
10151                                    ACTION_BORING_1, ACTION_BORING_LAST);
10152           stored_player[i].num_special_action_sleeping =
10153             get_num_special_action(artwork_element,
10154                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10155         }
10156       }
10157
10158       break;
10159     }
10160
10161     /* ---------- CE actions  ---------------------------------------------- */
10162
10163     case CA_SET_CE_VALUE:
10164     {
10165 #if USE_NEW_CUSTOM_VALUE
10166       int last_ce_value = CustomValue[x][y];
10167
10168       CustomValue[x][y] = action_arg_number_new;
10169
10170       if (CustomValue[x][y] != last_ce_value)
10171       {
10172         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10173         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10174
10175         if (CustomValue[x][y] == 0)
10176         {
10177           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10178           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10179         }
10180       }
10181 #endif
10182
10183       break;
10184     }
10185
10186     case CA_SET_CE_SCORE:
10187     {
10188 #if USE_NEW_CUSTOM_VALUE
10189       int last_ce_score = ei->collect_score;
10190
10191       ei->collect_score = action_arg_number_new;
10192
10193       if (ei->collect_score != last_ce_score)
10194       {
10195         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10196         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10197
10198         if (ei->collect_score == 0)
10199         {
10200           int xx, yy;
10201
10202           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10203           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10204
10205           /*
10206             This is a very special case that seems to be a mixture between
10207             CheckElementChange() and CheckTriggeredElementChange(): while
10208             the first one only affects single elements that are triggered
10209             directly, the second one affects multiple elements in the playfield
10210             that are triggered indirectly by another element. This is a third
10211             case: Changing the CE score always affects multiple identical CEs,
10212             so every affected CE must be checked, not only the single CE for
10213             which the CE score was changed in the first place (as every instance
10214             of that CE shares the same CE score, and therefore also can change)!
10215           */
10216           SCAN_PLAYFIELD(xx, yy)
10217           {
10218             if (Feld[xx][yy] == element)
10219               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10220                                  CE_SCORE_GETS_ZERO);
10221           }
10222         }
10223       }
10224 #endif
10225
10226       break;
10227     }
10228
10229     /* ---------- engine actions  ------------------------------------------ */
10230
10231     case CA_SET_ENGINE_SCAN_MODE:
10232     {
10233       InitPlayfieldScanMode(action_arg);
10234
10235       break;
10236     }
10237
10238     default:
10239       break;
10240   }
10241 }
10242
10243 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10244 {
10245   int old_element = Feld[x][y];
10246   int new_element = GetElementFromGroupElement(element);
10247   int previous_move_direction = MovDir[x][y];
10248 #if USE_NEW_CUSTOM_VALUE
10249   int last_ce_value = CustomValue[x][y];
10250 #endif
10251   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10252   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10253   boolean add_player_onto_element = (new_element_is_player &&
10254 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10255                                      /* this breaks SnakeBite when a snake is
10256                                         halfway through a door that closes */
10257                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10258                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10259 #endif
10260                                      IS_WALKABLE(old_element));
10261
10262 #if 0
10263   /* check if element under the player changes from accessible to unaccessible
10264      (needed for special case of dropping element which then changes) */
10265   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10266       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10267   {
10268     Bang(x, y);
10269
10270     return;
10271   }
10272 #endif
10273
10274   if (!add_player_onto_element)
10275   {
10276     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10277       RemoveMovingField(x, y);
10278     else
10279       RemoveField(x, y);
10280
10281     Feld[x][y] = new_element;
10282
10283 #if !USE_GFX_RESET_GFX_ANIMATION
10284     ResetGfxAnimation(x, y);
10285     ResetRandomAnimationValue(x, y);
10286 #endif
10287
10288     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10289       MovDir[x][y] = previous_move_direction;
10290
10291 #if USE_NEW_CUSTOM_VALUE
10292     if (element_info[new_element].use_last_ce_value)
10293       CustomValue[x][y] = last_ce_value;
10294 #endif
10295
10296     InitField_WithBug1(x, y, FALSE);
10297
10298     new_element = Feld[x][y];   /* element may have changed */
10299
10300 #if USE_GFX_RESET_GFX_ANIMATION
10301     ResetGfxAnimation(x, y);
10302     ResetRandomAnimationValue(x, y);
10303 #endif
10304
10305     DrawLevelField(x, y);
10306
10307     if (GFX_CRUMBLED(new_element))
10308       DrawLevelFieldCrumbledSandNeighbours(x, y);
10309   }
10310
10311 #if 1
10312   /* check if element under the player changes from accessible to unaccessible
10313      (needed for special case of dropping element which then changes) */
10314   /* (must be checked after creating new element for walkable group elements) */
10315 #if USE_FIX_KILLED_BY_NON_WALKABLE
10316   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10317       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10318   {
10319     Bang(x, y);
10320
10321     return;
10322   }
10323 #else
10324   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10325       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10326   {
10327     Bang(x, y);
10328
10329     return;
10330   }
10331 #endif
10332 #endif
10333
10334   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10335   if (new_element_is_player)
10336     RelocatePlayer(x, y, new_element);
10337
10338   if (is_change)
10339     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10340
10341   TestIfBadThingTouchesPlayer(x, y);
10342   TestIfPlayerTouchesCustomElement(x, y);
10343   TestIfElementTouchesCustomElement(x, y);
10344 }
10345
10346 static void CreateField(int x, int y, int element)
10347 {
10348   CreateFieldExt(x, y, element, FALSE);
10349 }
10350
10351 static void CreateElementFromChange(int x, int y, int element)
10352 {
10353   element = GET_VALID_RUNTIME_ELEMENT(element);
10354
10355 #if USE_STOP_CHANGED_ELEMENTS
10356   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10357   {
10358     int old_element = Feld[x][y];
10359
10360     /* prevent changed element from moving in same engine frame
10361        unless both old and new element can either fall or move */
10362     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10363         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10364       Stop[x][y] = TRUE;
10365   }
10366 #endif
10367
10368   CreateFieldExt(x, y, element, TRUE);
10369 }
10370
10371 static boolean ChangeElement(int x, int y, int element, int page)
10372 {
10373   struct ElementInfo *ei = &element_info[element];
10374   struct ElementChangeInfo *change = &ei->change_page[page];
10375   int ce_value = CustomValue[x][y];
10376   int ce_score = ei->collect_score;
10377   int target_element;
10378   int old_element = Feld[x][y];
10379
10380   /* always use default change event to prevent running into a loop */
10381   if (ChangeEvent[x][y] == -1)
10382     ChangeEvent[x][y] = CE_DELAY;
10383
10384   if (ChangeEvent[x][y] == CE_DELAY)
10385   {
10386     /* reset actual trigger element, trigger player and action element */
10387     change->actual_trigger_element = EL_EMPTY;
10388     change->actual_trigger_player = EL_PLAYER_1;
10389     change->actual_trigger_side = CH_SIDE_NONE;
10390     change->actual_trigger_ce_value = 0;
10391     change->actual_trigger_ce_score = 0;
10392   }
10393
10394   /* do not change elements more than a specified maximum number of changes */
10395   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10396     return FALSE;
10397
10398   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10399
10400   if (change->explode)
10401   {
10402     Bang(x, y);
10403
10404     return TRUE;
10405   }
10406
10407   if (change->use_target_content)
10408   {
10409     boolean complete_replace = TRUE;
10410     boolean can_replace[3][3];
10411     int xx, yy;
10412
10413     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10414     {
10415       boolean is_empty;
10416       boolean is_walkable;
10417       boolean is_diggable;
10418       boolean is_collectible;
10419       boolean is_removable;
10420       boolean is_destructible;
10421       int ex = x + xx - 1;
10422       int ey = y + yy - 1;
10423       int content_element = change->target_content.e[xx][yy];
10424       int e;
10425
10426       can_replace[xx][yy] = TRUE;
10427
10428       if (ex == x && ey == y)   /* do not check changing element itself */
10429         continue;
10430
10431       if (content_element == EL_EMPTY_SPACE)
10432       {
10433         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10434
10435         continue;
10436       }
10437
10438       if (!IN_LEV_FIELD(ex, ey))
10439       {
10440         can_replace[xx][yy] = FALSE;
10441         complete_replace = FALSE;
10442
10443         continue;
10444       }
10445
10446       e = Feld[ex][ey];
10447
10448       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10449         e = MovingOrBlocked2Element(ex, ey);
10450
10451       is_empty = (IS_FREE(ex, ey) ||
10452                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10453
10454       is_walkable     = (is_empty || IS_WALKABLE(e));
10455       is_diggable     = (is_empty || IS_DIGGABLE(e));
10456       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10457       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10458       is_removable    = (is_diggable || is_collectible);
10459
10460       can_replace[xx][yy] =
10461         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10462           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10463           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10464           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10465           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10466           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10467          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10468
10469       if (!can_replace[xx][yy])
10470         complete_replace = FALSE;
10471     }
10472
10473     if (!change->only_if_complete || complete_replace)
10474     {
10475       boolean something_has_changed = FALSE;
10476
10477       if (change->only_if_complete && change->use_random_replace &&
10478           RND(100) < change->random_percentage)
10479         return FALSE;
10480
10481       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10482       {
10483         int ex = x + xx - 1;
10484         int ey = y + yy - 1;
10485         int content_element;
10486
10487         if (can_replace[xx][yy] && (!change->use_random_replace ||
10488                                     RND(100) < change->random_percentage))
10489         {
10490           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10491             RemoveMovingField(ex, ey);
10492
10493           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10494
10495           content_element = change->target_content.e[xx][yy];
10496           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10497                                               ce_value, ce_score);
10498
10499           CreateElementFromChange(ex, ey, target_element);
10500
10501           something_has_changed = TRUE;
10502
10503           /* for symmetry reasons, freeze newly created border elements */
10504           if (ex != x || ey != y)
10505             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10506         }
10507       }
10508
10509       if (something_has_changed)
10510       {
10511         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10512         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10513       }
10514     }
10515   }
10516   else
10517   {
10518     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10519                                         ce_value, ce_score);
10520
10521     if (element == EL_DIAGONAL_GROWING ||
10522         element == EL_DIAGONAL_SHRINKING)
10523     {
10524       target_element = Store[x][y];
10525
10526       Store[x][y] = EL_EMPTY;
10527     }
10528
10529     CreateElementFromChange(x, y, target_element);
10530
10531     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10532     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10533   }
10534
10535   /* this uses direct change before indirect change */
10536   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10537
10538   return TRUE;
10539 }
10540
10541 #if USE_NEW_DELAYED_ACTION
10542
10543 static void HandleElementChange(int x, int y, int page)
10544 {
10545   int element = MovingOrBlocked2Element(x, y);
10546   struct ElementInfo *ei = &element_info[element];
10547   struct ElementChangeInfo *change = &ei->change_page[page];
10548
10549 #ifdef DEBUG
10550   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10551       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10552   {
10553     printf("\n\n");
10554     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10555            x, y, element, element_info[element].token_name);
10556     printf("HandleElementChange(): This should never happen!\n");
10557     printf("\n\n");
10558   }
10559 #endif
10560
10561   /* this can happen with classic bombs on walkable, changing elements */
10562   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10563   {
10564 #if 0
10565     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10566       ChangeDelay[x][y] = 0;
10567 #endif
10568
10569     return;
10570   }
10571
10572   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10573   {
10574     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10575
10576     if (change->can_change)
10577     {
10578 #if 1
10579       /* !!! not clear why graphic animation should be reset at all here !!! */
10580       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10581 #if USE_GFX_RESET_WHEN_NOT_MOVING
10582       /* when a custom element is about to change (for example by change delay),
10583          do not reset graphic animation when the custom element is moving */
10584       if (!IS_MOVING(x, y))
10585 #endif
10586       {
10587         ResetGfxAnimation(x, y);
10588         ResetRandomAnimationValue(x, y);
10589       }
10590 #endif
10591
10592       if (change->pre_change_function)
10593         change->pre_change_function(x, y);
10594     }
10595   }
10596
10597   ChangeDelay[x][y]--;
10598
10599   if (ChangeDelay[x][y] != 0)           /* continue element change */
10600   {
10601     if (change->can_change)
10602     {
10603       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10604
10605       if (IS_ANIMATED(graphic))
10606         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10607
10608       if (change->change_function)
10609         change->change_function(x, y);
10610     }
10611   }
10612   else                                  /* finish element change */
10613   {
10614     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10615     {
10616       page = ChangePage[x][y];
10617       ChangePage[x][y] = -1;
10618
10619       change = &ei->change_page[page];
10620     }
10621
10622     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10623     {
10624       ChangeDelay[x][y] = 1;            /* try change after next move step */
10625       ChangePage[x][y] = page;          /* remember page to use for change */
10626
10627       return;
10628     }
10629
10630     if (change->can_change)
10631     {
10632       if (ChangeElement(x, y, element, page))
10633       {
10634         if (change->post_change_function)
10635           change->post_change_function(x, y);
10636       }
10637     }
10638
10639     if (change->has_action)
10640       ExecuteCustomElementAction(x, y, element, page);
10641   }
10642 }
10643
10644 #else
10645
10646 static void HandleElementChange(int x, int y, int page)
10647 {
10648   int element = MovingOrBlocked2Element(x, y);
10649   struct ElementInfo *ei = &element_info[element];
10650   struct ElementChangeInfo *change = &ei->change_page[page];
10651
10652 #ifdef DEBUG
10653   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10654   {
10655     printf("\n\n");
10656     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10657            x, y, element, element_info[element].token_name);
10658     printf("HandleElementChange(): This should never happen!\n");
10659     printf("\n\n");
10660   }
10661 #endif
10662
10663   /* this can happen with classic bombs on walkable, changing elements */
10664   if (!CAN_CHANGE(element))
10665   {
10666 #if 0
10667     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10668       ChangeDelay[x][y] = 0;
10669 #endif
10670
10671     return;
10672   }
10673
10674   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10675   {
10676     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10677
10678     ResetGfxAnimation(x, y);
10679     ResetRandomAnimationValue(x, y);
10680
10681     if (change->pre_change_function)
10682       change->pre_change_function(x, y);
10683   }
10684
10685   ChangeDelay[x][y]--;
10686
10687   if (ChangeDelay[x][y] != 0)           /* continue element change */
10688   {
10689     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10690
10691     if (IS_ANIMATED(graphic))
10692       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10693
10694     if (change->change_function)
10695       change->change_function(x, y);
10696   }
10697   else                                  /* finish element change */
10698   {
10699     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10700     {
10701       page = ChangePage[x][y];
10702       ChangePage[x][y] = -1;
10703
10704       change = &ei->change_page[page];
10705     }
10706
10707     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10708     {
10709       ChangeDelay[x][y] = 1;            /* try change after next move step */
10710       ChangePage[x][y] = page;          /* remember page to use for change */
10711
10712       return;
10713     }
10714
10715     if (ChangeElement(x, y, element, page))
10716     {
10717       if (change->post_change_function)
10718         change->post_change_function(x, y);
10719     }
10720   }
10721 }
10722
10723 #endif
10724
10725 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10726                                               int trigger_element,
10727                                               int trigger_event,
10728                                               int trigger_player,
10729                                               int trigger_side,
10730                                               int trigger_page)
10731 {
10732   boolean change_done_any = FALSE;
10733   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10734   int i;
10735
10736   if (!(trigger_events[trigger_element][trigger_event]))
10737     return FALSE;
10738
10739 #if 0
10740   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10741          trigger_event, recursion_loop_depth, recursion_loop_detected,
10742          recursion_loop_element, EL_NAME(recursion_loop_element));
10743 #endif
10744
10745   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10746
10747   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10748   {
10749     int element = EL_CUSTOM_START + i;
10750     boolean change_done = FALSE;
10751     int p;
10752
10753     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10754         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10755       continue;
10756
10757     for (p = 0; p < element_info[element].num_change_pages; p++)
10758     {
10759       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10760
10761       if (change->can_change_or_has_action &&
10762           change->has_event[trigger_event] &&
10763           change->trigger_side & trigger_side &&
10764           change->trigger_player & trigger_player &&
10765           change->trigger_page & trigger_page_bits &&
10766           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10767       {
10768         change->actual_trigger_element = trigger_element;
10769         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10770         change->actual_trigger_side = trigger_side;
10771         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10772         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10773
10774         if ((change->can_change && !change_done) || change->has_action)
10775         {
10776           int x, y;
10777
10778           SCAN_PLAYFIELD(x, y)
10779           {
10780             if (Feld[x][y] == element)
10781             {
10782               if (change->can_change && !change_done)
10783               {
10784                 ChangeDelay[x][y] = 1;
10785                 ChangeEvent[x][y] = trigger_event;
10786
10787                 HandleElementChange(x, y, p);
10788               }
10789 #if USE_NEW_DELAYED_ACTION
10790               else if (change->has_action)
10791               {
10792                 ExecuteCustomElementAction(x, y, element, p);
10793                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10794               }
10795 #else
10796               if (change->has_action)
10797               {
10798                 ExecuteCustomElementAction(x, y, element, p);
10799                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10800               }
10801 #endif
10802             }
10803           }
10804
10805           if (change->can_change)
10806           {
10807             change_done = TRUE;
10808             change_done_any = TRUE;
10809           }
10810         }
10811       }
10812     }
10813   }
10814
10815   RECURSION_LOOP_DETECTION_END();
10816
10817   return change_done_any;
10818 }
10819
10820 static boolean CheckElementChangeExt(int x, int y,
10821                                      int element,
10822                                      int trigger_element,
10823                                      int trigger_event,
10824                                      int trigger_player,
10825                                      int trigger_side)
10826 {
10827   boolean change_done = FALSE;
10828   int p;
10829
10830   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10831       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10832     return FALSE;
10833
10834   if (Feld[x][y] == EL_BLOCKED)
10835   {
10836     Blocked2Moving(x, y, &x, &y);
10837     element = Feld[x][y];
10838   }
10839
10840 #if 0
10841   /* check if element has already changed */
10842   if (Feld[x][y] != element)
10843     return FALSE;
10844 #else
10845   /* check if element has already changed or is about to change after moving */
10846   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10847        Feld[x][y] != element) ||
10848
10849       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10850        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10851         ChangePage[x][y] != -1)))
10852     return FALSE;
10853 #endif
10854
10855 #if 0
10856   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10857          trigger_event, recursion_loop_depth, recursion_loop_detected,
10858          recursion_loop_element, EL_NAME(recursion_loop_element));
10859 #endif
10860
10861   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10862
10863   for (p = 0; p < element_info[element].num_change_pages; p++)
10864   {
10865     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10866
10867     /* check trigger element for all events where the element that is checked
10868        for changing interacts with a directly adjacent element -- this is
10869        different to element changes that affect other elements to change on the
10870        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10871     boolean check_trigger_element =
10872       (trigger_event == CE_TOUCHING_X ||
10873        trigger_event == CE_HITTING_X ||
10874        trigger_event == CE_HIT_BY_X ||
10875 #if 1
10876        /* this one was forgotten until 3.2.3 */
10877        trigger_event == CE_DIGGING_X);
10878 #endif
10879
10880     if (change->can_change_or_has_action &&
10881         change->has_event[trigger_event] &&
10882         change->trigger_side & trigger_side &&
10883         change->trigger_player & trigger_player &&
10884         (!check_trigger_element ||
10885          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10886     {
10887       change->actual_trigger_element = trigger_element;
10888       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10889       change->actual_trigger_side = trigger_side;
10890       change->actual_trigger_ce_value = CustomValue[x][y];
10891       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10892
10893       /* special case: trigger element not at (x,y) position for some events */
10894       if (check_trigger_element)
10895       {
10896         static struct
10897         {
10898           int dx, dy;
10899         } move_xy[] =
10900           {
10901             {  0,  0 },
10902             { -1,  0 },
10903             { +1,  0 },
10904             {  0,  0 },
10905             {  0, -1 },
10906             {  0,  0 }, { 0, 0 }, { 0, 0 },
10907             {  0, +1 }
10908           };
10909
10910         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10911         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10912
10913         change->actual_trigger_ce_value = CustomValue[xx][yy];
10914         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10915       }
10916
10917       if (change->can_change && !change_done)
10918       {
10919         ChangeDelay[x][y] = 1;
10920         ChangeEvent[x][y] = trigger_event;
10921
10922         HandleElementChange(x, y, p);
10923
10924         change_done = TRUE;
10925       }
10926 #if USE_NEW_DELAYED_ACTION
10927       else if (change->has_action)
10928       {
10929         ExecuteCustomElementAction(x, y, element, p);
10930         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10931       }
10932 #else
10933       if (change->has_action)
10934       {
10935         ExecuteCustomElementAction(x, y, element, p);
10936         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10937       }
10938 #endif
10939     }
10940   }
10941
10942   RECURSION_LOOP_DETECTION_END();
10943
10944   return change_done;
10945 }
10946
10947 static void PlayPlayerSound(struct PlayerInfo *player)
10948 {
10949   int jx = player->jx, jy = player->jy;
10950   int sound_element = player->artwork_element;
10951   int last_action = player->last_action_waiting;
10952   int action = player->action_waiting;
10953
10954   if (player->is_waiting)
10955   {
10956     if (action != last_action)
10957       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10958     else
10959       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10960   }
10961   else
10962   {
10963     if (action != last_action)
10964       StopSound(element_info[sound_element].sound[last_action]);
10965
10966     if (last_action == ACTION_SLEEPING)
10967       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10968   }
10969 }
10970
10971 static void PlayAllPlayersSound()
10972 {
10973   int i;
10974
10975   for (i = 0; i < MAX_PLAYERS; i++)
10976     if (stored_player[i].active)
10977       PlayPlayerSound(&stored_player[i]);
10978 }
10979
10980 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10981 {
10982   boolean last_waiting = player->is_waiting;
10983   int move_dir = player->MovDir;
10984
10985   player->dir_waiting = move_dir;
10986   player->last_action_waiting = player->action_waiting;
10987
10988   if (is_waiting)
10989   {
10990     if (!last_waiting)          /* not waiting -> waiting */
10991     {
10992       player->is_waiting = TRUE;
10993
10994       player->frame_counter_bored =
10995         FrameCounter +
10996         game.player_boring_delay_fixed +
10997         GetSimpleRandom(game.player_boring_delay_random);
10998       player->frame_counter_sleeping =
10999         FrameCounter +
11000         game.player_sleeping_delay_fixed +
11001         GetSimpleRandom(game.player_sleeping_delay_random);
11002
11003       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11004     }
11005
11006     if (game.player_sleeping_delay_fixed +
11007         game.player_sleeping_delay_random > 0 &&
11008         player->anim_delay_counter == 0 &&
11009         player->post_delay_counter == 0 &&
11010         FrameCounter >= player->frame_counter_sleeping)
11011       player->is_sleeping = TRUE;
11012     else if (game.player_boring_delay_fixed +
11013              game.player_boring_delay_random > 0 &&
11014              FrameCounter >= player->frame_counter_bored)
11015       player->is_bored = TRUE;
11016
11017     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11018                               player->is_bored ? ACTION_BORING :
11019                               ACTION_WAITING);
11020
11021     if (player->is_sleeping && player->use_murphy)
11022     {
11023       /* special case for sleeping Murphy when leaning against non-free tile */
11024
11025       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11026           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11027            !IS_MOVING(player->jx - 1, player->jy)))
11028         move_dir = MV_LEFT;
11029       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11030                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11031                 !IS_MOVING(player->jx + 1, player->jy)))
11032         move_dir = MV_RIGHT;
11033       else
11034         player->is_sleeping = FALSE;
11035
11036       player->dir_waiting = move_dir;
11037     }
11038
11039     if (player->is_sleeping)
11040     {
11041       if (player->num_special_action_sleeping > 0)
11042       {
11043         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11044         {
11045           int last_special_action = player->special_action_sleeping;
11046           int num_special_action = player->num_special_action_sleeping;
11047           int special_action =
11048             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11049              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11050              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11051              last_special_action + 1 : ACTION_SLEEPING);
11052           int special_graphic =
11053             el_act_dir2img(player->artwork_element, special_action, move_dir);
11054
11055           player->anim_delay_counter =
11056             graphic_info[special_graphic].anim_delay_fixed +
11057             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11058           player->post_delay_counter =
11059             graphic_info[special_graphic].post_delay_fixed +
11060             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11061
11062           player->special_action_sleeping = special_action;
11063         }
11064
11065         if (player->anim_delay_counter > 0)
11066         {
11067           player->action_waiting = player->special_action_sleeping;
11068           player->anim_delay_counter--;
11069         }
11070         else if (player->post_delay_counter > 0)
11071         {
11072           player->post_delay_counter--;
11073         }
11074       }
11075     }
11076     else if (player->is_bored)
11077     {
11078       if (player->num_special_action_bored > 0)
11079       {
11080         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11081         {
11082           int special_action =
11083             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11084           int special_graphic =
11085             el_act_dir2img(player->artwork_element, special_action, move_dir);
11086
11087           player->anim_delay_counter =
11088             graphic_info[special_graphic].anim_delay_fixed +
11089             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11090           player->post_delay_counter =
11091             graphic_info[special_graphic].post_delay_fixed +
11092             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11093
11094           player->special_action_bored = special_action;
11095         }
11096
11097         if (player->anim_delay_counter > 0)
11098         {
11099           player->action_waiting = player->special_action_bored;
11100           player->anim_delay_counter--;
11101         }
11102         else if (player->post_delay_counter > 0)
11103         {
11104           player->post_delay_counter--;
11105         }
11106       }
11107     }
11108   }
11109   else if (last_waiting)        /* waiting -> not waiting */
11110   {
11111     player->is_waiting = FALSE;
11112     player->is_bored = FALSE;
11113     player->is_sleeping = FALSE;
11114
11115     player->frame_counter_bored = -1;
11116     player->frame_counter_sleeping = -1;
11117
11118     player->anim_delay_counter = 0;
11119     player->post_delay_counter = 0;
11120
11121     player->dir_waiting = player->MovDir;
11122     player->action_waiting = ACTION_DEFAULT;
11123
11124     player->special_action_bored = ACTION_DEFAULT;
11125     player->special_action_sleeping = ACTION_DEFAULT;
11126   }
11127 }
11128
11129 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11130 {
11131   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11132   int left      = player_action & JOY_LEFT;
11133   int right     = player_action & JOY_RIGHT;
11134   int up        = player_action & JOY_UP;
11135   int down      = player_action & JOY_DOWN;
11136   int button1   = player_action & JOY_BUTTON_1;
11137   int button2   = player_action & JOY_BUTTON_2;
11138   int dx        = (left ? -1 : right ? 1 : 0);
11139   int dy        = (up   ? -1 : down  ? 1 : 0);
11140
11141   if (!player->active || tape.pausing)
11142     return 0;
11143
11144   if (player_action)
11145   {
11146     if (button1)
11147       snapped = SnapField(player, dx, dy);
11148     else
11149     {
11150       if (button2)
11151         dropped = DropElement(player);
11152
11153       moved = MovePlayer(player, dx, dy);
11154     }
11155
11156     if (tape.single_step && tape.recording && !tape.pausing)
11157     {
11158       if (button1 || (dropped && !moved))
11159       {
11160         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11161         SnapField(player, 0, 0);                /* stop snapping */
11162       }
11163     }
11164
11165     SetPlayerWaiting(player, FALSE);
11166
11167     return player_action;
11168   }
11169   else
11170   {
11171     /* no actions for this player (no input at player's configured device) */
11172
11173     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11174     SnapField(player, 0, 0);
11175     CheckGravityMovementWhenNotMoving(player);
11176
11177     if (player->MovPos == 0)
11178       SetPlayerWaiting(player, TRUE);
11179
11180     if (player->MovPos == 0)    /* needed for tape.playing */
11181       player->is_moving = FALSE;
11182
11183     player->is_dropping = FALSE;
11184     player->is_dropping_pressed = FALSE;
11185     player->drop_pressed_delay = 0;
11186
11187     return 0;
11188   }
11189 }
11190
11191 static void CheckLevelTime()
11192 {
11193   int i;
11194
11195   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11196   {
11197     if (level.native_em_level->lev->home == 0)  /* all players at home */
11198     {
11199       PlayerWins(local_player);
11200
11201       AllPlayersGone = TRUE;
11202
11203       level.native_em_level->lev->home = -1;
11204     }
11205
11206     if (level.native_em_level->ply[0]->alive == 0 &&
11207         level.native_em_level->ply[1]->alive == 0 &&
11208         level.native_em_level->ply[2]->alive == 0 &&
11209         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11210       AllPlayersGone = TRUE;
11211   }
11212
11213   if (TimeFrames >= FRAMES_PER_SECOND)
11214   {
11215     TimeFrames = 0;
11216     TapeTime++;
11217
11218     for (i = 0; i < MAX_PLAYERS; i++)
11219     {
11220       struct PlayerInfo *player = &stored_player[i];
11221
11222       if (SHIELD_ON(player))
11223       {
11224         player->shield_normal_time_left--;
11225
11226         if (player->shield_deadly_time_left > 0)
11227           player->shield_deadly_time_left--;
11228       }
11229     }
11230
11231     if (!local_player->LevelSolved && !level.use_step_counter)
11232     {
11233       TimePlayed++;
11234
11235       if (TimeLeft > 0)
11236       {
11237         TimeLeft--;
11238
11239         if (TimeLeft <= 10 && setup.time_limit)
11240           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11241
11242 #if 1
11243         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11244
11245         DisplayGameControlValues();
11246 #else
11247         DrawGameValue_Time(TimeLeft);
11248 #endif
11249
11250         if (!TimeLeft && setup.time_limit)
11251         {
11252           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11253             level.native_em_level->lev->killed_out_of_time = TRUE;
11254           else
11255             for (i = 0; i < MAX_PLAYERS; i++)
11256               KillPlayer(&stored_player[i]);
11257         }
11258       }
11259 #if 1
11260       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11261       {
11262         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11263
11264         DisplayGameControlValues();
11265       }
11266 #else
11267       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11268         DrawGameValue_Time(TimePlayed);
11269 #endif
11270
11271       level.native_em_level->lev->time =
11272         (level.time == 0 ? TimePlayed : TimeLeft);
11273     }
11274
11275     if (tape.recording || tape.playing)
11276       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11277   }
11278
11279   UpdateGameDoorValues();
11280   DrawGameDoorValues();
11281 }
11282
11283 void AdvanceFrameAndPlayerCounters(int player_nr)
11284 {
11285   int i;
11286
11287   /* advance frame counters (global frame counter and time frame counter) */
11288   FrameCounter++;
11289   TimeFrames++;
11290
11291   /* advance player counters (counters for move delay, move animation etc.) */
11292   for (i = 0; i < MAX_PLAYERS; i++)
11293   {
11294     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11295     int move_delay_value = stored_player[i].move_delay_value;
11296     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11297
11298     if (!advance_player_counters)       /* not all players may be affected */
11299       continue;
11300
11301 #if USE_NEW_PLAYER_ANIM
11302     if (move_frames == 0)       /* less than one move per game frame */
11303     {
11304       int stepsize = TILEX / move_delay_value;
11305       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11306       int count = (stored_player[i].is_moving ?
11307                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11308
11309       if (count % delay == 0)
11310         move_frames = 1;
11311     }
11312 #endif
11313
11314     stored_player[i].Frame += move_frames;
11315
11316     if (stored_player[i].MovPos != 0)
11317       stored_player[i].StepFrame += move_frames;
11318
11319     if (stored_player[i].move_delay > 0)
11320       stored_player[i].move_delay--;
11321
11322     /* due to bugs in previous versions, counter must count up, not down */
11323     if (stored_player[i].push_delay != -1)
11324       stored_player[i].push_delay++;
11325
11326     if (stored_player[i].drop_delay > 0)
11327       stored_player[i].drop_delay--;
11328
11329     if (stored_player[i].is_dropping_pressed)
11330       stored_player[i].drop_pressed_delay++;
11331   }
11332 }
11333
11334 void StartGameActions(boolean init_network_game, boolean record_tape,
11335                       long random_seed)
11336 {
11337   unsigned long new_random_seed = InitRND(random_seed);
11338
11339   if (record_tape)
11340     TapeStartRecording(new_random_seed);
11341
11342 #if defined(NETWORK_AVALIABLE)
11343   if (init_network_game)
11344   {
11345     SendToServer_StartPlaying();
11346
11347     return;
11348   }
11349 #endif
11350
11351   InitGame();
11352 }
11353
11354 void GameActions()
11355 {
11356   static unsigned long game_frame_delay = 0;
11357   unsigned long game_frame_delay_value;
11358   byte *recorded_player_action;
11359   byte summarized_player_action = 0;
11360   byte tape_action[MAX_PLAYERS];
11361   int i;
11362
11363   /* detect endless loops, caused by custom element programming */
11364   if (recursion_loop_detected && recursion_loop_depth == 0)
11365   {
11366     char *message = getStringCat3("Internal Error ! Element ",
11367                                   EL_NAME(recursion_loop_element),
11368                                   " caused endless loop ! Quit the game ?");
11369
11370     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11371           EL_NAME(recursion_loop_element));
11372
11373     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11374
11375     recursion_loop_detected = FALSE;    /* if game should be continued */
11376
11377     free(message);
11378
11379     return;
11380   }
11381
11382   if (game.restart_level)
11383     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11384
11385   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11386   {
11387     if (level.native_em_level->lev->home == 0)  /* all players at home */
11388     {
11389       PlayerWins(local_player);
11390
11391       AllPlayersGone = TRUE;
11392
11393       level.native_em_level->lev->home = -1;
11394     }
11395
11396     if (level.native_em_level->ply[0]->alive == 0 &&
11397         level.native_em_level->ply[1]->alive == 0 &&
11398         level.native_em_level->ply[2]->alive == 0 &&
11399         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11400       AllPlayersGone = TRUE;
11401   }
11402
11403   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11404     GameWon();
11405
11406   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11407     TapeStop();
11408
11409   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11410     return;
11411
11412   game_frame_delay_value =
11413     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11414
11415   if (tape.playing && tape.warp_forward && !tape.pausing)
11416     game_frame_delay_value = 0;
11417
11418   /* ---------- main game synchronization point ---------- */
11419
11420   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11421
11422   if (network_playing && !network_player_action_received)
11423   {
11424     /* try to get network player actions in time */
11425
11426 #if defined(NETWORK_AVALIABLE)
11427     /* last chance to get network player actions without main loop delay */
11428     HandleNetworking();
11429 #endif
11430
11431     /* game was quit by network peer */
11432     if (game_status != GAME_MODE_PLAYING)
11433       return;
11434
11435     if (!network_player_action_received)
11436       return;           /* failed to get network player actions in time */
11437
11438     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11439   }
11440
11441   if (tape.pausing)
11442     return;
11443
11444   /* at this point we know that we really continue executing the game */
11445
11446   network_player_action_received = FALSE;
11447
11448   /* when playing tape, read previously recorded player input from tape data */
11449   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11450
11451 #if 1
11452   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11453   if (tape.pausing)
11454     return;
11455 #endif
11456
11457   if (tape.set_centered_player)
11458   {
11459     game.centered_player_nr_next = tape.centered_player_nr_next;
11460     game.set_centered_player = TRUE;
11461   }
11462
11463   for (i = 0; i < MAX_PLAYERS; i++)
11464   {
11465     summarized_player_action |= stored_player[i].action;
11466
11467     if (!network_playing)
11468       stored_player[i].effective_action = stored_player[i].action;
11469   }
11470
11471 #if defined(NETWORK_AVALIABLE)
11472   if (network_playing)
11473     SendToServer_MovePlayer(summarized_player_action);
11474 #endif
11475
11476   if (!options.network && !setup.team_mode)
11477     local_player->effective_action = summarized_player_action;
11478
11479   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11480   {
11481     for (i = 0; i < MAX_PLAYERS; i++)
11482       stored_player[i].effective_action =
11483         (i == game.centered_player_nr ? summarized_player_action : 0);
11484   }
11485
11486   if (recorded_player_action != NULL)
11487     for (i = 0; i < MAX_PLAYERS; i++)
11488       stored_player[i].effective_action = recorded_player_action[i];
11489
11490   for (i = 0; i < MAX_PLAYERS; i++)
11491   {
11492     tape_action[i] = stored_player[i].effective_action;
11493
11494     /* (this can only happen in the R'n'D game engine) */
11495     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11496       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11497   }
11498
11499   /* only record actions from input devices, but not programmed actions */
11500   if (tape.recording)
11501     TapeRecordAction(tape_action);
11502
11503   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11504   {
11505     GameActions_EM_Main();
11506   }
11507   else
11508   {
11509     GameActions_RND();
11510   }
11511 }
11512
11513 void GameActions_EM_Main()
11514 {
11515   byte effective_action[MAX_PLAYERS];
11516   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11517   int i;
11518
11519   for (i = 0; i < MAX_PLAYERS; i++)
11520     effective_action[i] = stored_player[i].effective_action;
11521
11522   GameActions_EM(effective_action, warp_mode);
11523
11524   CheckLevelTime();
11525
11526   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11527 }
11528
11529 void GameActions_RND()
11530 {
11531   int magic_wall_x = 0, magic_wall_y = 0;
11532   int i, x, y, element, graphic;
11533
11534   InitPlayfieldScanModeVars();
11535
11536 #if USE_ONE_MORE_CHANGE_PER_FRAME
11537   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11538   {
11539     SCAN_PLAYFIELD(x, y)
11540     {
11541       ChangeCount[x][y] = 0;
11542       ChangeEvent[x][y] = -1;
11543     }
11544   }
11545 #endif
11546
11547   if (game.set_centered_player)
11548   {
11549     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11550
11551     /* switching to "all players" only possible if all players fit to screen */
11552     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11553     {
11554       game.centered_player_nr_next = game.centered_player_nr;
11555       game.set_centered_player = FALSE;
11556     }
11557
11558     /* do not switch focus to non-existing (or non-active) player */
11559     if (game.centered_player_nr_next >= 0 &&
11560         !stored_player[game.centered_player_nr_next].active)
11561     {
11562       game.centered_player_nr_next = game.centered_player_nr;
11563       game.set_centered_player = FALSE;
11564     }
11565   }
11566
11567   if (game.set_centered_player &&
11568       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11569   {
11570     int sx, sy;
11571
11572     if (game.centered_player_nr_next == -1)
11573     {
11574       setScreenCenteredToAllPlayers(&sx, &sy);
11575     }
11576     else
11577     {
11578       sx = stored_player[game.centered_player_nr_next].jx;
11579       sy = stored_player[game.centered_player_nr_next].jy;
11580     }
11581
11582     game.centered_player_nr = game.centered_player_nr_next;
11583     game.set_centered_player = FALSE;
11584
11585     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11586     DrawGameDoorValues();
11587   }
11588
11589   for (i = 0; i < MAX_PLAYERS; i++)
11590   {
11591     int actual_player_action = stored_player[i].effective_action;
11592
11593 #if 1
11594     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11595        - rnd_equinox_tetrachloride 048
11596        - rnd_equinox_tetrachloride_ii 096
11597        - rnd_emanuel_schmieg 002
11598        - doctor_sloan_ww 001, 020
11599     */
11600     if (stored_player[i].MovPos == 0)
11601       CheckGravityMovement(&stored_player[i]);
11602 #endif
11603
11604     /* overwrite programmed action with tape action */
11605     if (stored_player[i].programmed_action)
11606       actual_player_action = stored_player[i].programmed_action;
11607
11608     PlayerActions(&stored_player[i], actual_player_action);
11609
11610     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11611   }
11612
11613   ScrollScreen(NULL, SCROLL_GO_ON);
11614
11615   /* for backwards compatibility, the following code emulates a fixed bug that
11616      occured when pushing elements (causing elements that just made their last
11617      pushing step to already (if possible) make their first falling step in the
11618      same game frame, which is bad); this code is also needed to use the famous
11619      "spring push bug" which is used in older levels and might be wanted to be
11620      used also in newer levels, but in this case the buggy pushing code is only
11621      affecting the "spring" element and no other elements */
11622
11623   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11624   {
11625     for (i = 0; i < MAX_PLAYERS; i++)
11626     {
11627       struct PlayerInfo *player = &stored_player[i];
11628       int x = player->jx;
11629       int y = player->jy;
11630
11631       if (player->active && player->is_pushing && player->is_moving &&
11632           IS_MOVING(x, y) &&
11633           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11634            Feld[x][y] == EL_SPRING))
11635       {
11636         ContinueMoving(x, y);
11637
11638         /* continue moving after pushing (this is actually a bug) */
11639         if (!IS_MOVING(x, y))
11640           Stop[x][y] = FALSE;
11641       }
11642     }
11643   }
11644
11645 #if 0
11646   debug_print_timestamp(0, "start main loop profiling");
11647 #endif
11648
11649   SCAN_PLAYFIELD(x, y)
11650   {
11651     ChangeCount[x][y] = 0;
11652     ChangeEvent[x][y] = -1;
11653
11654     /* this must be handled before main playfield loop */
11655     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11656     {
11657       MovDelay[x][y]--;
11658       if (MovDelay[x][y] <= 0)
11659         RemoveField(x, y);
11660     }
11661
11662 #if USE_NEW_SNAP_DELAY
11663     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11664     {
11665       MovDelay[x][y]--;
11666       if (MovDelay[x][y] <= 0)
11667       {
11668         RemoveField(x, y);
11669         DrawLevelField(x, y);
11670
11671         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11672       }
11673     }
11674 #endif
11675
11676 #if DEBUG
11677     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11678     {
11679       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11680       printf("GameActions(): This should never happen!\n");
11681
11682       ChangePage[x][y] = -1;
11683     }
11684 #endif
11685
11686     Stop[x][y] = FALSE;
11687     if (WasJustMoving[x][y] > 0)
11688       WasJustMoving[x][y]--;
11689     if (WasJustFalling[x][y] > 0)
11690       WasJustFalling[x][y]--;
11691     if (CheckCollision[x][y] > 0)
11692       CheckCollision[x][y]--;
11693     if (CheckImpact[x][y] > 0)
11694       CheckImpact[x][y]--;
11695
11696     GfxFrame[x][y]++;
11697
11698     /* reset finished pushing action (not done in ContinueMoving() to allow
11699        continuous pushing animation for elements with zero push delay) */
11700     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11701     {
11702       ResetGfxAnimation(x, y);
11703       DrawLevelField(x, y);
11704     }
11705
11706 #if DEBUG
11707     if (IS_BLOCKED(x, y))
11708     {
11709       int oldx, oldy;
11710
11711       Blocked2Moving(x, y, &oldx, &oldy);
11712       if (!IS_MOVING(oldx, oldy))
11713       {
11714         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11715         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11716         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11717         printf("GameActions(): This should never happen!\n");
11718       }
11719     }
11720 #endif
11721   }
11722
11723 #if 0
11724   debug_print_timestamp(0, "- time for pre-main loop:");
11725 #endif
11726
11727 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11728   SCAN_PLAYFIELD(x, y)
11729   {
11730     element = Feld[x][y];
11731     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11732
11733 #if 1
11734     {
11735 #if 1
11736       int element2 = element;
11737       int graphic2 = graphic;
11738 #else
11739       int element2 = Feld[x][y];
11740       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11741 #endif
11742       int last_gfx_frame = GfxFrame[x][y];
11743
11744       if (graphic_info[graphic2].anim_global_sync)
11745         GfxFrame[x][y] = FrameCounter;
11746       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11747         GfxFrame[x][y] = CustomValue[x][y];
11748       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11749         GfxFrame[x][y] = element_info[element2].collect_score;
11750       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11751         GfxFrame[x][y] = ChangeDelay[x][y];
11752
11753       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11754         DrawLevelGraphicAnimation(x, y, graphic2);
11755     }
11756 #else
11757     ResetGfxFrame(x, y, TRUE);
11758 #endif
11759
11760 #if 1
11761     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11762         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11763       ResetRandomAnimationValue(x, y);
11764 #endif
11765
11766 #if 1
11767     SetRandomAnimationValue(x, y);
11768 #endif
11769
11770 #if 1
11771     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11772 #endif
11773   }
11774 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11775
11776 #if 0
11777   debug_print_timestamp(0, "- time for TEST loop:     -->");
11778 #endif
11779
11780   SCAN_PLAYFIELD(x, y)
11781   {
11782     element = Feld[x][y];
11783     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11784
11785     ResetGfxFrame(x, y, TRUE);
11786
11787     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11788         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11789       ResetRandomAnimationValue(x, y);
11790
11791     SetRandomAnimationValue(x, y);
11792
11793     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11794
11795     if (IS_INACTIVE(element))
11796     {
11797       if (IS_ANIMATED(graphic))
11798         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11799
11800       continue;
11801     }
11802
11803     /* this may take place after moving, so 'element' may have changed */
11804     if (IS_CHANGING(x, y) &&
11805         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11806     {
11807       int page = element_info[element].event_page_nr[CE_DELAY];
11808
11809 #if 1
11810       HandleElementChange(x, y, page);
11811 #else
11812       if (CAN_CHANGE(element))
11813         HandleElementChange(x, y, page);
11814
11815       if (HAS_ACTION(element))
11816         ExecuteCustomElementAction(x, y, element, page);
11817 #endif
11818
11819       element = Feld[x][y];
11820       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821     }
11822
11823 #if 0   // ---------------------------------------------------------------------
11824
11825     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11826     {
11827       StartMoving(x, y);
11828
11829       element = Feld[x][y];
11830       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11831
11832       if (IS_ANIMATED(graphic) &&
11833           !IS_MOVING(x, y) &&
11834           !Stop[x][y])
11835         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11836
11837       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11838         DrawTwinkleOnField(x, y);
11839     }
11840     else if (IS_MOVING(x, y))
11841       ContinueMoving(x, y);
11842     else
11843     {
11844       switch (element)
11845       {
11846         case EL_ACID:
11847         case EL_EXIT_OPEN:
11848         case EL_EM_EXIT_OPEN:
11849         case EL_SP_EXIT_OPEN:
11850         case EL_STEEL_EXIT_OPEN:
11851         case EL_EM_STEEL_EXIT_OPEN:
11852         case EL_SP_TERMINAL:
11853         case EL_SP_TERMINAL_ACTIVE:
11854         case EL_EXTRA_TIME:
11855         case EL_SHIELD_NORMAL:
11856         case EL_SHIELD_DEADLY:
11857           if (IS_ANIMATED(graphic))
11858             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11859           break;
11860
11861         case EL_DYNAMITE_ACTIVE:
11862         case EL_EM_DYNAMITE_ACTIVE:
11863         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11864         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11865         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11866         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11867         case EL_SP_DISK_RED_ACTIVE:
11868           CheckDynamite(x, y);
11869           break;
11870
11871         case EL_AMOEBA_GROWING:
11872           AmoebeWaechst(x, y);
11873           break;
11874
11875         case EL_AMOEBA_SHRINKING:
11876           AmoebaDisappearing(x, y);
11877           break;
11878
11879 #if !USE_NEW_AMOEBA_CODE
11880         case EL_AMOEBA_WET:
11881         case EL_AMOEBA_DRY:
11882         case EL_AMOEBA_FULL:
11883         case EL_BD_AMOEBA:
11884         case EL_EMC_DRIPPER:
11885           AmoebeAbleger(x, y);
11886           break;
11887 #endif
11888
11889         case EL_GAME_OF_LIFE:
11890         case EL_BIOMAZE:
11891           Life(x, y);
11892           break;
11893
11894         case EL_EXIT_CLOSED:
11895           CheckExit(x, y);
11896           break;
11897
11898         case EL_EM_EXIT_CLOSED:
11899           CheckExitEM(x, y);
11900           break;
11901
11902         case EL_STEEL_EXIT_CLOSED:
11903           CheckExitSteel(x, y);
11904           break;
11905
11906         case EL_EM_STEEL_EXIT_CLOSED:
11907           CheckExitSteelEM(x, y);
11908           break;
11909
11910         case EL_SP_EXIT_CLOSED:
11911           CheckExitSP(x, y);
11912           break;
11913
11914         case EL_EXPANDABLE_WALL_GROWING:
11915         case EL_EXPANDABLE_STEELWALL_GROWING:
11916           MauerWaechst(x, y);
11917           break;
11918
11919         case EL_EXPANDABLE_WALL:
11920         case EL_EXPANDABLE_WALL_HORIZONTAL:
11921         case EL_EXPANDABLE_WALL_VERTICAL:
11922         case EL_EXPANDABLE_WALL_ANY:
11923         case EL_BD_EXPANDABLE_WALL:
11924           MauerAbleger(x, y);
11925           break;
11926
11927         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11928         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11929         case EL_EXPANDABLE_STEELWALL_ANY:
11930           MauerAblegerStahl(x, y);
11931           break;
11932
11933         case EL_FLAMES:
11934           CheckForDragon(x, y);
11935           break;
11936
11937         case EL_EXPLOSION:
11938           break;
11939
11940         case EL_ELEMENT_SNAPPING:
11941         case EL_DIAGONAL_SHRINKING:
11942         case EL_DIAGONAL_GROWING:
11943         {
11944           graphic =
11945             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11946
11947           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11948           break;
11949         }
11950
11951         default:
11952           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11953             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11954           break;
11955       }
11956     }
11957
11958 #else   // ---------------------------------------------------------------------
11959
11960     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11961     {
11962       StartMoving(x, y);
11963
11964       element = Feld[x][y];
11965       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11966
11967       if (IS_ANIMATED(graphic) &&
11968           !IS_MOVING(x, y) &&
11969           !Stop[x][y])
11970         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11971
11972       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11973         DrawTwinkleOnField(x, y);
11974     }
11975     else if ((element == EL_ACID ||
11976               element == EL_EXIT_OPEN ||
11977               element == EL_EM_EXIT_OPEN ||
11978               element == EL_SP_EXIT_OPEN ||
11979               element == EL_STEEL_EXIT_OPEN ||
11980               element == EL_EM_STEEL_EXIT_OPEN ||
11981               element == EL_SP_TERMINAL ||
11982               element == EL_SP_TERMINAL_ACTIVE ||
11983               element == EL_EXTRA_TIME ||
11984               element == EL_SHIELD_NORMAL ||
11985               element == EL_SHIELD_DEADLY) &&
11986              IS_ANIMATED(graphic))
11987       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11988     else if (IS_MOVING(x, y))
11989       ContinueMoving(x, y);
11990     else if (IS_ACTIVE_BOMB(element))
11991       CheckDynamite(x, y);
11992     else if (element == EL_AMOEBA_GROWING)
11993       AmoebeWaechst(x, y);
11994     else if (element == EL_AMOEBA_SHRINKING)
11995       AmoebaDisappearing(x, y);
11996
11997 #if !USE_NEW_AMOEBA_CODE
11998     else if (IS_AMOEBALIVE(element))
11999       AmoebeAbleger(x, y);
12000 #endif
12001
12002     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12003       Life(x, y);
12004     else if (element == EL_EXIT_CLOSED)
12005       CheckExit(x, y);
12006     else if (element == EL_EM_EXIT_CLOSED)
12007       CheckExitEM(x, y);
12008     else if (element == EL_STEEL_EXIT_CLOSED)
12009       CheckExitSteel(x, y);
12010     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12011       CheckExitSteelEM(x, y);
12012     else if (element == EL_SP_EXIT_CLOSED)
12013       CheckExitSP(x, y);
12014     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12015              element == EL_EXPANDABLE_STEELWALL_GROWING)
12016       MauerWaechst(x, y);
12017     else if (element == EL_EXPANDABLE_WALL ||
12018              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12019              element == EL_EXPANDABLE_WALL_VERTICAL ||
12020              element == EL_EXPANDABLE_WALL_ANY ||
12021              element == EL_BD_EXPANDABLE_WALL)
12022       MauerAbleger(x, y);
12023     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12024              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12025              element == EL_EXPANDABLE_STEELWALL_ANY)
12026       MauerAblegerStahl(x, y);
12027     else if (element == EL_FLAMES)
12028       CheckForDragon(x, y);
12029     else if (element == EL_EXPLOSION)
12030       ; /* drawing of correct explosion animation is handled separately */
12031     else if (element == EL_ELEMENT_SNAPPING ||
12032              element == EL_DIAGONAL_SHRINKING ||
12033              element == EL_DIAGONAL_GROWING)
12034     {
12035       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12036
12037       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12038     }
12039     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12040       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12041
12042 #endif  // ---------------------------------------------------------------------
12043
12044     if (IS_BELT_ACTIVE(element))
12045       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12046
12047     if (game.magic_wall_active)
12048     {
12049       int jx = local_player->jx, jy = local_player->jy;
12050
12051       /* play the element sound at the position nearest to the player */
12052       if ((element == EL_MAGIC_WALL_FULL ||
12053            element == EL_MAGIC_WALL_ACTIVE ||
12054            element == EL_MAGIC_WALL_EMPTYING ||
12055            element == EL_BD_MAGIC_WALL_FULL ||
12056            element == EL_BD_MAGIC_WALL_ACTIVE ||
12057            element == EL_BD_MAGIC_WALL_EMPTYING ||
12058            element == EL_DC_MAGIC_WALL_FULL ||
12059            element == EL_DC_MAGIC_WALL_ACTIVE ||
12060            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12061           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12062       {
12063         magic_wall_x = x;
12064         magic_wall_y = y;
12065       }
12066     }
12067   }
12068
12069 #if 0
12070   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12071 #endif
12072
12073 #if USE_NEW_AMOEBA_CODE
12074   /* new experimental amoeba growth stuff */
12075   if (!(FrameCounter % 8))
12076   {
12077     static unsigned long random = 1684108901;
12078
12079     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12080     {
12081       x = RND(lev_fieldx);
12082       y = RND(lev_fieldy);
12083       element = Feld[x][y];
12084
12085       if (!IS_PLAYER(x,y) &&
12086           (element == EL_EMPTY ||
12087            CAN_GROW_INTO(element) ||
12088            element == EL_QUICKSAND_EMPTY ||
12089            element == EL_QUICKSAND_FAST_EMPTY ||
12090            element == EL_ACID_SPLASH_LEFT ||
12091            element == EL_ACID_SPLASH_RIGHT))
12092       {
12093         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12094             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12095             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12096             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12097           Feld[x][y] = EL_AMOEBA_DROP;
12098       }
12099
12100       random = random * 129 + 1;
12101     }
12102   }
12103 #endif
12104
12105 #if 0
12106   if (game.explosions_delayed)
12107 #endif
12108   {
12109     game.explosions_delayed = FALSE;
12110
12111     SCAN_PLAYFIELD(x, y)
12112     {
12113       element = Feld[x][y];
12114
12115       if (ExplodeField[x][y])
12116         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12117       else if (element == EL_EXPLOSION)
12118         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12119
12120       ExplodeField[x][y] = EX_TYPE_NONE;
12121     }
12122
12123     game.explosions_delayed = TRUE;
12124   }
12125
12126   if (game.magic_wall_active)
12127   {
12128     if (!(game.magic_wall_time_left % 4))
12129     {
12130       int element = Feld[magic_wall_x][magic_wall_y];
12131
12132       if (element == EL_BD_MAGIC_WALL_FULL ||
12133           element == EL_BD_MAGIC_WALL_ACTIVE ||
12134           element == EL_BD_MAGIC_WALL_EMPTYING)
12135         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12136       else if (element == EL_DC_MAGIC_WALL_FULL ||
12137                element == EL_DC_MAGIC_WALL_ACTIVE ||
12138                element == EL_DC_MAGIC_WALL_EMPTYING)
12139         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12140       else
12141         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12142     }
12143
12144     if (game.magic_wall_time_left > 0)
12145     {
12146       game.magic_wall_time_left--;
12147
12148       if (!game.magic_wall_time_left)
12149       {
12150         SCAN_PLAYFIELD(x, y)
12151         {
12152           element = Feld[x][y];
12153
12154           if (element == EL_MAGIC_WALL_ACTIVE ||
12155               element == EL_MAGIC_WALL_FULL)
12156           {
12157             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12158             DrawLevelField(x, y);
12159           }
12160           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12161                    element == EL_BD_MAGIC_WALL_FULL)
12162           {
12163             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12164             DrawLevelField(x, y);
12165           }
12166           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12167                    element == EL_DC_MAGIC_WALL_FULL)
12168           {
12169             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12170             DrawLevelField(x, y);
12171           }
12172         }
12173
12174         game.magic_wall_active = FALSE;
12175       }
12176     }
12177   }
12178
12179   if (game.light_time_left > 0)
12180   {
12181     game.light_time_left--;
12182
12183     if (game.light_time_left == 0)
12184       RedrawAllLightSwitchesAndInvisibleElements();
12185   }
12186
12187   if (game.timegate_time_left > 0)
12188   {
12189     game.timegate_time_left--;
12190
12191     if (game.timegate_time_left == 0)
12192       CloseAllOpenTimegates();
12193   }
12194
12195   if (game.lenses_time_left > 0)
12196   {
12197     game.lenses_time_left--;
12198
12199     if (game.lenses_time_left == 0)
12200       RedrawAllInvisibleElementsForLenses();
12201   }
12202
12203   if (game.magnify_time_left > 0)
12204   {
12205     game.magnify_time_left--;
12206
12207     if (game.magnify_time_left == 0)
12208       RedrawAllInvisibleElementsForMagnifier();
12209   }
12210
12211   for (i = 0; i < MAX_PLAYERS; i++)
12212   {
12213     struct PlayerInfo *player = &stored_player[i];
12214
12215     if (SHIELD_ON(player))
12216     {
12217       if (player->shield_deadly_time_left)
12218         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12219       else if (player->shield_normal_time_left)
12220         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12221     }
12222   }
12223
12224   CheckLevelTime();
12225
12226   DrawAllPlayers();
12227   PlayAllPlayersSound();
12228
12229   if (options.debug)                    /* calculate frames per second */
12230   {
12231     static unsigned long fps_counter = 0;
12232     static int fps_frames = 0;
12233     unsigned long fps_delay_ms = Counter() - fps_counter;
12234
12235     fps_frames++;
12236
12237     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12238     {
12239       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12240
12241       fps_frames = 0;
12242       fps_counter = Counter();
12243     }
12244
12245     redraw_mask |= REDRAW_FPS;
12246   }
12247
12248   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12249
12250   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12251   {
12252     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12253
12254     local_player->show_envelope = 0;
12255   }
12256
12257 #if 0
12258   debug_print_timestamp(0, "stop main loop profiling ");
12259   printf("----------------------------------------------------------\n");
12260 #endif
12261
12262   /* use random number generator in every frame to make it less predictable */
12263   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12264     RND(1);
12265 }
12266
12267 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12268 {
12269   int min_x = x, min_y = y, max_x = x, max_y = y;
12270   int i;
12271
12272   for (i = 0; i < MAX_PLAYERS; i++)
12273   {
12274     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12275
12276     if (!stored_player[i].active || &stored_player[i] == player)
12277       continue;
12278
12279     min_x = MIN(min_x, jx);
12280     min_y = MIN(min_y, jy);
12281     max_x = MAX(max_x, jx);
12282     max_y = MAX(max_y, jy);
12283   }
12284
12285   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12286 }
12287
12288 static boolean AllPlayersInVisibleScreen()
12289 {
12290   int i;
12291
12292   for (i = 0; i < MAX_PLAYERS; i++)
12293   {
12294     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12295
12296     if (!stored_player[i].active)
12297       continue;
12298
12299     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12300       return FALSE;
12301   }
12302
12303   return TRUE;
12304 }
12305
12306 void ScrollLevel(int dx, int dy)
12307 {
12308 #if 1
12309   static Bitmap *bitmap_db_field2 = NULL;
12310   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12311   int x, y;
12312 #else
12313   int i, x, y;
12314 #endif
12315
12316 #if 0
12317   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12318   /* only horizontal XOR vertical scroll direction allowed */
12319   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12320     return;
12321 #endif
12322
12323 #if 1
12324   if (bitmap_db_field2 == NULL)
12325     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12326
12327   /* needed when blitting directly to same bitmap -- should not be needed with
12328      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12329   BlitBitmap(drawto_field, bitmap_db_field2,
12330              FX + TILEX * (dx == -1) - softscroll_offset,
12331              FY + TILEY * (dy == -1) - softscroll_offset,
12332              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12333              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12334              FX + TILEX * (dx == 1) - softscroll_offset,
12335              FY + TILEY * (dy == 1) - softscroll_offset);
12336   BlitBitmap(bitmap_db_field2, drawto_field,
12337              FX + TILEX * (dx == 1) - softscroll_offset,
12338              FY + TILEY * (dy == 1) - softscroll_offset,
12339              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12340              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12341              FX + TILEX * (dx == 1) - softscroll_offset,
12342              FY + TILEY * (dy == 1) - softscroll_offset);
12343
12344 #else
12345
12346 #if 1
12347   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12348   int xsize = (BX2 - BX1 + 1);
12349   int ysize = (BY2 - BY1 + 1);
12350   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12351   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12352   int step  = (start < end ? +1 : -1);
12353
12354   for (i = start; i != end; i += step)
12355   {
12356     BlitBitmap(drawto_field, drawto_field,
12357                FX + TILEX * (dx != 0 ? i + step : 0),
12358                FY + TILEY * (dy != 0 ? i + step : 0),
12359                TILEX * (dx != 0 ? 1 : xsize),
12360                TILEY * (dy != 0 ? 1 : ysize),
12361                FX + TILEX * (dx != 0 ? i : 0),
12362                FY + TILEY * (dy != 0 ? i : 0));
12363   }
12364
12365 #else
12366
12367   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12368
12369   BlitBitmap(drawto_field, drawto_field,
12370              FX + TILEX * (dx == -1) - softscroll_offset,
12371              FY + TILEY * (dy == -1) - softscroll_offset,
12372              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12373              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12374              FX + TILEX * (dx == 1) - softscroll_offset,
12375              FY + TILEY * (dy == 1) - softscroll_offset);
12376 #endif
12377 #endif
12378
12379   if (dx != 0)
12380   {
12381     x = (dx == 1 ? BX1 : BX2);
12382     for (y = BY1; y <= BY2; y++)
12383       DrawScreenField(x, y);
12384   }
12385
12386   if (dy != 0)
12387   {
12388     y = (dy == 1 ? BY1 : BY2);
12389     for (x = BX1; x <= BX2; x++)
12390       DrawScreenField(x, y);
12391   }
12392
12393   redraw_mask |= REDRAW_FIELD;
12394 }
12395
12396 static boolean canFallDown(struct PlayerInfo *player)
12397 {
12398   int jx = player->jx, jy = player->jy;
12399
12400   return (IN_LEV_FIELD(jx, jy + 1) &&
12401           (IS_FREE(jx, jy + 1) ||
12402            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12403           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12404           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12405 }
12406
12407 static boolean canPassField(int x, int y, int move_dir)
12408 {
12409   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12410   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12411   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12412   int nextx = x + dx;
12413   int nexty = y + dy;
12414   int element = Feld[x][y];
12415
12416   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12417           !CAN_MOVE(element) &&
12418           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12419           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12420           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12421 }
12422
12423 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12424 {
12425   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12426   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12427   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12428   int newx = x + dx;
12429   int newy = y + dy;
12430
12431   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12432           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12433           (IS_DIGGABLE(Feld[newx][newy]) ||
12434            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12435            canPassField(newx, newy, move_dir)));
12436 }
12437
12438 static void CheckGravityMovement(struct PlayerInfo *player)
12439 {
12440 #if USE_PLAYER_GRAVITY
12441   if (player->gravity && !player->programmed_action)
12442 #else
12443   if (game.gravity && !player->programmed_action)
12444 #endif
12445   {
12446     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12447     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12448     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12449     int jx = player->jx, jy = player->jy;
12450     boolean player_is_moving_to_valid_field =
12451       (!player_is_snapping &&
12452        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12453         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12454     boolean player_can_fall_down = canFallDown(player);
12455
12456     if (player_can_fall_down &&
12457         !player_is_moving_to_valid_field)
12458       player->programmed_action = MV_DOWN;
12459   }
12460 }
12461
12462 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12463 {
12464   return CheckGravityMovement(player);
12465
12466 #if USE_PLAYER_GRAVITY
12467   if (player->gravity && !player->programmed_action)
12468 #else
12469   if (game.gravity && !player->programmed_action)
12470 #endif
12471   {
12472     int jx = player->jx, jy = player->jy;
12473     boolean field_under_player_is_free =
12474       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12475     boolean player_is_standing_on_valid_field =
12476       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12477        (IS_WALKABLE(Feld[jx][jy]) &&
12478         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12479
12480     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12481       player->programmed_action = MV_DOWN;
12482   }
12483 }
12484
12485 /*
12486   MovePlayerOneStep()
12487   -----------------------------------------------------------------------------
12488   dx, dy:               direction (non-diagonal) to try to move the player to
12489   real_dx, real_dy:     direction as read from input device (can be diagonal)
12490 */
12491
12492 boolean MovePlayerOneStep(struct PlayerInfo *player,
12493                           int dx, int dy, int real_dx, int real_dy)
12494 {
12495   int jx = player->jx, jy = player->jy;
12496   int new_jx = jx + dx, new_jy = jy + dy;
12497 #if !USE_FIXED_DONT_RUN_INTO
12498   int element;
12499 #endif
12500   int can_move;
12501   boolean player_can_move = !player->cannot_move;
12502
12503   if (!player->active || (!dx && !dy))
12504     return MP_NO_ACTION;
12505
12506   player->MovDir = (dx < 0 ? MV_LEFT :
12507                     dx > 0 ? MV_RIGHT :
12508                     dy < 0 ? MV_UP :
12509                     dy > 0 ? MV_DOWN :  MV_NONE);
12510
12511   if (!IN_LEV_FIELD(new_jx, new_jy))
12512     return MP_NO_ACTION;
12513
12514   if (!player_can_move)
12515   {
12516     if (player->MovPos == 0)
12517     {
12518       player->is_moving = FALSE;
12519       player->is_digging = FALSE;
12520       player->is_collecting = FALSE;
12521       player->is_snapping = FALSE;
12522       player->is_pushing = FALSE;
12523     }
12524   }
12525
12526 #if 1
12527   if (!options.network && game.centered_player_nr == -1 &&
12528       !AllPlayersInSight(player, new_jx, new_jy))
12529     return MP_NO_ACTION;
12530 #else
12531   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12532     return MP_NO_ACTION;
12533 #endif
12534
12535 #if !USE_FIXED_DONT_RUN_INTO
12536   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12537
12538   /* (moved to DigField()) */
12539   if (player_can_move && DONT_RUN_INTO(element))
12540   {
12541     if (element == EL_ACID && dx == 0 && dy == 1)
12542     {
12543       SplashAcid(new_jx, new_jy);
12544       Feld[jx][jy] = EL_PLAYER_1;
12545       InitMovingField(jx, jy, MV_DOWN);
12546       Store[jx][jy] = EL_ACID;
12547       ContinueMoving(jx, jy);
12548       BuryPlayer(player);
12549     }
12550     else
12551       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12552
12553     return MP_MOVING;
12554   }
12555 #endif
12556
12557   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12558   if (can_move != MP_MOVING)
12559     return can_move;
12560
12561   /* check if DigField() has caused relocation of the player */
12562   if (player->jx != jx || player->jy != jy)
12563     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12564
12565   StorePlayer[jx][jy] = 0;
12566   player->last_jx = jx;
12567   player->last_jy = jy;
12568   player->jx = new_jx;
12569   player->jy = new_jy;
12570   StorePlayer[new_jx][new_jy] = player->element_nr;
12571
12572   if (player->move_delay_value_next != -1)
12573   {
12574     player->move_delay_value = player->move_delay_value_next;
12575     player->move_delay_value_next = -1;
12576   }
12577
12578   player->MovPos =
12579     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12580
12581   player->step_counter++;
12582
12583   PlayerVisit[jx][jy] = FrameCounter;
12584
12585 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12586   player->is_moving = TRUE;
12587 #endif
12588
12589 #if 1
12590   /* should better be called in MovePlayer(), but this breaks some tapes */
12591   ScrollPlayer(player, SCROLL_INIT);
12592 #endif
12593
12594   return MP_MOVING;
12595 }
12596
12597 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12598 {
12599   int jx = player->jx, jy = player->jy;
12600   int old_jx = jx, old_jy = jy;
12601   int moved = MP_NO_ACTION;
12602
12603   if (!player->active)
12604     return FALSE;
12605
12606   if (!dx && !dy)
12607   {
12608     if (player->MovPos == 0)
12609     {
12610       player->is_moving = FALSE;
12611       player->is_digging = FALSE;
12612       player->is_collecting = FALSE;
12613       player->is_snapping = FALSE;
12614       player->is_pushing = FALSE;
12615     }
12616
12617     return FALSE;
12618   }
12619
12620   if (player->move_delay > 0)
12621     return FALSE;
12622
12623   player->move_delay = -1;              /* set to "uninitialized" value */
12624
12625   /* store if player is automatically moved to next field */
12626   player->is_auto_moving = (player->programmed_action != MV_NONE);
12627
12628   /* remove the last programmed player action */
12629   player->programmed_action = 0;
12630
12631   if (player->MovPos)
12632   {
12633     /* should only happen if pre-1.2 tape recordings are played */
12634     /* this is only for backward compatibility */
12635
12636     int original_move_delay_value = player->move_delay_value;
12637
12638 #if DEBUG
12639     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12640            tape.counter);
12641 #endif
12642
12643     /* scroll remaining steps with finest movement resolution */
12644     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12645
12646     while (player->MovPos)
12647     {
12648       ScrollPlayer(player, SCROLL_GO_ON);
12649       ScrollScreen(NULL, SCROLL_GO_ON);
12650
12651       AdvanceFrameAndPlayerCounters(player->index_nr);
12652
12653       DrawAllPlayers();
12654       BackToFront();
12655     }
12656
12657     player->move_delay_value = original_move_delay_value;
12658   }
12659
12660   player->is_active = FALSE;
12661
12662   if (player->last_move_dir & MV_HORIZONTAL)
12663   {
12664     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12665       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12666   }
12667   else
12668   {
12669     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12670       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12671   }
12672
12673 #if USE_FIXED_BORDER_RUNNING_GFX
12674   if (!moved && !player->is_active)
12675   {
12676     player->is_moving = FALSE;
12677     player->is_digging = FALSE;
12678     player->is_collecting = FALSE;
12679     player->is_snapping = FALSE;
12680     player->is_pushing = FALSE;
12681   }
12682 #endif
12683
12684   jx = player->jx;
12685   jy = player->jy;
12686
12687 #if 1
12688   if (moved & MP_MOVING && !ScreenMovPos &&
12689       (player->index_nr == game.centered_player_nr ||
12690        game.centered_player_nr == -1))
12691 #else
12692   if (moved & MP_MOVING && !ScreenMovPos &&
12693       (player == local_player || !options.network))
12694 #endif
12695   {
12696     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12697     int offset = game.scroll_delay_value;
12698
12699     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12700     {
12701       /* actual player has left the screen -- scroll in that direction */
12702       if (jx != old_jx)         /* player has moved horizontally */
12703         scroll_x += (jx - old_jx);
12704       else                      /* player has moved vertically */
12705         scroll_y += (jy - old_jy);
12706     }
12707     else
12708     {
12709       if (jx != old_jx)         /* player has moved horizontally */
12710       {
12711         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12712             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12713           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12714
12715         /* don't scroll over playfield boundaries */
12716         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12717           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12718
12719         /* don't scroll more than one field at a time */
12720         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12721
12722         /* don't scroll against the player's moving direction */
12723         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12724             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12725           scroll_x = old_scroll_x;
12726       }
12727       else                      /* player has moved vertically */
12728       {
12729         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12730             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12731           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12732
12733         /* don't scroll over playfield boundaries */
12734         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12735           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12736
12737         /* don't scroll more than one field at a time */
12738         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12739
12740         /* don't scroll against the player's moving direction */
12741         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12742             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12743           scroll_y = old_scroll_y;
12744       }
12745     }
12746
12747     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12748     {
12749 #if 1
12750       if (!options.network && game.centered_player_nr == -1 &&
12751           !AllPlayersInVisibleScreen())
12752       {
12753         scroll_x = old_scroll_x;
12754         scroll_y = old_scroll_y;
12755       }
12756       else
12757 #else
12758       if (!options.network && !AllPlayersInVisibleScreen())
12759       {
12760         scroll_x = old_scroll_x;
12761         scroll_y = old_scroll_y;
12762       }
12763       else
12764 #endif
12765       {
12766         ScrollScreen(player, SCROLL_INIT);
12767         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12768       }
12769     }
12770   }
12771
12772   player->StepFrame = 0;
12773
12774   if (moved & MP_MOVING)
12775   {
12776     if (old_jx != jx && old_jy == jy)
12777       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12778     else if (old_jx == jx && old_jy != jy)
12779       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12780
12781     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12782
12783     player->last_move_dir = player->MovDir;
12784     player->is_moving = TRUE;
12785     player->is_snapping = FALSE;
12786     player->is_switching = FALSE;
12787     player->is_dropping = FALSE;
12788     player->is_dropping_pressed = FALSE;
12789     player->drop_pressed_delay = 0;
12790
12791 #if 0
12792     /* should better be called here than above, but this breaks some tapes */
12793     ScrollPlayer(player, SCROLL_INIT);
12794 #endif
12795   }
12796   else
12797   {
12798     CheckGravityMovementWhenNotMoving(player);
12799
12800     player->is_moving = FALSE;
12801
12802     /* at this point, the player is allowed to move, but cannot move right now
12803        (e.g. because of something blocking the way) -- ensure that the player
12804        is also allowed to move in the next frame (in old versions before 3.1.1,
12805        the player was forced to wait again for eight frames before next try) */
12806
12807     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12808       player->move_delay = 0;   /* allow direct movement in the next frame */
12809   }
12810
12811   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12812     player->move_delay = player->move_delay_value;
12813
12814   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12815   {
12816     TestIfPlayerTouchesBadThing(jx, jy);
12817     TestIfPlayerTouchesCustomElement(jx, jy);
12818   }
12819
12820   if (!player->active)
12821     RemovePlayer(player);
12822
12823   return moved;
12824 }
12825
12826 void ScrollPlayer(struct PlayerInfo *player, int mode)
12827 {
12828   int jx = player->jx, jy = player->jy;
12829   int last_jx = player->last_jx, last_jy = player->last_jy;
12830   int move_stepsize = TILEX / player->move_delay_value;
12831
12832 #if USE_NEW_PLAYER_SPEED
12833   if (!player->active)
12834     return;
12835
12836   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12837     return;
12838 #else
12839   if (!player->active || player->MovPos == 0)
12840     return;
12841 #endif
12842
12843   if (mode == SCROLL_INIT)
12844   {
12845     player->actual_frame_counter = FrameCounter;
12846     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12847
12848     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12849         Feld[last_jx][last_jy] == EL_EMPTY)
12850     {
12851       int last_field_block_delay = 0;   /* start with no blocking at all */
12852       int block_delay_adjustment = player->block_delay_adjustment;
12853
12854       /* if player blocks last field, add delay for exactly one move */
12855       if (player->block_last_field)
12856       {
12857         last_field_block_delay += player->move_delay_value;
12858
12859         /* when blocking enabled, prevent moving up despite gravity */
12860 #if USE_PLAYER_GRAVITY
12861         if (player->gravity && player->MovDir == MV_UP)
12862           block_delay_adjustment = -1;
12863 #else
12864         if (game.gravity && player->MovDir == MV_UP)
12865           block_delay_adjustment = -1;
12866 #endif
12867       }
12868
12869       /* add block delay adjustment (also possible when not blocking) */
12870       last_field_block_delay += block_delay_adjustment;
12871
12872       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12873       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12874     }
12875
12876 #if USE_NEW_PLAYER_SPEED
12877     if (player->MovPos != 0)    /* player has not yet reached destination */
12878       return;
12879 #else
12880     return;
12881 #endif
12882   }
12883   else if (!FrameReached(&player->actual_frame_counter, 1))
12884     return;
12885
12886 #if USE_NEW_PLAYER_SPEED
12887   if (player->MovPos != 0)
12888   {
12889     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12890     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12891
12892     /* before DrawPlayer() to draw correct player graphic for this case */
12893     if (player->MovPos == 0)
12894       CheckGravityMovement(player);
12895   }
12896 #else
12897   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12898   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12899
12900   /* before DrawPlayer() to draw correct player graphic for this case */
12901   if (player->MovPos == 0)
12902     CheckGravityMovement(player);
12903 #endif
12904
12905   if (player->MovPos == 0)      /* player reached destination field */
12906   {
12907     if (player->move_delay_reset_counter > 0)
12908     {
12909       player->move_delay_reset_counter--;
12910
12911       if (player->move_delay_reset_counter == 0)
12912       {
12913         /* continue with normal speed after quickly moving through gate */
12914         HALVE_PLAYER_SPEED(player);
12915
12916         /* be able to make the next move without delay */
12917         player->move_delay = 0;
12918       }
12919     }
12920
12921     player->last_jx = jx;
12922     player->last_jy = jy;
12923
12924     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12925         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12926         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12927         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12928         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12929         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12930     {
12931       DrawPlayer(player);       /* needed here only to cleanup last field */
12932       RemovePlayer(player);
12933
12934       if (local_player->friends_still_needed == 0 ||
12935           IS_SP_ELEMENT(Feld[jx][jy]))
12936         PlayerWins(player);
12937     }
12938
12939     /* this breaks one level: "machine", level 000 */
12940     {
12941       int move_direction = player->MovDir;
12942       int enter_side = MV_DIR_OPPOSITE(move_direction);
12943       int leave_side = move_direction;
12944       int old_jx = last_jx;
12945       int old_jy = last_jy;
12946       int old_element = Feld[old_jx][old_jy];
12947       int new_element = Feld[jx][jy];
12948
12949       if (IS_CUSTOM_ELEMENT(old_element))
12950         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12951                                    CE_LEFT_BY_PLAYER,
12952                                    player->index_bit, leave_side);
12953
12954       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12955                                           CE_PLAYER_LEAVES_X,
12956                                           player->index_bit, leave_side);
12957
12958       if (IS_CUSTOM_ELEMENT(new_element))
12959         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12960                                    player->index_bit, enter_side);
12961
12962       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12963                                           CE_PLAYER_ENTERS_X,
12964                                           player->index_bit, enter_side);
12965
12966       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12967                                         CE_MOVE_OF_X, move_direction);
12968     }
12969
12970     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12971     {
12972       TestIfPlayerTouchesBadThing(jx, jy);
12973       TestIfPlayerTouchesCustomElement(jx, jy);
12974
12975       /* needed because pushed element has not yet reached its destination,
12976          so it would trigger a change event at its previous field location */
12977       if (!player->is_pushing)
12978         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12979
12980       if (!player->active)
12981         RemovePlayer(player);
12982     }
12983
12984     if (!local_player->LevelSolved && level.use_step_counter)
12985     {
12986       int i;
12987
12988       TimePlayed++;
12989
12990       if (TimeLeft > 0)
12991       {
12992         TimeLeft--;
12993
12994         if (TimeLeft <= 10 && setup.time_limit)
12995           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12996
12997 #if 1
12998         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12999
13000         DisplayGameControlValues();
13001 #else
13002         DrawGameValue_Time(TimeLeft);
13003 #endif
13004
13005         if (!TimeLeft && setup.time_limit)
13006           for (i = 0; i < MAX_PLAYERS; i++)
13007             KillPlayer(&stored_player[i]);
13008       }
13009 #if 1
13010       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13011       {
13012         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13013
13014         DisplayGameControlValues();
13015       }
13016 #else
13017       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13018         DrawGameValue_Time(TimePlayed);
13019 #endif
13020     }
13021
13022     if (tape.single_step && tape.recording && !tape.pausing &&
13023         !player->programmed_action)
13024       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13025   }
13026 }
13027
13028 void ScrollScreen(struct PlayerInfo *player, int mode)
13029 {
13030   static unsigned long screen_frame_counter = 0;
13031
13032   if (mode == SCROLL_INIT)
13033   {
13034     /* set scrolling step size according to actual player's moving speed */
13035     ScrollStepSize = TILEX / player->move_delay_value;
13036
13037     screen_frame_counter = FrameCounter;
13038     ScreenMovDir = player->MovDir;
13039     ScreenMovPos = player->MovPos;
13040     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13041     return;
13042   }
13043   else if (!FrameReached(&screen_frame_counter, 1))
13044     return;
13045
13046   if (ScreenMovPos)
13047   {
13048     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13049     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13050     redraw_mask |= REDRAW_FIELD;
13051   }
13052   else
13053     ScreenMovDir = MV_NONE;
13054 }
13055
13056 void TestIfPlayerTouchesCustomElement(int x, int y)
13057 {
13058   static int xy[4][2] =
13059   {
13060     { 0, -1 },
13061     { -1, 0 },
13062     { +1, 0 },
13063     { 0, +1 }
13064   };
13065   static int trigger_sides[4][2] =
13066   {
13067     /* center side       border side */
13068     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13069     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13070     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13071     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13072   };
13073   static int touch_dir[4] =
13074   {
13075     MV_LEFT | MV_RIGHT,
13076     MV_UP   | MV_DOWN,
13077     MV_UP   | MV_DOWN,
13078     MV_LEFT | MV_RIGHT
13079   };
13080   int center_element = Feld[x][y];      /* should always be non-moving! */
13081   int i;
13082
13083   for (i = 0; i < NUM_DIRECTIONS; i++)
13084   {
13085     int xx = x + xy[i][0];
13086     int yy = y + xy[i][1];
13087     int center_side = trigger_sides[i][0];
13088     int border_side = trigger_sides[i][1];
13089     int border_element;
13090
13091     if (!IN_LEV_FIELD(xx, yy))
13092       continue;
13093
13094     if (IS_PLAYER(x, y))
13095     {
13096       struct PlayerInfo *player = PLAYERINFO(x, y);
13097
13098       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13099         border_element = Feld[xx][yy];          /* may be moving! */
13100       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13101         border_element = Feld[xx][yy];
13102       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13103         border_element = MovingOrBlocked2Element(xx, yy);
13104       else
13105         continue;               /* center and border element do not touch */
13106
13107       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13108                                  player->index_bit, border_side);
13109       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13110                                           CE_PLAYER_TOUCHES_X,
13111                                           player->index_bit, border_side);
13112     }
13113     else if (IS_PLAYER(xx, yy))
13114     {
13115       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13116
13117       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13118       {
13119         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13120           continue;             /* center and border element do not touch */
13121       }
13122
13123       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13124                                  player->index_bit, center_side);
13125       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13126                                           CE_PLAYER_TOUCHES_X,
13127                                           player->index_bit, center_side);
13128       break;
13129     }
13130   }
13131 }
13132
13133 #if USE_ELEMENT_TOUCHING_BUGFIX
13134
13135 void TestIfElementTouchesCustomElement(int x, int y)
13136 {
13137   static int xy[4][2] =
13138   {
13139     { 0, -1 },
13140     { -1, 0 },
13141     { +1, 0 },
13142     { 0, +1 }
13143   };
13144   static int trigger_sides[4][2] =
13145   {
13146     /* center side      border side */
13147     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13148     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13149     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13150     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13151   };
13152   static int touch_dir[4] =
13153   {
13154     MV_LEFT | MV_RIGHT,
13155     MV_UP   | MV_DOWN,
13156     MV_UP   | MV_DOWN,
13157     MV_LEFT | MV_RIGHT
13158   };
13159   boolean change_center_element = FALSE;
13160   int center_element = Feld[x][y];      /* should always be non-moving! */
13161   int border_element_old[NUM_DIRECTIONS];
13162   int i;
13163
13164   for (i = 0; i < NUM_DIRECTIONS; i++)
13165   {
13166     int xx = x + xy[i][0];
13167     int yy = y + xy[i][1];
13168     int border_element;
13169
13170     border_element_old[i] = -1;
13171
13172     if (!IN_LEV_FIELD(xx, yy))
13173       continue;
13174
13175     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13176       border_element = Feld[xx][yy];    /* may be moving! */
13177     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13178       border_element = Feld[xx][yy];
13179     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13180       border_element = MovingOrBlocked2Element(xx, yy);
13181     else
13182       continue;                 /* center and border element do not touch */
13183
13184     border_element_old[i] = border_element;
13185   }
13186
13187   for (i = 0; i < NUM_DIRECTIONS; i++)
13188   {
13189     int xx = x + xy[i][0];
13190     int yy = y + xy[i][1];
13191     int center_side = trigger_sides[i][0];
13192     int border_element = border_element_old[i];
13193
13194     if (border_element == -1)
13195       continue;
13196
13197     /* check for change of border element */
13198     CheckElementChangeBySide(xx, yy, border_element, center_element,
13199                              CE_TOUCHING_X, center_side);
13200   }
13201
13202   for (i = 0; i < NUM_DIRECTIONS; i++)
13203   {
13204     int border_side = trigger_sides[i][1];
13205     int border_element = border_element_old[i];
13206
13207     if (border_element == -1)
13208       continue;
13209
13210     /* check for change of center element (but change it only once) */
13211     if (!change_center_element)
13212       change_center_element =
13213         CheckElementChangeBySide(x, y, center_element, border_element,
13214                                  CE_TOUCHING_X, border_side);
13215   }
13216 }
13217
13218 #else
13219
13220 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13221 {
13222   static int xy[4][2] =
13223   {
13224     { 0, -1 },
13225     { -1, 0 },
13226     { +1, 0 },
13227     { 0, +1 }
13228   };
13229   static int trigger_sides[4][2] =
13230   {
13231     /* center side      border side */
13232     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13233     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13234     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13235     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13236   };
13237   static int touch_dir[4] =
13238   {
13239     MV_LEFT | MV_RIGHT,
13240     MV_UP   | MV_DOWN,
13241     MV_UP   | MV_DOWN,
13242     MV_LEFT | MV_RIGHT
13243   };
13244   boolean change_center_element = FALSE;
13245   int center_element = Feld[x][y];      /* should always be non-moving! */
13246   int i;
13247
13248   for (i = 0; i < NUM_DIRECTIONS; i++)
13249   {
13250     int xx = x + xy[i][0];
13251     int yy = y + xy[i][1];
13252     int center_side = trigger_sides[i][0];
13253     int border_side = trigger_sides[i][1];
13254     int border_element;
13255
13256     if (!IN_LEV_FIELD(xx, yy))
13257       continue;
13258
13259     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13260       border_element = Feld[xx][yy];    /* may be moving! */
13261     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13262       border_element = Feld[xx][yy];
13263     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13264       border_element = MovingOrBlocked2Element(xx, yy);
13265     else
13266       continue;                 /* center and border element do not touch */
13267
13268     /* check for change of center element (but change it only once) */
13269     if (!change_center_element)
13270       change_center_element =
13271         CheckElementChangeBySide(x, y, center_element, border_element,
13272                                  CE_TOUCHING_X, border_side);
13273
13274     /* check for change of border element */
13275     CheckElementChangeBySide(xx, yy, border_element, center_element,
13276                              CE_TOUCHING_X, center_side);
13277   }
13278 }
13279
13280 #endif
13281
13282 void TestIfElementHitsCustomElement(int x, int y, int direction)
13283 {
13284   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13285   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13286   int hitx = x + dx, hity = y + dy;
13287   int hitting_element = Feld[x][y];
13288   int touched_element;
13289
13290   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13291     return;
13292
13293   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13294                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13295
13296   if (IN_LEV_FIELD(hitx, hity))
13297   {
13298     int opposite_direction = MV_DIR_OPPOSITE(direction);
13299     int hitting_side = direction;
13300     int touched_side = opposite_direction;
13301     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13302                           MovDir[hitx][hity] != direction ||
13303                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13304
13305     object_hit = TRUE;
13306
13307     if (object_hit)
13308     {
13309       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13310                                CE_HITTING_X, touched_side);
13311
13312       CheckElementChangeBySide(hitx, hity, touched_element,
13313                                hitting_element, CE_HIT_BY_X, hitting_side);
13314
13315       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13316                                CE_HIT_BY_SOMETHING, opposite_direction);
13317     }
13318   }
13319
13320   /* "hitting something" is also true when hitting the playfield border */
13321   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13322                            CE_HITTING_SOMETHING, direction);
13323 }
13324
13325 #if 0
13326 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13327 {
13328   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13329   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13330   int hitx = x + dx, hity = y + dy;
13331   int hitting_element = Feld[x][y];
13332   int touched_element;
13333 #if 0
13334   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13335                         !IS_FREE(hitx, hity) &&
13336                         (!IS_MOVING(hitx, hity) ||
13337                          MovDir[hitx][hity] != direction ||
13338                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13339 #endif
13340
13341   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13342     return;
13343
13344 #if 0
13345   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13346     return;
13347 #endif
13348
13349   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13350                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13351
13352   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13353                            EP_CAN_SMASH_EVERYTHING, direction);
13354
13355   if (IN_LEV_FIELD(hitx, hity))
13356   {
13357     int opposite_direction = MV_DIR_OPPOSITE(direction);
13358     int hitting_side = direction;
13359     int touched_side = opposite_direction;
13360 #if 0
13361     int touched_element = MovingOrBlocked2Element(hitx, hity);
13362 #endif
13363 #if 1
13364     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13365                           MovDir[hitx][hity] != direction ||
13366                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13367
13368     object_hit = TRUE;
13369 #endif
13370
13371     if (object_hit)
13372     {
13373       int i;
13374
13375       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13376                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13377
13378       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13379                                CE_OTHER_IS_SMASHING, touched_side);
13380
13381       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13382                                CE_OTHER_GETS_SMASHED, hitting_side);
13383     }
13384   }
13385 }
13386 #endif
13387
13388 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13389 {
13390   int i, kill_x = -1, kill_y = -1;
13391
13392   int bad_element = -1;
13393   static int test_xy[4][2] =
13394   {
13395     { 0, -1 },
13396     { -1, 0 },
13397     { +1, 0 },
13398     { 0, +1 }
13399   };
13400   static int test_dir[4] =
13401   {
13402     MV_UP,
13403     MV_LEFT,
13404     MV_RIGHT,
13405     MV_DOWN
13406   };
13407
13408   for (i = 0; i < NUM_DIRECTIONS; i++)
13409   {
13410     int test_x, test_y, test_move_dir, test_element;
13411
13412     test_x = good_x + test_xy[i][0];
13413     test_y = good_y + test_xy[i][1];
13414
13415     if (!IN_LEV_FIELD(test_x, test_y))
13416       continue;
13417
13418     test_move_dir =
13419       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13420
13421     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13422
13423     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13424        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13425     */
13426     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13427         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13428     {
13429       kill_x = test_x;
13430       kill_y = test_y;
13431       bad_element = test_element;
13432
13433       break;
13434     }
13435   }
13436
13437   if (kill_x != -1 || kill_y != -1)
13438   {
13439     if (IS_PLAYER(good_x, good_y))
13440     {
13441       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13442
13443       if (player->shield_deadly_time_left > 0 &&
13444           !IS_INDESTRUCTIBLE(bad_element))
13445         Bang(kill_x, kill_y);
13446       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13447         KillPlayer(player);
13448     }
13449     else
13450       Bang(good_x, good_y);
13451   }
13452 }
13453
13454 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13455 {
13456   int i, kill_x = -1, kill_y = -1;
13457   int bad_element = Feld[bad_x][bad_y];
13458   static int test_xy[4][2] =
13459   {
13460     { 0, -1 },
13461     { -1, 0 },
13462     { +1, 0 },
13463     { 0, +1 }
13464   };
13465   static int touch_dir[4] =
13466   {
13467     MV_LEFT | MV_RIGHT,
13468     MV_UP   | MV_DOWN,
13469     MV_UP   | MV_DOWN,
13470     MV_LEFT | MV_RIGHT
13471   };
13472   static int test_dir[4] =
13473   {
13474     MV_UP,
13475     MV_LEFT,
13476     MV_RIGHT,
13477     MV_DOWN
13478   };
13479
13480   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13481     return;
13482
13483   for (i = 0; i < NUM_DIRECTIONS; i++)
13484   {
13485     int test_x, test_y, test_move_dir, test_element;
13486
13487     test_x = bad_x + test_xy[i][0];
13488     test_y = bad_y + test_xy[i][1];
13489     if (!IN_LEV_FIELD(test_x, test_y))
13490       continue;
13491
13492     test_move_dir =
13493       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13494
13495     test_element = Feld[test_x][test_y];
13496
13497     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13498        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13499     */
13500     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13501         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13502     {
13503       /* good thing is player or penguin that does not move away */
13504       if (IS_PLAYER(test_x, test_y))
13505       {
13506         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13507
13508         if (bad_element == EL_ROBOT && player->is_moving)
13509           continue;     /* robot does not kill player if he is moving */
13510
13511         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13512         {
13513           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13514             continue;           /* center and border element do not touch */
13515         }
13516
13517         kill_x = test_x;
13518         kill_y = test_y;
13519         break;
13520       }
13521       else if (test_element == EL_PENGUIN)
13522       {
13523         kill_x = test_x;
13524         kill_y = test_y;
13525         break;
13526       }
13527     }
13528   }
13529
13530   if (kill_x != -1 || kill_y != -1)
13531   {
13532     if (IS_PLAYER(kill_x, kill_y))
13533     {
13534       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13535
13536       if (player->shield_deadly_time_left > 0 &&
13537           !IS_INDESTRUCTIBLE(bad_element))
13538         Bang(bad_x, bad_y);
13539       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13540         KillPlayer(player);
13541     }
13542     else
13543       Bang(kill_x, kill_y);
13544   }
13545 }
13546
13547 void TestIfPlayerTouchesBadThing(int x, int y)
13548 {
13549   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13550 }
13551
13552 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13553 {
13554   TestIfGoodThingHitsBadThing(x, y, move_dir);
13555 }
13556
13557 void TestIfBadThingTouchesPlayer(int x, int y)
13558 {
13559   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13560 }
13561
13562 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13563 {
13564   TestIfBadThingHitsGoodThing(x, y, move_dir);
13565 }
13566
13567 void TestIfFriendTouchesBadThing(int x, int y)
13568 {
13569   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13570 }
13571
13572 void TestIfBadThingTouchesFriend(int x, int y)
13573 {
13574   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13575 }
13576
13577 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13578 {
13579   int i, kill_x = bad_x, kill_y = bad_y;
13580   static int xy[4][2] =
13581   {
13582     { 0, -1 },
13583     { -1, 0 },
13584     { +1, 0 },
13585     { 0, +1 }
13586   };
13587
13588   for (i = 0; i < NUM_DIRECTIONS; i++)
13589   {
13590     int x, y, element;
13591
13592     x = bad_x + xy[i][0];
13593     y = bad_y + xy[i][1];
13594     if (!IN_LEV_FIELD(x, y))
13595       continue;
13596
13597     element = Feld[x][y];
13598     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13599         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13600     {
13601       kill_x = x;
13602       kill_y = y;
13603       break;
13604     }
13605   }
13606
13607   if (kill_x != bad_x || kill_y != bad_y)
13608     Bang(bad_x, bad_y);
13609 }
13610
13611 void KillPlayer(struct PlayerInfo *player)
13612 {
13613   int jx = player->jx, jy = player->jy;
13614
13615   if (!player->active)
13616     return;
13617
13618   /* the following code was introduced to prevent an infinite loop when calling
13619      -> Bang()
13620      -> CheckTriggeredElementChangeExt()
13621      -> ExecuteCustomElementAction()
13622      -> KillPlayer()
13623      -> (infinitely repeating the above sequence of function calls)
13624      which occurs when killing the player while having a CE with the setting
13625      "kill player X when explosion of <player X>"; the solution using a new
13626      field "player->killed" was chosen for backwards compatibility, although
13627      clever use of the fields "player->active" etc. would probably also work */
13628 #if 1
13629   if (player->killed)
13630     return;
13631 #endif
13632
13633   player->killed = TRUE;
13634
13635   /* remove accessible field at the player's position */
13636   Feld[jx][jy] = EL_EMPTY;
13637
13638   /* deactivate shield (else Bang()/Explode() would not work right) */
13639   player->shield_normal_time_left = 0;
13640   player->shield_deadly_time_left = 0;
13641
13642   Bang(jx, jy);
13643   BuryPlayer(player);
13644 }
13645
13646 static void KillPlayerUnlessEnemyProtected(int x, int y)
13647 {
13648   if (!PLAYER_ENEMY_PROTECTED(x, y))
13649     KillPlayer(PLAYERINFO(x, y));
13650 }
13651
13652 static void KillPlayerUnlessExplosionProtected(int x, int y)
13653 {
13654   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13655     KillPlayer(PLAYERINFO(x, y));
13656 }
13657
13658 void BuryPlayer(struct PlayerInfo *player)
13659 {
13660   int jx = player->jx, jy = player->jy;
13661
13662   if (!player->active)
13663     return;
13664
13665   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13666   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13667
13668   player->GameOver = TRUE;
13669   RemovePlayer(player);
13670 }
13671
13672 void RemovePlayer(struct PlayerInfo *player)
13673 {
13674   int jx = player->jx, jy = player->jy;
13675   int i, found = FALSE;
13676
13677   player->present = FALSE;
13678   player->active = FALSE;
13679
13680   if (!ExplodeField[jx][jy])
13681     StorePlayer[jx][jy] = 0;
13682
13683   if (player->is_moving)
13684     DrawLevelField(player->last_jx, player->last_jy);
13685
13686   for (i = 0; i < MAX_PLAYERS; i++)
13687     if (stored_player[i].active)
13688       found = TRUE;
13689
13690   if (!found)
13691     AllPlayersGone = TRUE;
13692
13693   ExitX = ZX = jx;
13694   ExitY = ZY = jy;
13695 }
13696
13697 #if USE_NEW_SNAP_DELAY
13698 static void setFieldForSnapping(int x, int y, int element, int direction)
13699 {
13700   struct ElementInfo *ei = &element_info[element];
13701   int direction_bit = MV_DIR_TO_BIT(direction);
13702   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13703   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13704                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13705
13706   Feld[x][y] = EL_ELEMENT_SNAPPING;
13707   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13708
13709   ResetGfxAnimation(x, y);
13710
13711   GfxElement[x][y] = element;
13712   GfxAction[x][y] = action;
13713   GfxDir[x][y] = direction;
13714   GfxFrame[x][y] = -1;
13715 }
13716 #endif
13717
13718 /*
13719   =============================================================================
13720   checkDiagonalPushing()
13721   -----------------------------------------------------------------------------
13722   check if diagonal input device direction results in pushing of object
13723   (by checking if the alternative direction is walkable, diggable, ...)
13724   =============================================================================
13725 */
13726
13727 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13728                                     int x, int y, int real_dx, int real_dy)
13729 {
13730   int jx, jy, dx, dy, xx, yy;
13731
13732   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13733     return TRUE;
13734
13735   /* diagonal direction: check alternative direction */
13736   jx = player->jx;
13737   jy = player->jy;
13738   dx = x - jx;
13739   dy = y - jy;
13740   xx = jx + (dx == 0 ? real_dx : 0);
13741   yy = jy + (dy == 0 ? real_dy : 0);
13742
13743   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13744 }
13745
13746 /*
13747   =============================================================================
13748   DigField()
13749   -----------------------------------------------------------------------------
13750   x, y:                 field next to player (non-diagonal) to try to dig to
13751   real_dx, real_dy:     direction as read from input device (can be diagonal)
13752   =============================================================================
13753 */
13754
13755 int DigField(struct PlayerInfo *player,
13756              int oldx, int oldy, int x, int y,
13757              int real_dx, int real_dy, int mode)
13758 {
13759   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13760   boolean player_was_pushing = player->is_pushing;
13761   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13762   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13763   int jx = oldx, jy = oldy;
13764   int dx = x - jx, dy = y - jy;
13765   int nextx = x + dx, nexty = y + dy;
13766   int move_direction = (dx == -1 ? MV_LEFT  :
13767                         dx == +1 ? MV_RIGHT :
13768                         dy == -1 ? MV_UP    :
13769                         dy == +1 ? MV_DOWN  : MV_NONE);
13770   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13771   int dig_side = MV_DIR_OPPOSITE(move_direction);
13772   int old_element = Feld[jx][jy];
13773 #if USE_FIXED_DONT_RUN_INTO
13774   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13775 #else
13776   int element;
13777 #endif
13778   int collect_count;
13779
13780   if (is_player)                /* function can also be called by EL_PENGUIN */
13781   {
13782     if (player->MovPos == 0)
13783     {
13784       player->is_digging = FALSE;
13785       player->is_collecting = FALSE;
13786     }
13787
13788     if (player->MovPos == 0)    /* last pushing move finished */
13789       player->is_pushing = FALSE;
13790
13791     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13792     {
13793       player->is_switching = FALSE;
13794       player->push_delay = -1;
13795
13796       return MP_NO_ACTION;
13797     }
13798   }
13799
13800 #if !USE_FIXED_DONT_RUN_INTO
13801   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13802     return MP_NO_ACTION;
13803 #endif
13804
13805   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13806     old_element = Back[jx][jy];
13807
13808   /* in case of element dropped at player position, check background */
13809   else if (Back[jx][jy] != EL_EMPTY &&
13810            game.engine_version >= VERSION_IDENT(2,2,0,0))
13811     old_element = Back[jx][jy];
13812
13813   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13814     return MP_NO_ACTION;        /* field has no opening in this direction */
13815
13816   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13817     return MP_NO_ACTION;        /* field has no opening in this direction */
13818
13819 #if USE_FIXED_DONT_RUN_INTO
13820   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13821   {
13822     SplashAcid(x, y);
13823
13824     Feld[jx][jy] = player->artwork_element;
13825     InitMovingField(jx, jy, MV_DOWN);
13826     Store[jx][jy] = EL_ACID;
13827     ContinueMoving(jx, jy);
13828     BuryPlayer(player);
13829
13830     return MP_DONT_RUN_INTO;
13831   }
13832 #endif
13833
13834 #if USE_FIXED_DONT_RUN_INTO
13835   if (player_can_move && DONT_RUN_INTO(element))
13836   {
13837     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13838
13839     return MP_DONT_RUN_INTO;
13840   }
13841 #endif
13842
13843 #if USE_FIXED_DONT_RUN_INTO
13844   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13845     return MP_NO_ACTION;
13846 #endif
13847
13848 #if !USE_FIXED_DONT_RUN_INTO
13849   element = Feld[x][y];
13850 #endif
13851
13852   collect_count = element_info[element].collect_count_initial;
13853
13854   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13855     return MP_NO_ACTION;
13856
13857   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13858     player_can_move = player_can_move_or_snap;
13859
13860   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13861       game.engine_version >= VERSION_IDENT(2,2,0,0))
13862   {
13863     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13864                                player->index_bit, dig_side);
13865     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13866                                         player->index_bit, dig_side);
13867
13868     if (element == EL_DC_LANDMINE)
13869       Bang(x, y);
13870
13871     if (Feld[x][y] != element)          /* field changed by snapping */
13872       return MP_ACTION;
13873
13874     return MP_NO_ACTION;
13875   }
13876
13877 #if USE_PLAYER_GRAVITY
13878   if (player->gravity && is_player && !player->is_auto_moving &&
13879       canFallDown(player) && move_direction != MV_DOWN &&
13880       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13881     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13882 #else
13883   if (game.gravity && is_player && !player->is_auto_moving &&
13884       canFallDown(player) && move_direction != MV_DOWN &&
13885       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13886     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13887 #endif
13888
13889   if (player_can_move &&
13890       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13891   {
13892     int sound_element = SND_ELEMENT(element);
13893     int sound_action = ACTION_WALKING;
13894
13895     if (IS_RND_GATE(element))
13896     {
13897       if (!player->key[RND_GATE_NR(element)])
13898         return MP_NO_ACTION;
13899     }
13900     else if (IS_RND_GATE_GRAY(element))
13901     {
13902       if (!player->key[RND_GATE_GRAY_NR(element)])
13903         return MP_NO_ACTION;
13904     }
13905     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13906     {
13907       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13908         return MP_NO_ACTION;
13909     }
13910     else if (element == EL_EXIT_OPEN ||
13911              element == EL_EM_EXIT_OPEN ||
13912              element == EL_STEEL_EXIT_OPEN ||
13913              element == EL_EM_STEEL_EXIT_OPEN ||
13914              element == EL_SP_EXIT_OPEN ||
13915              element == EL_SP_EXIT_OPENING)
13916     {
13917       sound_action = ACTION_PASSING;    /* player is passing exit */
13918     }
13919     else if (element == EL_EMPTY)
13920     {
13921       sound_action = ACTION_MOVING;             /* nothing to walk on */
13922     }
13923
13924     /* play sound from background or player, whatever is available */
13925     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13926       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13927     else
13928       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13929   }
13930   else if (player_can_move &&
13931            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13932   {
13933     if (!ACCESS_FROM(element, opposite_direction))
13934       return MP_NO_ACTION;      /* field not accessible from this direction */
13935
13936     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13937       return MP_NO_ACTION;
13938
13939     if (IS_EM_GATE(element))
13940     {
13941       if (!player->key[EM_GATE_NR(element)])
13942         return MP_NO_ACTION;
13943     }
13944     else if (IS_EM_GATE_GRAY(element))
13945     {
13946       if (!player->key[EM_GATE_GRAY_NR(element)])
13947         return MP_NO_ACTION;
13948     }
13949     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13950     {
13951       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13952         return MP_NO_ACTION;
13953     }
13954     else if (IS_EMC_GATE(element))
13955     {
13956       if (!player->key[EMC_GATE_NR(element)])
13957         return MP_NO_ACTION;
13958     }
13959     else if (IS_EMC_GATE_GRAY(element))
13960     {
13961       if (!player->key[EMC_GATE_GRAY_NR(element)])
13962         return MP_NO_ACTION;
13963     }
13964     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13965     {
13966       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13967         return MP_NO_ACTION;
13968     }
13969     else if (element == EL_DC_GATE_WHITE ||
13970              element == EL_DC_GATE_WHITE_GRAY ||
13971              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13972     {
13973       if (player->num_white_keys == 0)
13974         return MP_NO_ACTION;
13975
13976       player->num_white_keys--;
13977     }
13978     else if (IS_SP_PORT(element))
13979     {
13980       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13981           element == EL_SP_GRAVITY_PORT_RIGHT ||
13982           element == EL_SP_GRAVITY_PORT_UP ||
13983           element == EL_SP_GRAVITY_PORT_DOWN)
13984 #if USE_PLAYER_GRAVITY
13985         player->gravity = !player->gravity;
13986 #else
13987         game.gravity = !game.gravity;
13988 #endif
13989       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13990                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13991                element == EL_SP_GRAVITY_ON_PORT_UP ||
13992                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13993 #if USE_PLAYER_GRAVITY
13994         player->gravity = TRUE;
13995 #else
13996         game.gravity = TRUE;
13997 #endif
13998       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13999                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14000                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14001                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14002 #if USE_PLAYER_GRAVITY
14003         player->gravity = FALSE;
14004 #else
14005         game.gravity = FALSE;
14006 #endif
14007     }
14008
14009     /* automatically move to the next field with double speed */
14010     player->programmed_action = move_direction;
14011
14012     if (player->move_delay_reset_counter == 0)
14013     {
14014       player->move_delay_reset_counter = 2;     /* two double speed steps */
14015
14016       DOUBLE_PLAYER_SPEED(player);
14017     }
14018
14019     PlayLevelSoundAction(x, y, ACTION_PASSING);
14020   }
14021   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14022   {
14023     RemoveField(x, y);
14024
14025     if (mode != DF_SNAP)
14026     {
14027       GfxElement[x][y] = GFX_ELEMENT(element);
14028       player->is_digging = TRUE;
14029     }
14030
14031     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14032
14033     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14034                                         player->index_bit, dig_side);
14035
14036     if (mode == DF_SNAP)
14037     {
14038 #if USE_NEW_SNAP_DELAY
14039       if (level.block_snap_field)
14040         setFieldForSnapping(x, y, element, move_direction);
14041       else
14042         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14043 #else
14044       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14045 #endif
14046
14047       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14048                                           player->index_bit, dig_side);
14049     }
14050   }
14051   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14052   {
14053     RemoveField(x, y);
14054
14055     if (is_player && mode != DF_SNAP)
14056     {
14057       GfxElement[x][y] = element;
14058       player->is_collecting = TRUE;
14059     }
14060
14061     if (element == EL_SPEED_PILL)
14062     {
14063       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14064     }
14065     else if (element == EL_EXTRA_TIME && level.time > 0)
14066     {
14067       TimeLeft += level.extra_time;
14068
14069 #if 1
14070       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14071
14072       DisplayGameControlValues();
14073 #else
14074       DrawGameValue_Time(TimeLeft);
14075 #endif
14076     }
14077     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14078     {
14079       player->shield_normal_time_left += level.shield_normal_time;
14080       if (element == EL_SHIELD_DEADLY)
14081         player->shield_deadly_time_left += level.shield_deadly_time;
14082     }
14083     else if (element == EL_DYNAMITE ||
14084              element == EL_EM_DYNAMITE ||
14085              element == EL_SP_DISK_RED)
14086     {
14087       if (player->inventory_size < MAX_INVENTORY_SIZE)
14088         player->inventory_element[player->inventory_size++] = element;
14089
14090       DrawGameDoorValues();
14091     }
14092     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14093     {
14094       player->dynabomb_count++;
14095       player->dynabombs_left++;
14096     }
14097     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14098     {
14099       player->dynabomb_size++;
14100     }
14101     else if (element == EL_DYNABOMB_INCREASE_POWER)
14102     {
14103       player->dynabomb_xl = TRUE;
14104     }
14105     else if (IS_KEY(element))
14106     {
14107       player->key[KEY_NR(element)] = TRUE;
14108
14109       DrawGameDoorValues();
14110     }
14111     else if (element == EL_DC_KEY_WHITE)
14112     {
14113       player->num_white_keys++;
14114
14115       /* display white keys? */
14116       /* DrawGameDoorValues(); */
14117     }
14118     else if (IS_ENVELOPE(element))
14119     {
14120       player->show_envelope = element;
14121     }
14122     else if (element == EL_EMC_LENSES)
14123     {
14124       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14125
14126       RedrawAllInvisibleElementsForLenses();
14127     }
14128     else if (element == EL_EMC_MAGNIFIER)
14129     {
14130       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14131
14132       RedrawAllInvisibleElementsForMagnifier();
14133     }
14134     else if (IS_DROPPABLE(element) ||
14135              IS_THROWABLE(element))     /* can be collected and dropped */
14136     {
14137       int i;
14138
14139       if (collect_count == 0)
14140         player->inventory_infinite_element = element;
14141       else
14142         for (i = 0; i < collect_count; i++)
14143           if (player->inventory_size < MAX_INVENTORY_SIZE)
14144             player->inventory_element[player->inventory_size++] = element;
14145
14146       DrawGameDoorValues();
14147     }
14148     else if (collect_count > 0)
14149     {
14150       local_player->gems_still_needed -= collect_count;
14151       if (local_player->gems_still_needed < 0)
14152         local_player->gems_still_needed = 0;
14153
14154 #if 1
14155       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14156
14157       DisplayGameControlValues();
14158 #else
14159       DrawGameValue_Emeralds(local_player->gems_still_needed);
14160 #endif
14161     }
14162
14163     RaiseScoreElement(element);
14164     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14165
14166     if (is_player)
14167       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14168                                           player->index_bit, dig_side);
14169
14170     if (mode == DF_SNAP)
14171     {
14172 #if USE_NEW_SNAP_DELAY
14173       if (level.block_snap_field)
14174         setFieldForSnapping(x, y, element, move_direction);
14175       else
14176         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14177 #else
14178       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14179 #endif
14180
14181       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14182                                           player->index_bit, dig_side);
14183     }
14184   }
14185   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14186   {
14187     if (mode == DF_SNAP && element != EL_BD_ROCK)
14188       return MP_NO_ACTION;
14189
14190     if (CAN_FALL(element) && dy)
14191       return MP_NO_ACTION;
14192
14193     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14194         !(element == EL_SPRING && level.use_spring_bug))
14195       return MP_NO_ACTION;
14196
14197     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14198         ((move_direction & MV_VERTICAL &&
14199           ((element_info[element].move_pattern & MV_LEFT &&
14200             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14201            (element_info[element].move_pattern & MV_RIGHT &&
14202             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14203          (move_direction & MV_HORIZONTAL &&
14204           ((element_info[element].move_pattern & MV_UP &&
14205             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14206            (element_info[element].move_pattern & MV_DOWN &&
14207             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14208       return MP_NO_ACTION;
14209
14210     /* do not push elements already moving away faster than player */
14211     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14212         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14213       return MP_NO_ACTION;
14214
14215     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14216     {
14217       if (player->push_delay_value == -1 || !player_was_pushing)
14218         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14219     }
14220     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14221     {
14222       if (player->push_delay_value == -1)
14223         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14224     }
14225     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14226     {
14227       if (!player->is_pushing)
14228         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14229     }
14230
14231     player->is_pushing = TRUE;
14232     player->is_active = TRUE;
14233
14234     if (!(IN_LEV_FIELD(nextx, nexty) &&
14235           (IS_FREE(nextx, nexty) ||
14236            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14237             IS_SB_ELEMENT(element)))))
14238       return MP_NO_ACTION;
14239
14240     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14241       return MP_NO_ACTION;
14242
14243     if (player->push_delay == -1)       /* new pushing; restart delay */
14244       player->push_delay = 0;
14245
14246     if (player->push_delay < player->push_delay_value &&
14247         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14248         element != EL_SPRING && element != EL_BALLOON)
14249     {
14250       /* make sure that there is no move delay before next try to push */
14251       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14252         player->move_delay = 0;
14253
14254       return MP_NO_ACTION;
14255     }
14256
14257     if (IS_SB_ELEMENT(element))
14258     {
14259       if (element == EL_SOKOBAN_FIELD_FULL)
14260       {
14261         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14262         local_player->sokobanfields_still_needed++;
14263       }
14264
14265       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14266       {
14267         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14268         local_player->sokobanfields_still_needed--;
14269       }
14270
14271       Feld[x][y] = EL_SOKOBAN_OBJECT;
14272
14273       if (Back[x][y] == Back[nextx][nexty])
14274         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14275       else if (Back[x][y] != 0)
14276         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14277                                     ACTION_EMPTYING);
14278       else
14279         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14280                                     ACTION_FILLING);
14281
14282       if (local_player->sokobanfields_still_needed == 0 &&
14283           game.emulation == EMU_SOKOBAN)
14284       {
14285         PlayerWins(player);
14286
14287         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14288       }
14289     }
14290     else
14291       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14292
14293     InitMovingField(x, y, move_direction);
14294     GfxAction[x][y] = ACTION_PUSHING;
14295
14296     if (mode == DF_SNAP)
14297       ContinueMoving(x, y);
14298     else
14299       MovPos[x][y] = (dx != 0 ? dx : dy);
14300
14301     Pushed[x][y] = TRUE;
14302     Pushed[nextx][nexty] = TRUE;
14303
14304     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14305       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14306     else
14307       player->push_delay_value = -1;    /* get new value later */
14308
14309     /* check for element change _after_ element has been pushed */
14310     if (game.use_change_when_pushing_bug)
14311     {
14312       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14313                                  player->index_bit, dig_side);
14314       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14315                                           player->index_bit, dig_side);
14316     }
14317   }
14318   else if (IS_SWITCHABLE(element))
14319   {
14320     if (PLAYER_SWITCHING(player, x, y))
14321     {
14322       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14323                                           player->index_bit, dig_side);
14324
14325       return MP_ACTION;
14326     }
14327
14328     player->is_switching = TRUE;
14329     player->switch_x = x;
14330     player->switch_y = y;
14331
14332     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14333
14334     if (element == EL_ROBOT_WHEEL)
14335     {
14336       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14337       ZX = x;
14338       ZY = y;
14339
14340       game.robot_wheel_active = TRUE;
14341
14342       DrawLevelField(x, y);
14343     }
14344     else if (element == EL_SP_TERMINAL)
14345     {
14346       int xx, yy;
14347
14348       SCAN_PLAYFIELD(xx, yy)
14349       {
14350         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14351           Bang(xx, yy);
14352         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14353           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14354       }
14355     }
14356     else if (IS_BELT_SWITCH(element))
14357     {
14358       ToggleBeltSwitch(x, y);
14359     }
14360     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14361              element == EL_SWITCHGATE_SWITCH_DOWN ||
14362              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14363              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14364     {
14365       ToggleSwitchgateSwitch(x, y);
14366     }
14367     else if (element == EL_LIGHT_SWITCH ||
14368              element == EL_LIGHT_SWITCH_ACTIVE)
14369     {
14370       ToggleLightSwitch(x, y);
14371     }
14372     else if (element == EL_TIMEGATE_SWITCH ||
14373              element == EL_DC_TIMEGATE_SWITCH)
14374     {
14375       ActivateTimegateSwitch(x, y);
14376     }
14377     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14378              element == EL_BALLOON_SWITCH_RIGHT ||
14379              element == EL_BALLOON_SWITCH_UP    ||
14380              element == EL_BALLOON_SWITCH_DOWN  ||
14381              element == EL_BALLOON_SWITCH_NONE  ||
14382              element == EL_BALLOON_SWITCH_ANY)
14383     {
14384       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14385                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14386                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14387                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14388                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14389                              move_direction);
14390     }
14391     else if (element == EL_LAMP)
14392     {
14393       Feld[x][y] = EL_LAMP_ACTIVE;
14394       local_player->lights_still_needed--;
14395
14396       ResetGfxAnimation(x, y);
14397       DrawLevelField(x, y);
14398     }
14399     else if (element == EL_TIME_ORB_FULL)
14400     {
14401       Feld[x][y] = EL_TIME_ORB_EMPTY;
14402
14403       if (level.time > 0 || level.use_time_orb_bug)
14404       {
14405         TimeLeft += level.time_orb_time;
14406
14407 #if 1
14408         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14409
14410         DisplayGameControlValues();
14411 #else
14412         DrawGameValue_Time(TimeLeft);
14413 #endif
14414       }
14415
14416       ResetGfxAnimation(x, y);
14417       DrawLevelField(x, y);
14418     }
14419     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14420              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14421     {
14422       int xx, yy;
14423
14424       game.ball_state = !game.ball_state;
14425
14426       SCAN_PLAYFIELD(xx, yy)
14427       {
14428         int e = Feld[xx][yy];
14429
14430         if (game.ball_state)
14431         {
14432           if (e == EL_EMC_MAGIC_BALL)
14433             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14434           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14435             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14436         }
14437         else
14438         {
14439           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14440             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14441           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14442             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14443         }
14444       }
14445     }
14446
14447     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14448                                         player->index_bit, dig_side);
14449
14450     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14451                                         player->index_bit, dig_side);
14452
14453     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14454                                         player->index_bit, dig_side);
14455
14456     return MP_ACTION;
14457   }
14458   else
14459   {
14460     if (!PLAYER_SWITCHING(player, x, y))
14461     {
14462       player->is_switching = TRUE;
14463       player->switch_x = x;
14464       player->switch_y = y;
14465
14466       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14467                                  player->index_bit, dig_side);
14468       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14469                                           player->index_bit, dig_side);
14470
14471       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14472                                  player->index_bit, dig_side);
14473       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14474                                           player->index_bit, dig_side);
14475     }
14476
14477     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14478                                player->index_bit, dig_side);
14479     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14480                                         player->index_bit, dig_side);
14481
14482     return MP_NO_ACTION;
14483   }
14484
14485   player->push_delay = -1;
14486
14487   if (is_player)                /* function can also be called by EL_PENGUIN */
14488   {
14489     if (Feld[x][y] != element)          /* really digged/collected something */
14490     {
14491       player->is_collecting = !player->is_digging;
14492       player->is_active = TRUE;
14493     }
14494   }
14495
14496   return MP_MOVING;
14497 }
14498
14499 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14500 {
14501   int jx = player->jx, jy = player->jy;
14502   int x = jx + dx, y = jy + dy;
14503   int snap_direction = (dx == -1 ? MV_LEFT  :
14504                         dx == +1 ? MV_RIGHT :
14505                         dy == -1 ? MV_UP    :
14506                         dy == +1 ? MV_DOWN  : MV_NONE);
14507   boolean can_continue_snapping = (level.continuous_snapping &&
14508                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14509
14510   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14511     return FALSE;
14512
14513   if (!player->active || !IN_LEV_FIELD(x, y))
14514     return FALSE;
14515
14516   if (dx && dy)
14517     return FALSE;
14518
14519   if (!dx && !dy)
14520   {
14521     if (player->MovPos == 0)
14522       player->is_pushing = FALSE;
14523
14524     player->is_snapping = FALSE;
14525
14526     if (player->MovPos == 0)
14527     {
14528       player->is_moving = FALSE;
14529       player->is_digging = FALSE;
14530       player->is_collecting = FALSE;
14531     }
14532
14533     return FALSE;
14534   }
14535
14536 #if USE_NEW_CONTINUOUS_SNAPPING
14537   /* prevent snapping with already pressed snap key when not allowed */
14538   if (player->is_snapping && !can_continue_snapping)
14539     return FALSE;
14540 #else
14541   if (player->is_snapping)
14542     return FALSE;
14543 #endif
14544
14545   player->MovDir = snap_direction;
14546
14547   if (player->MovPos == 0)
14548   {
14549     player->is_moving = FALSE;
14550     player->is_digging = FALSE;
14551     player->is_collecting = FALSE;
14552   }
14553
14554   player->is_dropping = FALSE;
14555   player->is_dropping_pressed = FALSE;
14556   player->drop_pressed_delay = 0;
14557
14558   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14559     return FALSE;
14560
14561   player->is_snapping = TRUE;
14562   player->is_active = TRUE;
14563
14564   if (player->MovPos == 0)
14565   {
14566     player->is_moving = FALSE;
14567     player->is_digging = FALSE;
14568     player->is_collecting = FALSE;
14569   }
14570
14571   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14572     DrawLevelField(player->last_jx, player->last_jy);
14573
14574   DrawLevelField(x, y);
14575
14576   return TRUE;
14577 }
14578
14579 boolean DropElement(struct PlayerInfo *player)
14580 {
14581   int old_element, new_element;
14582   int dropx = player->jx, dropy = player->jy;
14583   int drop_direction = player->MovDir;
14584   int drop_side = drop_direction;
14585 #if 1
14586   int drop_element = get_next_dropped_element(player);
14587 #else
14588   int drop_element = (player->inventory_size > 0 ?
14589                       player->inventory_element[player->inventory_size - 1] :
14590                       player->inventory_infinite_element != EL_UNDEFINED ?
14591                       player->inventory_infinite_element :
14592                       player->dynabombs_left > 0 ?
14593                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14594                       EL_UNDEFINED);
14595 #endif
14596
14597   player->is_dropping_pressed = TRUE;
14598
14599   /* do not drop an element on top of another element; when holding drop key
14600      pressed without moving, dropped element must move away before the next
14601      element can be dropped (this is especially important if the next element
14602      is dynamite, which can be placed on background for historical reasons) */
14603   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14604     return MP_ACTION;
14605
14606   if (IS_THROWABLE(drop_element))
14607   {
14608     dropx += GET_DX_FROM_DIR(drop_direction);
14609     dropy += GET_DY_FROM_DIR(drop_direction);
14610
14611     if (!IN_LEV_FIELD(dropx, dropy))
14612       return FALSE;
14613   }
14614
14615   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14616   new_element = drop_element;           /* default: no change when dropping */
14617
14618   /* check if player is active, not moving and ready to drop */
14619   if (!player->active || player->MovPos || player->drop_delay > 0)
14620     return FALSE;
14621
14622   /* check if player has anything that can be dropped */
14623   if (new_element == EL_UNDEFINED)
14624     return FALSE;
14625
14626   /* check if drop key was pressed long enough for EM style dynamite */
14627   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14628     return FALSE;
14629
14630   /* check if anything can be dropped at the current position */
14631   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14632     return FALSE;
14633
14634   /* collected custom elements can only be dropped on empty fields */
14635   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14636     return FALSE;
14637
14638   if (old_element != EL_EMPTY)
14639     Back[dropx][dropy] = old_element;   /* store old element on this field */
14640
14641   ResetGfxAnimation(dropx, dropy);
14642   ResetRandomAnimationValue(dropx, dropy);
14643
14644   if (player->inventory_size > 0 ||
14645       player->inventory_infinite_element != EL_UNDEFINED)
14646   {
14647     if (player->inventory_size > 0)
14648     {
14649       player->inventory_size--;
14650
14651       DrawGameDoorValues();
14652
14653       if (new_element == EL_DYNAMITE)
14654         new_element = EL_DYNAMITE_ACTIVE;
14655       else if (new_element == EL_EM_DYNAMITE)
14656         new_element = EL_EM_DYNAMITE_ACTIVE;
14657       else if (new_element == EL_SP_DISK_RED)
14658         new_element = EL_SP_DISK_RED_ACTIVE;
14659     }
14660
14661     Feld[dropx][dropy] = new_element;
14662
14663     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14664       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14665                           el2img(Feld[dropx][dropy]), 0);
14666
14667     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14668
14669     /* needed if previous element just changed to "empty" in the last frame */
14670     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14671
14672     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14673                                player->index_bit, drop_side);
14674     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14675                                         CE_PLAYER_DROPS_X,
14676                                         player->index_bit, drop_side);
14677
14678     TestIfElementTouchesCustomElement(dropx, dropy);
14679   }
14680   else          /* player is dropping a dyna bomb */
14681   {
14682     player->dynabombs_left--;
14683
14684     Feld[dropx][dropy] = new_element;
14685
14686     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14687       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14688                           el2img(Feld[dropx][dropy]), 0);
14689
14690     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14691   }
14692
14693   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14694     InitField_WithBug1(dropx, dropy, FALSE);
14695
14696   new_element = Feld[dropx][dropy];     /* element might have changed */
14697
14698   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14699       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14700   {
14701     int move_direction, nextx, nexty;
14702
14703     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14704       MovDir[dropx][dropy] = drop_direction;
14705
14706     move_direction = MovDir[dropx][dropy];
14707     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14708     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14709
14710     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14711
14712 #if USE_FIX_IMPACT_COLLISION
14713     /* do not cause impact style collision by dropping elements that can fall */
14714     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14715 #else
14716     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14717 #endif
14718   }
14719
14720   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14721   player->is_dropping = TRUE;
14722
14723   player->drop_pressed_delay = 0;
14724   player->is_dropping_pressed = FALSE;
14725
14726   player->drop_x = dropx;
14727   player->drop_y = dropy;
14728
14729   return TRUE;
14730 }
14731
14732 /* ------------------------------------------------------------------------- */
14733 /* game sound playing functions                                              */
14734 /* ------------------------------------------------------------------------- */
14735
14736 static int *loop_sound_frame = NULL;
14737 static int *loop_sound_volume = NULL;
14738
14739 void InitPlayLevelSound()
14740 {
14741   int num_sounds = getSoundListSize();
14742
14743   checked_free(loop_sound_frame);
14744   checked_free(loop_sound_volume);
14745
14746   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14747   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14748 }
14749
14750 static void PlayLevelSound(int x, int y, int nr)
14751 {
14752   int sx = SCREENX(x), sy = SCREENY(y);
14753   int volume, stereo_position;
14754   int max_distance = 8;
14755   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14756
14757   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14758       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14759     return;
14760
14761   if (!IN_LEV_FIELD(x, y) ||
14762       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14763       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14764     return;
14765
14766   volume = SOUND_MAX_VOLUME;
14767
14768   if (!IN_SCR_FIELD(sx, sy))
14769   {
14770     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14771     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14772
14773     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14774   }
14775
14776   stereo_position = (SOUND_MAX_LEFT +
14777                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14778                      (SCR_FIELDX + 2 * max_distance));
14779
14780   if (IS_LOOP_SOUND(nr))
14781   {
14782     /* This assures that quieter loop sounds do not overwrite louder ones,
14783        while restarting sound volume comparison with each new game frame. */
14784
14785     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14786       return;
14787
14788     loop_sound_volume[nr] = volume;
14789     loop_sound_frame[nr] = FrameCounter;
14790   }
14791
14792   PlaySoundExt(nr, volume, stereo_position, type);
14793 }
14794
14795 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14796 {
14797   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14798                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14799                  y < LEVELY(BY1) ? LEVELY(BY1) :
14800                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14801                  sound_action);
14802 }
14803
14804 static void PlayLevelSoundAction(int x, int y, int action)
14805 {
14806   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14807 }
14808
14809 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14810 {
14811   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14812
14813   if (sound_effect != SND_UNDEFINED)
14814     PlayLevelSound(x, y, sound_effect);
14815 }
14816
14817 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14818                                               int action)
14819 {
14820   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14821
14822   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14823     PlayLevelSound(x, y, sound_effect);
14824 }
14825
14826 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14827 {
14828   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14829
14830   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14831     PlayLevelSound(x, y, sound_effect);
14832 }
14833
14834 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14835 {
14836   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14837
14838   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14839     StopSound(sound_effect);
14840 }
14841
14842 static void PlayLevelMusic()
14843 {
14844   if (levelset.music[level_nr] != MUS_UNDEFINED)
14845     PlayMusic(levelset.music[level_nr]);        /* from config file */
14846   else
14847     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14848 }
14849
14850 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14851 {
14852   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14853   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14854   int x = xx - 1 - offset;
14855   int y = yy - 1 - offset;
14856
14857   switch (sample)
14858   {
14859     case SAMPLE_blank:
14860       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14861       break;
14862
14863     case SAMPLE_roll:
14864       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14865       break;
14866
14867     case SAMPLE_stone:
14868       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14869       break;
14870
14871     case SAMPLE_nut:
14872       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14873       break;
14874
14875     case SAMPLE_crack:
14876       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14877       break;
14878
14879     case SAMPLE_bug:
14880       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14881       break;
14882
14883     case SAMPLE_tank:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14885       break;
14886
14887     case SAMPLE_android_clone:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14889       break;
14890
14891     case SAMPLE_android_move:
14892       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14893       break;
14894
14895     case SAMPLE_spring:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14897       break;
14898
14899     case SAMPLE_slurp:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14901       break;
14902
14903     case SAMPLE_eater:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14905       break;
14906
14907     case SAMPLE_eater_eat:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14909       break;
14910
14911     case SAMPLE_alien:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14913       break;
14914
14915     case SAMPLE_collect:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14917       break;
14918
14919     case SAMPLE_diamond:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14921       break;
14922
14923     case SAMPLE_squash:
14924       /* !!! CHECK THIS !!! */
14925 #if 1
14926       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14927 #else
14928       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14929 #endif
14930       break;
14931
14932     case SAMPLE_wonderfall:
14933       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14934       break;
14935
14936     case SAMPLE_drip:
14937       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14938       break;
14939
14940     case SAMPLE_push:
14941       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14942       break;
14943
14944     case SAMPLE_dirt:
14945       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14946       break;
14947
14948     case SAMPLE_acid:
14949       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14950       break;
14951
14952     case SAMPLE_ball:
14953       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14954       break;
14955
14956     case SAMPLE_grow:
14957       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14958       break;
14959
14960     case SAMPLE_wonder:
14961       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14962       break;
14963
14964     case SAMPLE_door:
14965       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14966       break;
14967
14968     case SAMPLE_exit_open:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14970       break;
14971
14972     case SAMPLE_exit_leave:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14974       break;
14975
14976     case SAMPLE_dynamite:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14978       break;
14979
14980     case SAMPLE_tick:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14982       break;
14983
14984     case SAMPLE_press:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14986       break;
14987
14988     case SAMPLE_wheel:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14990       break;
14991
14992     case SAMPLE_boom:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14994       break;
14995
14996     case SAMPLE_die:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14998       break;
14999
15000     case SAMPLE_time:
15001       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15002       break;
15003
15004     default:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15006       break;
15007   }
15008 }
15009
15010 #if 0
15011 void ChangeTime(int value)
15012 {
15013   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15014
15015   *time += value;
15016
15017   /* EMC game engine uses value from time counter of RND game engine */
15018   level.native_em_level->lev->time = *time;
15019
15020   DrawGameValue_Time(*time);
15021 }
15022
15023 void RaiseScore(int value)
15024 {
15025   /* EMC game engine and RND game engine have separate score counters */
15026   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15027                 &level.native_em_level->lev->score : &local_player->score);
15028
15029   *score += value;
15030
15031   DrawGameValue_Score(*score);
15032 }
15033 #endif
15034
15035 void RaiseScore(int value)
15036 {
15037   local_player->score += value;
15038
15039 #if 1
15040   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15041
15042   DisplayGameControlValues();
15043 #else
15044   DrawGameValue_Score(local_player->score);
15045 #endif
15046 }
15047
15048 void RaiseScoreElement(int element)
15049 {
15050   switch (element)
15051   {
15052     case EL_EMERALD:
15053     case EL_BD_DIAMOND:
15054     case EL_EMERALD_YELLOW:
15055     case EL_EMERALD_RED:
15056     case EL_EMERALD_PURPLE:
15057     case EL_SP_INFOTRON:
15058       RaiseScore(level.score[SC_EMERALD]);
15059       break;
15060     case EL_DIAMOND:
15061       RaiseScore(level.score[SC_DIAMOND]);
15062       break;
15063     case EL_CRYSTAL:
15064       RaiseScore(level.score[SC_CRYSTAL]);
15065       break;
15066     case EL_PEARL:
15067       RaiseScore(level.score[SC_PEARL]);
15068       break;
15069     case EL_BUG:
15070     case EL_BD_BUTTERFLY:
15071     case EL_SP_ELECTRON:
15072       RaiseScore(level.score[SC_BUG]);
15073       break;
15074     case EL_SPACESHIP:
15075     case EL_BD_FIREFLY:
15076     case EL_SP_SNIKSNAK:
15077       RaiseScore(level.score[SC_SPACESHIP]);
15078       break;
15079     case EL_YAMYAM:
15080     case EL_DARK_YAMYAM:
15081       RaiseScore(level.score[SC_YAMYAM]);
15082       break;
15083     case EL_ROBOT:
15084       RaiseScore(level.score[SC_ROBOT]);
15085       break;
15086     case EL_PACMAN:
15087       RaiseScore(level.score[SC_PACMAN]);
15088       break;
15089     case EL_NUT:
15090       RaiseScore(level.score[SC_NUT]);
15091       break;
15092     case EL_DYNAMITE:
15093     case EL_EM_DYNAMITE:
15094     case EL_SP_DISK_RED:
15095     case EL_DYNABOMB_INCREASE_NUMBER:
15096     case EL_DYNABOMB_INCREASE_SIZE:
15097     case EL_DYNABOMB_INCREASE_POWER:
15098       RaiseScore(level.score[SC_DYNAMITE]);
15099       break;
15100     case EL_SHIELD_NORMAL:
15101     case EL_SHIELD_DEADLY:
15102       RaiseScore(level.score[SC_SHIELD]);
15103       break;
15104     case EL_EXTRA_TIME:
15105       RaiseScore(level.extra_time_score);
15106       break;
15107     case EL_KEY_1:
15108     case EL_KEY_2:
15109     case EL_KEY_3:
15110     case EL_KEY_4:
15111     case EL_EM_KEY_1:
15112     case EL_EM_KEY_2:
15113     case EL_EM_KEY_3:
15114     case EL_EM_KEY_4:
15115     case EL_EMC_KEY_5:
15116     case EL_EMC_KEY_6:
15117     case EL_EMC_KEY_7:
15118     case EL_EMC_KEY_8:
15119     case EL_DC_KEY_WHITE:
15120       RaiseScore(level.score[SC_KEY]);
15121       break;
15122     default:
15123       RaiseScore(element_info[element].collect_score);
15124       break;
15125   }
15126 }
15127
15128 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15129 {
15130   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15131   {
15132 #if defined(NETWORK_AVALIABLE)
15133     if (options.network)
15134       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15135     else
15136 #endif
15137     {
15138       if (quick_quit)
15139       {
15140 #if 1
15141
15142 #if 1
15143         FadeSkipNextFadeIn();
15144 #else
15145         fading = fading_none;
15146 #endif
15147
15148 #else
15149         OpenDoor(DOOR_CLOSE_1);
15150 #endif
15151
15152         game_status = GAME_MODE_MAIN;
15153
15154 #if 1
15155         DrawAndFadeInMainMenu(REDRAW_FIELD);
15156 #else
15157         DrawMainMenu();
15158 #endif
15159       }
15160       else
15161       {
15162 #if 0
15163         FadeOut(REDRAW_FIELD);
15164 #endif
15165
15166         game_status = GAME_MODE_MAIN;
15167
15168         DrawAndFadeInMainMenu(REDRAW_FIELD);
15169       }
15170     }
15171   }
15172   else          /* continue playing the game */
15173   {
15174     if (tape.playing && tape.deactivate_display)
15175       TapeDeactivateDisplayOff(TRUE);
15176
15177     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15178
15179     if (tape.playing && tape.deactivate_display)
15180       TapeDeactivateDisplayOn();
15181   }
15182 }
15183
15184 void RequestQuitGame(boolean ask_if_really_quit)
15185 {
15186   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15187   boolean skip_request = AllPlayersGone || quick_quit;
15188
15189   RequestQuitGameExt(skip_request, quick_quit,
15190                      "Do you really want to quit the game ?");
15191 }
15192
15193
15194 /* ------------------------------------------------------------------------- */
15195 /* random generator functions                                                */
15196 /* ------------------------------------------------------------------------- */
15197
15198 unsigned int InitEngineRandom_RND(long seed)
15199 {
15200   game.num_random_calls = 0;
15201
15202 #if 0
15203   unsigned int rnd_seed = InitEngineRandom(seed);
15204
15205   printf("::: START RND: %d\n", rnd_seed);
15206
15207   return rnd_seed;
15208 #else
15209
15210   return InitEngineRandom(seed);
15211
15212 #endif
15213
15214 }
15215
15216 unsigned int RND(int max)
15217 {
15218   if (max > 0)
15219   {
15220     game.num_random_calls++;
15221
15222     return GetEngineRandom(max);
15223   }
15224
15225   return 0;
15226 }
15227
15228
15229 /* ------------------------------------------------------------------------- */
15230 /* game engine snapshot handling functions                                   */
15231 /* ------------------------------------------------------------------------- */
15232
15233 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15234
15235 struct EngineSnapshotInfo
15236 {
15237   /* runtime values for custom element collect score */
15238   int collect_score[NUM_CUSTOM_ELEMENTS];
15239
15240   /* runtime values for group element choice position */
15241   int choice_pos[NUM_GROUP_ELEMENTS];
15242
15243   /* runtime values for belt position animations */
15244   int belt_graphic[4 * NUM_BELT_PARTS];
15245   int belt_anim_mode[4 * NUM_BELT_PARTS];
15246 };
15247
15248 struct EngineSnapshotNodeInfo
15249 {
15250   void *buffer_orig;
15251   void *buffer_copy;
15252   int size;
15253 };
15254
15255 static struct EngineSnapshotInfo engine_snapshot_rnd;
15256 static ListNode *engine_snapshot_list = NULL;
15257 static char *snapshot_level_identifier = NULL;
15258 static int snapshot_level_nr = -1;
15259
15260 void FreeEngineSnapshot()
15261 {
15262   while (engine_snapshot_list != NULL)
15263     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15264                        checked_free);
15265
15266   setString(&snapshot_level_identifier, NULL);
15267   snapshot_level_nr = -1;
15268 }
15269
15270 static void SaveEngineSnapshotValues_RND()
15271 {
15272   static int belt_base_active_element[4] =
15273   {
15274     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15275     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15276     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15277     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15278   };
15279   int i, j;
15280
15281   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15282   {
15283     int element = EL_CUSTOM_START + i;
15284
15285     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15286   }
15287
15288   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15289   {
15290     int element = EL_GROUP_START + i;
15291
15292     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15293   }
15294
15295   for (i = 0; i < 4; i++)
15296   {
15297     for (j = 0; j < NUM_BELT_PARTS; j++)
15298     {
15299       int element = belt_base_active_element[i] + j;
15300       int graphic = el2img(element);
15301       int anim_mode = graphic_info[graphic].anim_mode;
15302
15303       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15304       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15305     }
15306   }
15307 }
15308
15309 static void LoadEngineSnapshotValues_RND()
15310 {
15311   unsigned long num_random_calls = game.num_random_calls;
15312   int i, j;
15313
15314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15315   {
15316     int element = EL_CUSTOM_START + i;
15317
15318     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15319   }
15320
15321   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15322   {
15323     int element = EL_GROUP_START + i;
15324
15325     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15326   }
15327
15328   for (i = 0; i < 4; i++)
15329   {
15330     for (j = 0; j < NUM_BELT_PARTS; j++)
15331     {
15332       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15333       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15334
15335       graphic_info[graphic].anim_mode = anim_mode;
15336     }
15337   }
15338
15339   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15340   {
15341     InitRND(tape.random_seed);
15342     for (i = 0; i < num_random_calls; i++)
15343       RND(1);
15344   }
15345
15346   if (game.num_random_calls != num_random_calls)
15347   {
15348     Error(ERR_INFO, "number of random calls out of sync");
15349     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15350     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15351     Error(ERR_EXIT, "this should not happen -- please debug");
15352   }
15353 }
15354
15355 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15356 {
15357   struct EngineSnapshotNodeInfo *bi =
15358     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15359
15360   bi->buffer_orig = buffer;
15361   bi->buffer_copy = checked_malloc(size);
15362   bi->size = size;
15363
15364   memcpy(bi->buffer_copy, buffer, size);
15365
15366   addNodeToList(&engine_snapshot_list, NULL, bi);
15367 }
15368
15369 void SaveEngineSnapshot()
15370 {
15371   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15372
15373   if (level_editor_test_game)   /* do not save snapshots from editor */
15374     return;
15375
15376   /* copy some special values to a structure better suited for the snapshot */
15377
15378   SaveEngineSnapshotValues_RND();
15379   SaveEngineSnapshotValues_EM();
15380
15381   /* save values stored in special snapshot structure */
15382
15383   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15385
15386   /* save further RND engine values */
15387
15388   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15389   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15390   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15391
15392   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15393   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15394   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15395   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15396
15397   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15398   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15399   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15400   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15401   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15402
15403   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15404   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15405   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15406
15407   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15408
15409   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15410
15411   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15412   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15413
15414   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15415   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15416   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15417   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15418   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15419   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15420   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15421   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15422   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15423   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15424   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15425   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15426   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15427   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15428   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15429   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15430   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15431   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15432
15433   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15434   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15435
15436   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15437   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15438   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15439
15440   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15441   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15442
15443   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15444   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15445   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15446   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15447   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15448
15449   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15450   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15451
15452   /* save level identification information */
15453
15454   setString(&snapshot_level_identifier, leveldir_current->identifier);
15455   snapshot_level_nr = level_nr;
15456
15457 #if 0
15458   ListNode *node = engine_snapshot_list;
15459   int num_bytes = 0;
15460
15461   while (node != NULL)
15462   {
15463     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15464
15465     node = node->next;
15466   }
15467
15468   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15469 #endif
15470 }
15471
15472 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15473 {
15474   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15475 }
15476
15477 void LoadEngineSnapshot()
15478 {
15479   ListNode *node = engine_snapshot_list;
15480
15481   if (engine_snapshot_list == NULL)
15482     return;
15483
15484   while (node != NULL)
15485   {
15486     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15487
15488     node = node->next;
15489   }
15490
15491   /* restore special values from snapshot structure */
15492
15493   LoadEngineSnapshotValues_RND();
15494   LoadEngineSnapshotValues_EM();
15495 }
15496
15497 boolean CheckEngineSnapshot()
15498 {
15499   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15500           snapshot_level_nr == level_nr);
15501 }
15502
15503
15504 /* ---------- new game button stuff ---------------------------------------- */
15505
15506 /* graphic position values for game buttons */
15507 #define GAME_BUTTON_XSIZE       30
15508 #define GAME_BUTTON_YSIZE       30
15509 #define GAME_BUTTON_XPOS        5
15510 #define GAME_BUTTON_YPOS        215
15511 #define SOUND_BUTTON_XPOS       5
15512 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15513
15514 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15515 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15516 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15517 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15518 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15519 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15520
15521 static struct
15522 {
15523   int *x, *y;
15524   int gd_x, gd_y;
15525   int gadget_id;
15526   char *infotext;
15527 } gamebutton_info[NUM_GAME_BUTTONS] =
15528 {
15529 #if 1
15530   {
15531     &game.button.stop.x,        &game.button.stop.y,
15532     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15533     GAME_CTRL_ID_STOP,
15534     "stop game"
15535   },
15536   {
15537     &game.button.pause.x,       &game.button.pause.y,
15538     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15539     GAME_CTRL_ID_PAUSE,
15540     "pause game"
15541   },
15542   {
15543     &game.button.play.x,        &game.button.play.y,
15544     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15545     GAME_CTRL_ID_PLAY,
15546     "play game"
15547   },
15548   {
15549     &game.button.sound_music.x, &game.button.sound_music.y,
15550     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15551     SOUND_CTRL_ID_MUSIC,
15552     "background music on/off"
15553   },
15554   {
15555     &game.button.sound_loops.x, &game.button.sound_loops.y,
15556     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15557     SOUND_CTRL_ID_LOOPS,
15558     "sound loops on/off"
15559   },
15560   {
15561     &game.button.sound_simple.x,&game.button.sound_simple.y,
15562     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15563     SOUND_CTRL_ID_SIMPLE,
15564     "normal sounds on/off"
15565   }
15566 #else
15567   {
15568     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15569     GAME_CTRL_ID_STOP,
15570     "stop game"
15571   },
15572   {
15573     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15574     GAME_CTRL_ID_PAUSE,
15575     "pause game"
15576   },
15577   {
15578     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15579     GAME_CTRL_ID_PLAY,
15580     "play game"
15581   },
15582   {
15583     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15584     SOUND_CTRL_ID_MUSIC,
15585     "background music on/off"
15586   },
15587   {
15588     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15589     SOUND_CTRL_ID_LOOPS,
15590     "sound loops on/off"
15591   },
15592   {
15593     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15594     SOUND_CTRL_ID_SIMPLE,
15595     "normal sounds on/off"
15596   }
15597 #endif
15598 };
15599
15600 void CreateGameButtons()
15601 {
15602   int i;
15603
15604   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15605   {
15606     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15607     struct GadgetInfo *gi;
15608     int button_type;
15609     boolean checked;
15610     unsigned long event_mask;
15611     int x, y;
15612     int gd_xoffset, gd_yoffset;
15613     int gd_x1, gd_x2, gd_y1, gd_y2;
15614     int id = i;
15615
15616     x = DX + *gamebutton_info[i].x;
15617     y = DY + *gamebutton_info[i].y;
15618     gd_xoffset = gamebutton_info[i].gd_x;
15619     gd_yoffset = gamebutton_info[i].gd_y;
15620     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15621     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15622
15623     if (id == GAME_CTRL_ID_STOP ||
15624         id == GAME_CTRL_ID_PAUSE ||
15625         id == GAME_CTRL_ID_PLAY)
15626     {
15627       button_type = GD_TYPE_NORMAL_BUTTON;
15628       checked = FALSE;
15629       event_mask = GD_EVENT_RELEASED;
15630       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15631       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15632     }
15633     else
15634     {
15635       button_type = GD_TYPE_CHECK_BUTTON;
15636       checked =
15637         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15638          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15639          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15640       event_mask = GD_EVENT_PRESSED;
15641       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15642       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15643     }
15644
15645     gi = CreateGadget(GDI_CUSTOM_ID, id,
15646                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15647 #if 1
15648                       GDI_X, x,
15649                       GDI_Y, y,
15650 #else
15651                       GDI_X, DX + gd_xoffset,
15652                       GDI_Y, DY + gd_yoffset,
15653 #endif
15654                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15655                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15656                       GDI_TYPE, button_type,
15657                       GDI_STATE, GD_BUTTON_UNPRESSED,
15658                       GDI_CHECKED, checked,
15659                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15660                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15661                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15662                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15663                       GDI_DIRECT_DRAW, FALSE,
15664                       GDI_EVENT_MASK, event_mask,
15665                       GDI_CALLBACK_ACTION, HandleGameButtons,
15666                       GDI_END);
15667
15668     if (gi == NULL)
15669       Error(ERR_EXIT, "cannot create gadget");
15670
15671     game_gadget[id] = gi;
15672   }
15673 }
15674
15675 void FreeGameButtons()
15676 {
15677   int i;
15678
15679   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15680     FreeGadget(game_gadget[i]);
15681 }
15682
15683 static void MapGameButtons()
15684 {
15685   int i;
15686
15687   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15688     MapGadget(game_gadget[i]);
15689 }
15690
15691 void UnmapGameButtons()
15692 {
15693   int i;
15694
15695   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15696     UnmapGadget(game_gadget[i]);
15697 }
15698
15699 void RedrawGameButtons()
15700 {
15701   int i;
15702
15703   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15704     RedrawGadget(game_gadget[i]);
15705 }
15706
15707 static void HandleGameButtons(struct GadgetInfo *gi)
15708 {
15709   int id = gi->custom_id;
15710
15711   if (game_status != GAME_MODE_PLAYING)
15712     return;
15713
15714   switch (id)
15715   {
15716     case GAME_CTRL_ID_STOP:
15717       if (tape.playing)
15718         TapeStop();
15719       else
15720         RequestQuitGame(TRUE);
15721       break;
15722
15723     case GAME_CTRL_ID_PAUSE:
15724       if (options.network)
15725       {
15726 #if defined(NETWORK_AVALIABLE)
15727         if (tape.pausing)
15728           SendToServer_ContinuePlaying();
15729         else
15730           SendToServer_PausePlaying();
15731 #endif
15732       }
15733       else
15734         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15735       break;
15736
15737     case GAME_CTRL_ID_PLAY:
15738       if (tape.pausing)
15739       {
15740 #if defined(NETWORK_AVALIABLE)
15741         if (options.network)
15742           SendToServer_ContinuePlaying();
15743         else
15744 #endif
15745         {
15746           tape.pausing = FALSE;
15747           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15748         }
15749       }
15750       break;
15751
15752     case SOUND_CTRL_ID_MUSIC:
15753       if (setup.sound_music)
15754       { 
15755         setup.sound_music = FALSE;
15756         FadeMusic();
15757       }
15758       else if (audio.music_available)
15759       { 
15760         setup.sound = setup.sound_music = TRUE;
15761
15762         SetAudioMode(setup.sound);
15763
15764         PlayLevelMusic();
15765       }
15766       break;
15767
15768     case SOUND_CTRL_ID_LOOPS:
15769       if (setup.sound_loops)
15770         setup.sound_loops = FALSE;
15771       else if (audio.loops_available)
15772       {
15773         setup.sound = setup.sound_loops = TRUE;
15774         SetAudioMode(setup.sound);
15775       }
15776       break;
15777
15778     case SOUND_CTRL_ID_SIMPLE:
15779       if (setup.sound_simple)
15780         setup.sound_simple = FALSE;
15781       else if (audio.sound_available)
15782       {
15783         setup.sound = setup.sound_simple = TRUE;
15784         SetAudioMode(setup.sound);
15785       }
15786       break;
15787
15788     default:
15789       break;
15790   }
15791 }