updated contact info in source file headers
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "files.h"
19 #include "tape.h"
20 #include "network.h"
21
22
23 /* DEBUG SETTINGS */
24 #define DEBUG_INIT_PLAYER       1
25 #define DEBUG_PLAYER_ACTIONS    0
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF                   (                         1)
32
33 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
34 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
36 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
37 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
38 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
39 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
40 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
41 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
42 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
43 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
44 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
45 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
46 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
47 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
48 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
49 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
50 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
51 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
52
53 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
54
55 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
56
57 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
58
59 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
60 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
61
62 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
63 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
64 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
65 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
66
67 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
68
69 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
70
71 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
72
73 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
74
75 #if USE_DELAYED_GFX_REDRAW
76 #define TEST_DrawLevelField(x, y)                               \
77         GfxRedraw[x][y] |= GFX_REDRAW_TILE
78 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
79         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
80 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
81         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
82 #define TEST_DrawTwinkleOnField(x, y)                           \
83         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
84 #else
85 #define TEST_DrawLevelField(x, y)                               \
86              DrawLevelField(x, y)
87 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
88              DrawLevelFieldCrumbled(x, y)
89 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
90              DrawLevelFieldCrumbledNeighbours(x, y)
91 #define TEST_DrawTwinkleOnField(x, y)                           \
92              DrawTwinkleOnField(x, y)
93 #endif
94
95
96 /* for DigField() */
97 #define DF_NO_PUSH              0
98 #define DF_DIG                  1
99 #define DF_SNAP                 2
100
101 /* for MovePlayer() */
102 #define MP_NO_ACTION            0
103 #define MP_MOVING               1
104 #define MP_ACTION               2
105 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
106
107 /* for ScrollPlayer() */
108 #define SCROLL_INIT             0
109 #define SCROLL_GO_ON            1
110
111 /* for Bang()/Explode() */
112 #define EX_PHASE_START          0
113 #define EX_TYPE_NONE            0
114 #define EX_TYPE_NORMAL          (1 << 0)
115 #define EX_TYPE_CENTER          (1 << 1)
116 #define EX_TYPE_BORDER          (1 << 2)
117 #define EX_TYPE_CROSS           (1 << 3)
118 #define EX_TYPE_DYNA            (1 << 4)
119 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
120
121 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
122 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
123 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
124 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
125
126 /* game panel display and control definitions */
127 #define GAME_PANEL_LEVEL_NUMBER                 0
128 #define GAME_PANEL_GEMS                         1
129 #define GAME_PANEL_INVENTORY_COUNT              2
130 #define GAME_PANEL_INVENTORY_FIRST_1            3
131 #define GAME_PANEL_INVENTORY_FIRST_2            4
132 #define GAME_PANEL_INVENTORY_FIRST_3            5
133 #define GAME_PANEL_INVENTORY_FIRST_4            6
134 #define GAME_PANEL_INVENTORY_FIRST_5            7
135 #define GAME_PANEL_INVENTORY_FIRST_6            8
136 #define GAME_PANEL_INVENTORY_FIRST_7            9
137 #define GAME_PANEL_INVENTORY_FIRST_8            10
138 #define GAME_PANEL_INVENTORY_LAST_1             11
139 #define GAME_PANEL_INVENTORY_LAST_2             12
140 #define GAME_PANEL_INVENTORY_LAST_3             13
141 #define GAME_PANEL_INVENTORY_LAST_4             14
142 #define GAME_PANEL_INVENTORY_LAST_5             15
143 #define GAME_PANEL_INVENTORY_LAST_6             16
144 #define GAME_PANEL_INVENTORY_LAST_7             17
145 #define GAME_PANEL_INVENTORY_LAST_8             18
146 #define GAME_PANEL_KEY_1                        19
147 #define GAME_PANEL_KEY_2                        20
148 #define GAME_PANEL_KEY_3                        21
149 #define GAME_PANEL_KEY_4                        22
150 #define GAME_PANEL_KEY_5                        23
151 #define GAME_PANEL_KEY_6                        24
152 #define GAME_PANEL_KEY_7                        25
153 #define GAME_PANEL_KEY_8                        26
154 #define GAME_PANEL_KEY_WHITE                    27
155 #define GAME_PANEL_KEY_WHITE_COUNT              28
156 #define GAME_PANEL_SCORE                        29
157 #define GAME_PANEL_HIGHSCORE                    30
158 #define GAME_PANEL_TIME                         31
159 #define GAME_PANEL_TIME_HH                      32
160 #define GAME_PANEL_TIME_MM                      33
161 #define GAME_PANEL_TIME_SS                      34
162 #define GAME_PANEL_FRAME                        35
163 #define GAME_PANEL_SHIELD_NORMAL                36
164 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
165 #define GAME_PANEL_SHIELD_DEADLY                38
166 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
167 #define GAME_PANEL_EXIT                         40
168 #define GAME_PANEL_EMC_MAGIC_BALL               41
169 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
170 #define GAME_PANEL_LIGHT_SWITCH                 43
171 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
172 #define GAME_PANEL_TIMEGATE_SWITCH              45
173 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
174 #define GAME_PANEL_SWITCHGATE_SWITCH            47
175 #define GAME_PANEL_EMC_LENSES                   48
176 #define GAME_PANEL_EMC_LENSES_TIME              49
177 #define GAME_PANEL_EMC_MAGNIFIER                50
178 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
179 #define GAME_PANEL_BALLOON_SWITCH               52
180 #define GAME_PANEL_DYNABOMB_NUMBER              53
181 #define GAME_PANEL_DYNABOMB_SIZE                54
182 #define GAME_PANEL_DYNABOMB_POWER               55
183 #define GAME_PANEL_PENGUINS                     56
184 #define GAME_PANEL_SOKOBAN_OBJECTS              57
185 #define GAME_PANEL_SOKOBAN_FIELDS               58
186 #define GAME_PANEL_ROBOT_WHEEL                  59
187 #define GAME_PANEL_CONVEYOR_BELT_1              60
188 #define GAME_PANEL_CONVEYOR_BELT_2              61
189 #define GAME_PANEL_CONVEYOR_BELT_3              62
190 #define GAME_PANEL_CONVEYOR_BELT_4              63
191 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
192 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
193 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
194 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
195 #define GAME_PANEL_MAGIC_WALL                   68
196 #define GAME_PANEL_MAGIC_WALL_TIME              69
197 #define GAME_PANEL_GRAVITY_STATE                70
198 #define GAME_PANEL_GRAPHIC_1                    71
199 #define GAME_PANEL_GRAPHIC_2                    72
200 #define GAME_PANEL_GRAPHIC_3                    73
201 #define GAME_PANEL_GRAPHIC_4                    74
202 #define GAME_PANEL_GRAPHIC_5                    75
203 #define GAME_PANEL_GRAPHIC_6                    76
204 #define GAME_PANEL_GRAPHIC_7                    77
205 #define GAME_PANEL_GRAPHIC_8                    78
206 #define GAME_PANEL_ELEMENT_1                    79
207 #define GAME_PANEL_ELEMENT_2                    80
208 #define GAME_PANEL_ELEMENT_3                    81
209 #define GAME_PANEL_ELEMENT_4                    82
210 #define GAME_PANEL_ELEMENT_5                    83
211 #define GAME_PANEL_ELEMENT_6                    84
212 #define GAME_PANEL_ELEMENT_7                    85
213 #define GAME_PANEL_ELEMENT_8                    86
214 #define GAME_PANEL_ELEMENT_COUNT_1              87
215 #define GAME_PANEL_ELEMENT_COUNT_2              88
216 #define GAME_PANEL_ELEMENT_COUNT_3              89
217 #define GAME_PANEL_ELEMENT_COUNT_4              90
218 #define GAME_PANEL_ELEMENT_COUNT_5              91
219 #define GAME_PANEL_ELEMENT_COUNT_6              92
220 #define GAME_PANEL_ELEMENT_COUNT_7              93
221 #define GAME_PANEL_ELEMENT_COUNT_8              94
222 #define GAME_PANEL_CE_SCORE_1                   95
223 #define GAME_PANEL_CE_SCORE_2                   96
224 #define GAME_PANEL_CE_SCORE_3                   97
225 #define GAME_PANEL_CE_SCORE_4                   98
226 #define GAME_PANEL_CE_SCORE_5                   99
227 #define GAME_PANEL_CE_SCORE_6                   100
228 #define GAME_PANEL_CE_SCORE_7                   101
229 #define GAME_PANEL_CE_SCORE_8                   102
230 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
231 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
232 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
233 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
234 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
235 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
236 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
237 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
238 #define GAME_PANEL_PLAYER_NAME                  111
239 #define GAME_PANEL_LEVEL_NAME                   112
240 #define GAME_PANEL_LEVEL_AUTHOR                 113
241
242 #define NUM_GAME_PANEL_CONTROLS                 114
243
244 struct GamePanelOrderInfo
245 {
246   int nr;
247   int sort_priority;
248 };
249
250 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
251
252 struct GamePanelControlInfo
253 {
254   int nr;
255
256   struct TextPosInfo *pos;
257   int type;
258
259   int value, last_value;
260   int frame, last_frame;
261   int gfx_frame;
262   int gfx_random;
263 };
264
265 static struct GamePanelControlInfo game_panel_controls[] =
266 {
267   {
268     GAME_PANEL_LEVEL_NUMBER,
269     &game.panel.level_number,
270     TYPE_INTEGER,
271   },
272   {
273     GAME_PANEL_GEMS,
274     &game.panel.gems,
275     TYPE_INTEGER,
276   },
277   {
278     GAME_PANEL_INVENTORY_COUNT,
279     &game.panel.inventory_count,
280     TYPE_INTEGER,
281   },
282   {
283     GAME_PANEL_INVENTORY_FIRST_1,
284     &game.panel.inventory_first[0],
285     TYPE_ELEMENT,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_2,
289     &game.panel.inventory_first[1],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_3,
294     &game.panel.inventory_first[2],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_4,
299     &game.panel.inventory_first[3],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_FIRST_5,
304     &game.panel.inventory_first[4],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_FIRST_6,
309     &game.panel.inventory_first[5],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_FIRST_7,
314     &game.panel.inventory_first[6],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_8,
319     &game.panel.inventory_first[7],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_LAST_1,
324     &game.panel.inventory_last[0],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_2,
329     &game.panel.inventory_last[1],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_3,
334     &game.panel.inventory_last[2],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_4,
339     &game.panel.inventory_last[3],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_LAST_5,
344     &game.panel.inventory_last[4],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_LAST_6,
349     &game.panel.inventory_last[5],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_LAST_7,
354     &game.panel.inventory_last[6],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_8,
359     &game.panel.inventory_last[7],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_KEY_1,
364     &game.panel.key[0],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_2,
369     &game.panel.key[1],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_3,
374     &game.panel.key[2],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_4,
379     &game.panel.key[3],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_5,
384     &game.panel.key[4],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_6,
389     &game.panel.key[5],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_KEY_7,
394     &game.panel.key[6],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_8,
399     &game.panel.key[7],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_WHITE,
404     &game.panel.key_white,
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_WHITE_COUNT,
409     &game.panel.key_white_count,
410     TYPE_INTEGER,
411   },
412   {
413     GAME_PANEL_SCORE,
414     &game.panel.score,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_HIGHSCORE,
419     &game.panel.highscore,
420     TYPE_INTEGER,
421   },
422   {
423     GAME_PANEL_TIME,
424     &game.panel.time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_TIME_HH,
429     &game.panel.time_hh,
430     TYPE_INTEGER,
431   },
432   {
433     GAME_PANEL_TIME_MM,
434     &game.panel.time_mm,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_TIME_SS,
439     &game.panel.time_ss,
440     TYPE_INTEGER,
441   },
442   {
443     GAME_PANEL_FRAME,
444     &game.panel.frame,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SHIELD_NORMAL,
449     &game.panel.shield_normal,
450     TYPE_ELEMENT,
451   },
452   {
453     GAME_PANEL_SHIELD_NORMAL_TIME,
454     &game.panel.shield_normal_time,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_SHIELD_DEADLY,
459     &game.panel.shield_deadly,
460     TYPE_ELEMENT,
461   },
462   {
463     GAME_PANEL_SHIELD_DEADLY_TIME,
464     &game.panel.shield_deadly_time,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_EXIT,
469     &game.panel.exit,
470     TYPE_ELEMENT,
471   },
472   {
473     GAME_PANEL_EMC_MAGIC_BALL,
474     &game.panel.emc_magic_ball,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
479     &game.panel.emc_magic_ball_switch,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_LIGHT_SWITCH,
484     &game.panel.light_switch,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_LIGHT_SWITCH_TIME,
489     &game.panel.light_switch_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_TIMEGATE_SWITCH,
494     &game.panel.timegate_switch,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_TIMEGATE_SWITCH_TIME,
499     &game.panel.timegate_switch_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_SWITCHGATE_SWITCH,
504     &game.panel.switchgate_switch,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_LENSES,
509     &game.panel.emc_lenses,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_LENSES_TIME,
514     &game.panel.emc_lenses_time,
515     TYPE_INTEGER,
516   },
517   {
518     GAME_PANEL_EMC_MAGNIFIER,
519     &game.panel.emc_magnifier,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_EMC_MAGNIFIER_TIME,
524     &game.panel.emc_magnifier_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_BALLOON_SWITCH,
529     &game.panel.balloon_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_DYNABOMB_NUMBER,
534     &game.panel.dynabomb_number,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_DYNABOMB_SIZE,
539     &game.panel.dynabomb_size,
540     TYPE_INTEGER,
541   },
542   {
543     GAME_PANEL_DYNABOMB_POWER,
544     &game.panel.dynabomb_power,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_PENGUINS,
549     &game.panel.penguins,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_SOKOBAN_OBJECTS,
554     &game.panel.sokoban_objects,
555     TYPE_INTEGER,
556   },
557   {
558     GAME_PANEL_SOKOBAN_FIELDS,
559     &game.panel.sokoban_fields,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_ROBOT_WHEEL,
564     &game.panel.robot_wheel,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_1,
569     &game.panel.conveyor_belt[0],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_2,
574     &game.panel.conveyor_belt[1],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_CONVEYOR_BELT_3,
579     &game.panel.conveyor_belt[2],
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_CONVEYOR_BELT_4,
584     &game.panel.conveyor_belt[3],
585     TYPE_ELEMENT,
586   },
587   {
588     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
589     &game.panel.conveyor_belt_switch[0],
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
594     &game.panel.conveyor_belt_switch[1],
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
599     &game.panel.conveyor_belt_switch[2],
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
604     &game.panel.conveyor_belt_switch[3],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_MAGIC_WALL,
609     &game.panel.magic_wall,
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_MAGIC_WALL_TIME,
614     &game.panel.magic_wall_time,
615     TYPE_INTEGER,
616   },
617   {
618     GAME_PANEL_GRAVITY_STATE,
619     &game.panel.gravity_state,
620     TYPE_STRING,
621   },
622   {
623     GAME_PANEL_GRAPHIC_1,
624     &game.panel.graphic[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_GRAPHIC_2,
629     &game.panel.graphic[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_GRAPHIC_3,
634     &game.panel.graphic[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_GRAPHIC_4,
639     &game.panel.graphic[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_GRAPHIC_5,
644     &game.panel.graphic[4],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_GRAPHIC_6,
649     &game.panel.graphic[5],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_GRAPHIC_7,
654     &game.panel.graphic[6],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_GRAPHIC_8,
659     &game.panel.graphic[7],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_ELEMENT_1,
664     &game.panel.element[0],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_ELEMENT_2,
669     &game.panel.element[1],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_ELEMENT_3,
674     &game.panel.element[2],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_ELEMENT_4,
679     &game.panel.element[3],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_ELEMENT_5,
684     &game.panel.element[4],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_ELEMENT_6,
689     &game.panel.element[5],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_ELEMENT_7,
694     &game.panel.element[6],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_8,
699     &game.panel.element[7],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_COUNT_1,
704     &game.panel.element_count[0],
705     TYPE_INTEGER,
706   },
707   {
708     GAME_PANEL_ELEMENT_COUNT_2,
709     &game.panel.element_count[1],
710     TYPE_INTEGER,
711   },
712   {
713     GAME_PANEL_ELEMENT_COUNT_3,
714     &game.panel.element_count[2],
715     TYPE_INTEGER,
716   },
717   {
718     GAME_PANEL_ELEMENT_COUNT_4,
719     &game.panel.element_count[3],
720     TYPE_INTEGER,
721   },
722   {
723     GAME_PANEL_ELEMENT_COUNT_5,
724     &game.panel.element_count[4],
725     TYPE_INTEGER,
726   },
727   {
728     GAME_PANEL_ELEMENT_COUNT_6,
729     &game.panel.element_count[5],
730     TYPE_INTEGER,
731   },
732   {
733     GAME_PANEL_ELEMENT_COUNT_7,
734     &game.panel.element_count[6],
735     TYPE_INTEGER,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_8,
739     &game.panel.element_count[7],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_CE_SCORE_1,
744     &game.panel.ce_score[0],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_CE_SCORE_2,
749     &game.panel.ce_score[1],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_CE_SCORE_3,
754     &game.panel.ce_score[2],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_CE_SCORE_4,
759     &game.panel.ce_score[3],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_CE_SCORE_5,
764     &game.panel.ce_score[4],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_CE_SCORE_6,
769     &game.panel.ce_score[5],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_CE_SCORE_7,
774     &game.panel.ce_score[6],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_8,
779     &game.panel.ce_score[7],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_1_ELEMENT,
784     &game.panel.ce_score_element[0],
785     TYPE_ELEMENT,
786   },
787   {
788     GAME_PANEL_CE_SCORE_2_ELEMENT,
789     &game.panel.ce_score_element[1],
790     TYPE_ELEMENT,
791   },
792   {
793     GAME_PANEL_CE_SCORE_3_ELEMENT,
794     &game.panel.ce_score_element[2],
795     TYPE_ELEMENT,
796   },
797   {
798     GAME_PANEL_CE_SCORE_4_ELEMENT,
799     &game.panel.ce_score_element[3],
800     TYPE_ELEMENT,
801   },
802   {
803     GAME_PANEL_CE_SCORE_5_ELEMENT,
804     &game.panel.ce_score_element[4],
805     TYPE_ELEMENT,
806   },
807   {
808     GAME_PANEL_CE_SCORE_6_ELEMENT,
809     &game.panel.ce_score_element[5],
810     TYPE_ELEMENT,
811   },
812   {
813     GAME_PANEL_CE_SCORE_7_ELEMENT,
814     &game.panel.ce_score_element[6],
815     TYPE_ELEMENT,
816   },
817   {
818     GAME_PANEL_CE_SCORE_8_ELEMENT,
819     &game.panel.ce_score_element[7],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_PLAYER_NAME,
824     &game.panel.player_name,
825     TYPE_STRING,
826   },
827   {
828     GAME_PANEL_LEVEL_NAME,
829     &game.panel.level_name,
830     TYPE_STRING,
831   },
832   {
833     GAME_PANEL_LEVEL_AUTHOR,
834     &game.panel.level_author,
835     TYPE_STRING,
836   },
837
838   {
839     -1,
840     NULL,
841     -1,
842   }
843 };
844
845 /* values for delayed check of falling and moving elements and for collision */
846 #define CHECK_DELAY_MOVING      3
847 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
848 #define CHECK_DELAY_COLLISION   2
849 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
850
851 /* values for initial player move delay (initial delay counter value) */
852 #define INITIAL_MOVE_DELAY_OFF  -1
853 #define INITIAL_MOVE_DELAY_ON   0
854
855 /* values for player movement speed (which is in fact a delay value) */
856 #define MOVE_DELAY_MIN_SPEED    32
857 #define MOVE_DELAY_NORMAL_SPEED 8
858 #define MOVE_DELAY_HIGH_SPEED   4
859 #define MOVE_DELAY_MAX_SPEED    1
860
861 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
862 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
863
864 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
865 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
866
867 /* values for other actions */
868 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
869 #define MOVE_STEPSIZE_MIN       (1)
870 #define MOVE_STEPSIZE_MAX       (TILEX)
871
872 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
873 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
874
875 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
876
877 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
878                                  RND(element_info[e].push_delay_random))
879 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
880                                  RND(element_info[e].drop_delay_random))
881 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
882                                  RND(element_info[e].move_delay_random))
883 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
884                                     (element_info[e].move_delay_random))
885 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
886                                  RND(element_info[e].ce_value_random_initial))
887 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
888 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
889                                  RND((c)->delay_random * (c)->delay_frames))
890 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
891                                  RND((c)->delay_random))
892
893
894 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
895          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
896
897 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
898         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
899          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
900          (be) + (e) - EL_SELF)
901
902 #define GET_PLAYER_FROM_BITS(p)                                         \
903         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
904
905 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
906         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
907          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
908          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
909          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
910          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
911          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
912          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
913          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
914          (e))
915
916 #define CAN_GROW_INTO(e)                                                \
917         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
918
919 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
920                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
921                                         (condition)))
922
923 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
924                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
925                                         (CAN_MOVE_INTO_ACID(e) &&       \
926                                          Feld[x][y] == EL_ACID) ||      \
927                                         (condition)))
928
929 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
930                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
931                                         (CAN_MOVE_INTO_ACID(e) &&       \
932                                          Feld[x][y] == EL_ACID) ||      \
933                                         (condition)))
934
935 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
936                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
937                                         (condition) ||                  \
938                                         (CAN_MOVE_INTO_ACID(e) &&       \
939                                          Feld[x][y] == EL_ACID) ||      \
940                                         (DONT_COLLIDE_WITH(e) &&        \
941                                          IS_PLAYER(x, y) &&             \
942                                          !PLAYER_ENEMY_PROTECTED(x, y))))
943
944 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
945         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
946
947 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
949
950 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
951         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
952
953 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
954         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
955                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
956
957 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
958         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
959
960 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
962
963 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
965
966 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
968
969 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
971
972 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
974                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
975                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
976                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
977                                                  IS_FOOD_PENGUIN(Feld[x][y])))
978 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
980
981 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
983
984 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
986
987 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
988         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
989                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
990
991 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
992
993 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
994                 (!IS_PLAYER(x, y) &&                                    \
995                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
996
997 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
998         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
999
1000 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1001 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1002
1003 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1004 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1005 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1007
1008 /* game button identifiers */
1009 #define GAME_CTRL_ID_STOP               0
1010 #define GAME_CTRL_ID_PAUSE              1
1011 #define GAME_CTRL_ID_PLAY               2
1012 #define SOUND_CTRL_ID_MUSIC             3
1013 #define SOUND_CTRL_ID_LOOPS             4
1014 #define SOUND_CTRL_ID_SIMPLE            5
1015 #define GAME_CTRL_ID_SAVE               6
1016 #define GAME_CTRL_ID_LOAD               7
1017
1018 #define NUM_GAME_BUTTONS                8
1019
1020
1021 /* forward declaration for internal use */
1022
1023 static void CreateField(int, int, int);
1024
1025 static void ResetGfxAnimation(int, int);
1026
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1029
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1034
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1039
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1046
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1050 #if 0
1051 static void TestIfElementSmashesCustomElement(int, int, int);
1052 #endif
1053
1054 static void HandleElementChange(int, int, int);
1055 static void ExecuteCustomElementAction(int, int, int, int);
1056 static boolean ChangeElement(int, int, int, int);
1057
1058 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1059 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1060         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1061 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1063 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1065 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1066         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1067
1068 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1069 #define CheckElementChange(x, y, e, te, ev)                             \
1070         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1071 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1072         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1073 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1074         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1075
1076 static void PlayLevelSound(int, int, int);
1077 static void PlayLevelSoundNearest(int, int, int);
1078 static void PlayLevelSoundAction(int, int, int);
1079 static void PlayLevelSoundElementAction(int, int, int, int);
1080 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1081 static void PlayLevelSoundActionIfLoop(int, int, int);
1082 static void StopLevelSoundActionIfLoop(int, int, int);
1083 static void PlayLevelMusic();
1084
1085 static void HandleGameButtons(struct GadgetInfo *);
1086
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1094
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1105
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1109
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1112
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1114
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1118
1119 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1120 {                                                                       \
1121   if (recursion_loop_detected)                                          \
1122     return (rc);                                                        \
1123                                                                         \
1124   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1125   {                                                                     \
1126     recursion_loop_detected = TRUE;                                     \
1127     recursion_loop_element = (e);                                       \
1128   }                                                                     \
1129                                                                         \
1130   recursion_loop_depth++;                                               \
1131 }
1132
1133 #define RECURSION_LOOP_DETECTION_END()                                  \
1134 {                                                                       \
1135   recursion_loop_depth--;                                               \
1136 }
1137
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1141
1142 static int map_player_action[MAX_PLAYERS];
1143
1144
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after  */
1147 /* a specified time, eventually calling a function when changing             */
1148 /* ------------------------------------------------------------------------- */
1149
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1153
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1157
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1161
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1164
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1167
1168 struct ChangingElementInfo
1169 {
1170   int element;
1171   int target_element;
1172   int change_delay;
1173   void (*pre_change_function)(int x, int y);
1174   void (*change_function)(int x, int y);
1175   void (*post_change_function)(int x, int y);
1176 };
1177
1178 static struct ChangingElementInfo change_delay_list[] =
1179 {
1180   {
1181     EL_NUT_BREAKING,
1182     EL_EMERALD,
1183     6,
1184     NULL,
1185     NULL,
1186     NULL
1187   },
1188   {
1189     EL_PEARL_BREAKING,
1190     EL_EMPTY,
1191     8,
1192     NULL,
1193     NULL,
1194     NULL
1195   },
1196   {
1197     EL_EXIT_OPENING,
1198     EL_EXIT_OPEN,
1199     29,
1200     NULL,
1201     NULL,
1202     NULL
1203   },
1204   {
1205     EL_EXIT_CLOSING,
1206     EL_EXIT_CLOSED,
1207     29,
1208     NULL,
1209     NULL,
1210     NULL
1211   },
1212   {
1213     EL_STEEL_EXIT_OPENING,
1214     EL_STEEL_EXIT_OPEN,
1215     29,
1216     NULL,
1217     NULL,
1218     NULL
1219   },
1220   {
1221     EL_STEEL_EXIT_CLOSING,
1222     EL_STEEL_EXIT_CLOSED,
1223     29,
1224     NULL,
1225     NULL,
1226     NULL
1227   },
1228   {
1229     EL_EM_EXIT_OPENING,
1230     EL_EM_EXIT_OPEN,
1231     29,
1232     NULL,
1233     NULL,
1234     NULL
1235   },
1236   {
1237     EL_EM_EXIT_CLOSING,
1238 #if 1
1239     EL_EMPTY,
1240 #else
1241     EL_EM_EXIT_CLOSED,
1242 #endif
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_EM_STEEL_EXIT_OPENING,
1250     EL_EM_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_EM_STEEL_EXIT_CLOSING,
1258 #if 1
1259     EL_STEELWALL,
1260 #else
1261     EL_EM_STEEL_EXIT_CLOSED,
1262 #endif
1263     29,
1264     NULL,
1265     NULL,
1266     NULL
1267   },
1268   {
1269     EL_SP_EXIT_OPENING,
1270     EL_SP_EXIT_OPEN,
1271     29,
1272     NULL,
1273     NULL,
1274     NULL
1275   },
1276   {
1277     EL_SP_EXIT_CLOSING,
1278     EL_SP_EXIT_CLOSED,
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_SWITCHGATE_OPENING,
1286     EL_SWITCHGATE_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_SWITCHGATE_CLOSING,
1294     EL_SWITCHGATE_CLOSED,
1295     29,
1296     NULL,
1297     NULL,
1298     NULL
1299   },
1300   {
1301     EL_TIMEGATE_OPENING,
1302     EL_TIMEGATE_OPEN,
1303     29,
1304     NULL,
1305     NULL,
1306     NULL
1307   },
1308   {
1309     EL_TIMEGATE_CLOSING,
1310     EL_TIMEGATE_CLOSED,
1311     29,
1312     NULL,
1313     NULL,
1314     NULL
1315   },
1316
1317   {
1318     EL_ACID_SPLASH_LEFT,
1319     EL_EMPTY,
1320     8,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_ACID_SPLASH_RIGHT,
1327     EL_EMPTY,
1328     8,
1329     NULL,
1330     NULL,
1331     NULL
1332   },
1333   {
1334     EL_SP_BUGGY_BASE,
1335     EL_SP_BUGGY_BASE_ACTIVATING,
1336     0,
1337     InitBuggyBase,
1338     NULL,
1339     NULL
1340   },
1341   {
1342     EL_SP_BUGGY_BASE_ACTIVATING,
1343     EL_SP_BUGGY_BASE_ACTIVE,
1344     0,
1345     InitBuggyBase,
1346     NULL,
1347     NULL
1348   },
1349   {
1350     EL_SP_BUGGY_BASE_ACTIVE,
1351     EL_SP_BUGGY_BASE,
1352     0,
1353     InitBuggyBase,
1354     WarnBuggyBase,
1355     NULL
1356   },
1357   {
1358     EL_TRAP,
1359     EL_TRAP_ACTIVE,
1360     0,
1361     InitTrap,
1362     NULL,
1363     ActivateTrap
1364   },
1365   {
1366     EL_TRAP_ACTIVE,
1367     EL_TRAP,
1368     31,
1369     NULL,
1370     ChangeActiveTrap,
1371     NULL
1372   },
1373   {
1374     EL_ROBOT_WHEEL_ACTIVE,
1375     EL_ROBOT_WHEEL,
1376     0,
1377     InitRobotWheel,
1378     RunRobotWheel,
1379     StopRobotWheel
1380   },
1381   {
1382     EL_TIMEGATE_SWITCH_ACTIVE,
1383     EL_TIMEGATE_SWITCH,
1384     0,
1385     InitTimegateWheel,
1386     RunTimegateWheel,
1387     NULL
1388   },
1389   {
1390     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1391     EL_DC_TIMEGATE_SWITCH,
1392     0,
1393     InitTimegateWheel,
1394     RunTimegateWheel,
1395     NULL
1396   },
1397   {
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     0,
1401     InitMagicBallDelay,
1402     NULL,
1403     ActivateMagicBall
1404   },
1405   {
1406     EL_EMC_SPRING_BUMPER_ACTIVE,
1407     EL_EMC_SPRING_BUMPER,
1408     8,
1409     NULL,
1410     NULL,
1411     NULL
1412   },
1413   {
1414     EL_DIAGONAL_SHRINKING,
1415     EL_UNDEFINED,
1416     0,
1417     NULL,
1418     NULL,
1419     NULL
1420   },
1421   {
1422     EL_DIAGONAL_GROWING,
1423     EL_UNDEFINED,
1424     0,
1425     NULL,
1426     NULL,
1427     NULL,
1428   },
1429
1430   {
1431     EL_UNDEFINED,
1432     EL_UNDEFINED,
1433     -1,
1434     NULL,
1435     NULL,
1436     NULL
1437   }
1438 };
1439
1440 struct
1441 {
1442   int element;
1443   int push_delay_fixed, push_delay_random;
1444 }
1445 push_delay_list[] =
1446 {
1447   { EL_SPRING,                  0, 0 },
1448   { EL_BALLOON,                 0, 0 },
1449
1450   { EL_SOKOBAN_OBJECT,          2, 0 },
1451   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1452   { EL_SATELLITE,               2, 0 },
1453   { EL_SP_DISK_YELLOW,          2, 0 },
1454
1455   { EL_UNDEFINED,               0, 0 },
1456 };
1457
1458 struct
1459 {
1460   int element;
1461   int move_stepsize;
1462 }
1463 move_stepsize_list[] =
1464 {
1465   { EL_AMOEBA_DROP,             2 },
1466   { EL_AMOEBA_DROPPING,         2 },
1467   { EL_QUICKSAND_FILLING,       1 },
1468   { EL_QUICKSAND_EMPTYING,      1 },
1469   { EL_QUICKSAND_FAST_FILLING,  2 },
1470   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1471   { EL_MAGIC_WALL_FILLING,      2 },
1472   { EL_MAGIC_WALL_EMPTYING,     2 },
1473   { EL_BD_MAGIC_WALL_FILLING,   2 },
1474   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1475   { EL_DC_MAGIC_WALL_FILLING,   2 },
1476   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1477
1478   { EL_UNDEFINED,               0 },
1479 };
1480
1481 struct
1482 {
1483   int element;
1484   int count;
1485 }
1486 collect_count_list[] =
1487 {
1488   { EL_EMERALD,                 1 },
1489   { EL_BD_DIAMOND,              1 },
1490   { EL_EMERALD_YELLOW,          1 },
1491   { EL_EMERALD_RED,             1 },
1492   { EL_EMERALD_PURPLE,          1 },
1493   { EL_DIAMOND,                 3 },
1494   { EL_SP_INFOTRON,             1 },
1495   { EL_PEARL,                   5 },
1496   { EL_CRYSTAL,                 8 },
1497
1498   { EL_UNDEFINED,               0 },
1499 };
1500
1501 struct
1502 {
1503   int element;
1504   int direction;
1505 }
1506 access_direction_list[] =
1507 {
1508   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1509   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1510   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1511   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1514   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1515   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1516   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1517   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1518   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1519
1520   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1521   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1522   { EL_SP_PORT_UP,                                                   MV_DOWN },
1523   { EL_SP_PORT_DOWN,                                         MV_UP           },
1524   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1525   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1526   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1528   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1529   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1531   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1532   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1533   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1534   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1535   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1536   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1537   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1538   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1539
1540   { EL_UNDEFINED,                       MV_NONE                              }
1541 };
1542
1543 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1544
1545 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1546 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1547 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1548                                  IS_JUST_CHANGING(x, y))
1549
1550 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1551
1552 /* static variables for playfield scan mode (scanning forward or backward) */
1553 static int playfield_scan_start_x = 0;
1554 static int playfield_scan_start_y = 0;
1555 static int playfield_scan_delta_x = 1;
1556 static int playfield_scan_delta_y = 1;
1557
1558 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1559                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1560                                      (y) += playfield_scan_delta_y)     \
1561                                 for ((x) = playfield_scan_start_x;      \
1562                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1563                                      (x) += playfield_scan_delta_x)
1564
1565 #ifdef DEBUG
1566 void DEBUG_SetMaximumDynamite()
1567 {
1568   int i;
1569
1570   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1571     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1572       local_player->inventory_element[local_player->inventory_size++] =
1573         EL_DYNAMITE;
1574 }
1575 #endif
1576
1577 static void InitPlayfieldScanModeVars()
1578 {
1579   if (game.use_reverse_scan_direction)
1580   {
1581     playfield_scan_start_x = lev_fieldx - 1;
1582     playfield_scan_start_y = lev_fieldy - 1;
1583
1584     playfield_scan_delta_x = -1;
1585     playfield_scan_delta_y = -1;
1586   }
1587   else
1588   {
1589     playfield_scan_start_x = 0;
1590     playfield_scan_start_y = 0;
1591
1592     playfield_scan_delta_x = 1;
1593     playfield_scan_delta_y = 1;
1594   }
1595 }
1596
1597 static void InitPlayfieldScanMode(int mode)
1598 {
1599   game.use_reverse_scan_direction =
1600     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1601
1602   InitPlayfieldScanModeVars();
1603 }
1604
1605 static int get_move_delay_from_stepsize(int move_stepsize)
1606 {
1607   move_stepsize =
1608     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1609
1610   /* make sure that stepsize value is always a power of 2 */
1611   move_stepsize = (1 << log_2(move_stepsize));
1612
1613   return TILEX / move_stepsize;
1614 }
1615
1616 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617                                boolean init_game)
1618 {
1619   int player_nr = player->index_nr;
1620   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1621   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1622
1623   /* do no immediately change move delay -- the player might just be moving */
1624   player->move_delay_value_next = move_delay;
1625
1626   /* information if player can move must be set separately */
1627   player->cannot_move = cannot_move;
1628
1629   if (init_game)
1630   {
1631     player->move_delay       = game.initial_move_delay[player_nr];
1632     player->move_delay_value = game.initial_move_delay_value[player_nr];
1633
1634     player->move_delay_value_next = -1;
1635
1636     player->move_delay_reset_counter = 0;
1637   }
1638 }
1639
1640 void GetPlayerConfig()
1641 {
1642   GameFrameDelay = setup.game_frame_delay;
1643
1644   if (!audio.sound_available)
1645     setup.sound_simple = FALSE;
1646
1647   if (!audio.loops_available)
1648     setup.sound_loops = FALSE;
1649
1650   if (!audio.music_available)
1651     setup.sound_music = FALSE;
1652
1653   if (!video.fullscreen_available)
1654     setup.fullscreen = FALSE;
1655
1656   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1657
1658   SetAudioMode(setup.sound);
1659   InitJoysticks();
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void InitPlayerField(int x, int y, int element, boolean init_game)
1689 {
1690   if (element == EL_SP_MURPHY)
1691   {
1692     if (init_game)
1693     {
1694       if (stored_player[0].present)
1695       {
1696         Feld[x][y] = EL_SP_MURPHY_CLONE;
1697
1698         return;
1699       }
1700       else
1701       {
1702         stored_player[0].initial_element = element;
1703         stored_player[0].use_murphy = TRUE;
1704
1705         if (!level.use_artwork_element[0])
1706           stored_player[0].artwork_element = EL_SP_MURPHY;
1707       }
1708
1709       Feld[x][y] = EL_PLAYER_1;
1710     }
1711   }
1712
1713   if (init_game)
1714   {
1715     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1716     int jx = player->jx, jy = player->jy;
1717
1718     player->present = TRUE;
1719
1720     player->block_last_field = (element == EL_SP_MURPHY ?
1721                                 level.sp_block_last_field :
1722                                 level.block_last_field);
1723
1724     /* ---------- initialize player's last field block delay --------------- */
1725
1726     /* always start with reliable default value (no adjustment needed) */
1727     player->block_delay_adjustment = 0;
1728
1729     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1730     if (player->block_last_field && element == EL_SP_MURPHY)
1731       player->block_delay_adjustment = 1;
1732
1733     /* special case 2: in game engines before 3.1.1, blocking was different */
1734     if (game.use_block_last_field_bug)
1735       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1736
1737     if (!options.network || player->connected)
1738     {
1739       player->active = TRUE;
1740
1741       /* remove potentially duplicate players */
1742       if (StorePlayer[jx][jy] == Feld[x][y])
1743         StorePlayer[jx][jy] = 0;
1744
1745       StorePlayer[x][y] = Feld[x][y];
1746
1747 #if DEBUG_INIT_PLAYER
1748       if (options.debug)
1749       {
1750         printf("- player element %d activated", player->element_nr);
1751         printf(" (local player is %d and currently %s)\n",
1752                local_player->element_nr,
1753                local_player->active ? "active" : "not active");
1754       }
1755     }
1756 #endif
1757
1758     Feld[x][y] = EL_EMPTY;
1759
1760     player->jx = player->last_jx = x;
1761     player->jy = player->last_jy = y;
1762   }
1763
1764 #if USE_PLAYER_REANIMATION
1765   if (!init_game)
1766   {
1767     int player_nr = GET_PLAYER_NR(element);
1768     struct PlayerInfo *player = &stored_player[player_nr];
1769
1770     if (player->active && player->killed)
1771       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1772   }
1773 #endif
1774 }
1775
1776 static void InitField(int x, int y, boolean init_game)
1777 {
1778   int element = Feld[x][y];
1779
1780   switch (element)
1781   {
1782     case EL_SP_MURPHY:
1783     case EL_PLAYER_1:
1784     case EL_PLAYER_2:
1785     case EL_PLAYER_3:
1786     case EL_PLAYER_4:
1787       InitPlayerField(x, y, element, init_game);
1788       break;
1789
1790     case EL_SOKOBAN_FIELD_PLAYER:
1791       element = Feld[x][y] = EL_PLAYER_1;
1792       InitField(x, y, init_game);
1793
1794       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1795       InitField(x, y, init_game);
1796       break;
1797
1798     case EL_SOKOBAN_FIELD_EMPTY:
1799       local_player->sokobanfields_still_needed++;
1800       break;
1801
1802     case EL_STONEBLOCK:
1803       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1804         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1805       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1809       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1810         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1811       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1812         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1813       break;
1814
1815     case EL_BUG:
1816     case EL_BUG_RIGHT:
1817     case EL_BUG_UP:
1818     case EL_BUG_LEFT:
1819     case EL_BUG_DOWN:
1820     case EL_SPACESHIP:
1821     case EL_SPACESHIP_RIGHT:
1822     case EL_SPACESHIP_UP:
1823     case EL_SPACESHIP_LEFT:
1824     case EL_SPACESHIP_DOWN:
1825     case EL_BD_BUTTERFLY:
1826     case EL_BD_BUTTERFLY_RIGHT:
1827     case EL_BD_BUTTERFLY_UP:
1828     case EL_BD_BUTTERFLY_LEFT:
1829     case EL_BD_BUTTERFLY_DOWN:
1830     case EL_BD_FIREFLY:
1831     case EL_BD_FIREFLY_RIGHT:
1832     case EL_BD_FIREFLY_UP:
1833     case EL_BD_FIREFLY_LEFT:
1834     case EL_BD_FIREFLY_DOWN:
1835     case EL_PACMAN_RIGHT:
1836     case EL_PACMAN_UP:
1837     case EL_PACMAN_LEFT:
1838     case EL_PACMAN_DOWN:
1839     case EL_YAMYAM:
1840     case EL_YAMYAM_LEFT:
1841     case EL_YAMYAM_RIGHT:
1842     case EL_YAMYAM_UP:
1843     case EL_YAMYAM_DOWN:
1844     case EL_DARK_YAMYAM:
1845     case EL_ROBOT:
1846     case EL_PACMAN:
1847     case EL_SP_SNIKSNAK:
1848     case EL_SP_ELECTRON:
1849     case EL_MOLE:
1850     case EL_MOLE_LEFT:
1851     case EL_MOLE_RIGHT:
1852     case EL_MOLE_UP:
1853     case EL_MOLE_DOWN:
1854       InitMovDir(x, y);
1855       break;
1856
1857     case EL_AMOEBA_FULL:
1858     case EL_BD_AMOEBA:
1859       InitAmoebaNr(x, y);
1860       break;
1861
1862     case EL_AMOEBA_DROP:
1863       if (y == lev_fieldy - 1)
1864       {
1865         Feld[x][y] = EL_AMOEBA_GROWING;
1866         Store[x][y] = EL_AMOEBA_WET;
1867       }
1868       break;
1869
1870     case EL_DYNAMITE_ACTIVE:
1871     case EL_SP_DISK_RED_ACTIVE:
1872     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1873     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1876       MovDelay[x][y] = 96;
1877       break;
1878
1879     case EL_EM_DYNAMITE_ACTIVE:
1880       MovDelay[x][y] = 32;
1881       break;
1882
1883     case EL_LAMP:
1884       local_player->lights_still_needed++;
1885       break;
1886
1887     case EL_PENGUIN:
1888       local_player->friends_still_needed++;
1889       break;
1890
1891     case EL_PIG:
1892     case EL_DRAGON:
1893       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1894       break;
1895
1896     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1897     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1898     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1899     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1900     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1901     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1902     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1903     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1904     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1905     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1906     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1907     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1908       if (init_game)
1909       {
1910         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1911         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1912         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1913
1914         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1915         {
1916           game.belt_dir[belt_nr] = belt_dir;
1917           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1918         }
1919         else    /* more than one switch -- set it like the first switch */
1920         {
1921           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1922         }
1923       }
1924       break;
1925
1926 #if !USE_BOTH_SWITCHGATE_SWITCHES
1927     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1928       if (init_game)
1929         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1930       break;
1931
1932     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1933       if (init_game)
1934         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1935       break;
1936 #endif
1937
1938     case EL_LIGHT_SWITCH_ACTIVE:
1939       if (init_game)
1940         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1941       break;
1942
1943     case EL_INVISIBLE_STEELWALL:
1944     case EL_INVISIBLE_WALL:
1945     case EL_INVISIBLE_SAND:
1946       if (game.light_time_left > 0 ||
1947           game.lenses_time_left > 0)
1948         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1949       break;
1950
1951     case EL_EMC_MAGIC_BALL:
1952       if (game.ball_state)
1953         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1954       break;
1955
1956     case EL_EMC_MAGIC_BALL_SWITCH:
1957       if (game.ball_state)
1958         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1959       break;
1960
1961     case EL_TRIGGER_PLAYER:
1962     case EL_TRIGGER_ELEMENT:
1963     case EL_TRIGGER_CE_VALUE:
1964     case EL_TRIGGER_CE_SCORE:
1965     case EL_SELF:
1966     case EL_ANY_ELEMENT:
1967     case EL_CURRENT_CE_VALUE:
1968     case EL_CURRENT_CE_SCORE:
1969     case EL_PREV_CE_1:
1970     case EL_PREV_CE_2:
1971     case EL_PREV_CE_3:
1972     case EL_PREV_CE_4:
1973     case EL_PREV_CE_5:
1974     case EL_PREV_CE_6:
1975     case EL_PREV_CE_7:
1976     case EL_PREV_CE_8:
1977     case EL_NEXT_CE_1:
1978     case EL_NEXT_CE_2:
1979     case EL_NEXT_CE_3:
1980     case EL_NEXT_CE_4:
1981     case EL_NEXT_CE_5:
1982     case EL_NEXT_CE_6:
1983     case EL_NEXT_CE_7:
1984     case EL_NEXT_CE_8:
1985       /* reference elements should not be used on the playfield */
1986       Feld[x][y] = EL_EMPTY;
1987       break;
1988
1989     default:
1990       if (IS_CUSTOM_ELEMENT(element))
1991       {
1992         if (CAN_MOVE(element))
1993           InitMovDir(x, y);
1994
1995 #if USE_NEW_CUSTOM_VALUE
1996         if (!element_info[element].use_last_ce_value || init_game)
1997           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1998 #endif
1999       }
2000       else if (IS_GROUP_ELEMENT(element))
2001       {
2002         Feld[x][y] = GetElementFromGroupElement(element);
2003
2004         InitField(x, y, init_game);
2005       }
2006
2007       break;
2008   }
2009
2010   if (!init_game)
2011     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2012 }
2013
2014 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2015 {
2016   InitField(x, y, init_game);
2017
2018   /* not needed to call InitMovDir() -- already done by InitField()! */
2019   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2020       CAN_MOVE(Feld[x][y]))
2021     InitMovDir(x, y);
2022 }
2023
2024 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2025 {
2026   int old_element = Feld[x][y];
2027
2028   InitField(x, y, init_game);
2029
2030   /* not needed to call InitMovDir() -- already done by InitField()! */
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(old_element) &&
2033       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2034     InitMovDir(x, y);
2035
2036   /* this case is in fact a combination of not less than three bugs:
2037      first, it calls InitMovDir() for elements that can move, although this is
2038      already done by InitField(); then, it checks the element that was at this
2039      field _before_ the call to InitField() (which can change it); lastly, it
2040      was not called for "mole with direction" elements, which were treated as
2041      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2042   */
2043 }
2044
2045 static int get_key_element_from_nr(int key_nr)
2046 {
2047   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2048                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2049                           EL_EM_KEY_1 : EL_KEY_1);
2050
2051   return key_base_element + key_nr;
2052 }
2053
2054 static int get_next_dropped_element(struct PlayerInfo *player)
2055 {
2056   return (player->inventory_size > 0 ?
2057           player->inventory_element[player->inventory_size - 1] :
2058           player->inventory_infinite_element != EL_UNDEFINED ?
2059           player->inventory_infinite_element :
2060           player->dynabombs_left > 0 ?
2061           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2062           EL_UNDEFINED);
2063 }
2064
2065 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2066 {
2067   /* pos >= 0: get element from bottom of the stack;
2068      pos <  0: get element from top of the stack */
2069
2070   if (pos < 0)
2071   {
2072     int min_inventory_size = -pos;
2073     int inventory_pos = player->inventory_size - min_inventory_size;
2074     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2075
2076     return (player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             player->inventory_infinite_element != EL_UNDEFINED ?
2079             player->inventory_infinite_element :
2080             player->dynabombs_left >= min_dynabombs_left ?
2081             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2082             EL_UNDEFINED);
2083   }
2084   else
2085   {
2086     int min_dynabombs_left = pos + 1;
2087     int min_inventory_size = pos + 1 - player->dynabombs_left;
2088     int inventory_pos = pos - player->dynabombs_left;
2089
2090     return (player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             player->inventory_size >= min_inventory_size ?
2095             player->inventory_element[inventory_pos] :
2096             EL_UNDEFINED);
2097   }
2098 }
2099
2100 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2101 {
2102   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2103   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2104   int compare_result;
2105
2106   if (gpo1->sort_priority != gpo2->sort_priority)
2107     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2108   else
2109     compare_result = gpo1->nr - gpo2->nr;
2110
2111   return compare_result;
2112 }
2113
2114 void InitGameControlValues()
2115 {
2116   int i;
2117
2118   for (i = 0; game_panel_controls[i].nr != -1; i++)
2119   {
2120     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2121     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2122     struct TextPosInfo *pos = gpc->pos;
2123     int nr = gpc->nr;
2124     int type = gpc->type;
2125
2126     if (nr != i)
2127     {
2128       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2129       Error(ERR_EXIT, "this should not happen -- please debug");
2130     }
2131
2132     /* force update of game controls after initialization */
2133     gpc->value = gpc->last_value = -1;
2134     gpc->frame = gpc->last_frame = -1;
2135     gpc->gfx_frame = -1;
2136
2137     /* determine panel value width for later calculation of alignment */
2138     if (type == TYPE_INTEGER || type == TYPE_STRING)
2139     {
2140       pos->width = pos->size * getFontWidth(pos->font);
2141       pos->height = getFontHeight(pos->font);
2142     }
2143     else if (type == TYPE_ELEMENT)
2144     {
2145       pos->width = pos->size;
2146       pos->height = pos->size;
2147     }
2148
2149     /* fill structure for game panel draw order */
2150     gpo->nr = gpc->nr;
2151     gpo->sort_priority = pos->sort_priority;
2152   }
2153
2154   /* sort game panel controls according to sort_priority and control number */
2155   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2156         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2157 }
2158
2159 void UpdatePlayfieldElementCount()
2160 {
2161   boolean use_element_count = FALSE;
2162   int i, j, x, y;
2163
2164   /* first check if it is needed at all to calculate playfield element count */
2165   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2166     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2167       use_element_count = TRUE;
2168
2169   if (!use_element_count)
2170     return;
2171
2172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2173     element_info[i].element_count = 0;
2174
2175   SCAN_PLAYFIELD(x, y)
2176   {
2177     element_info[Feld[x][y]].element_count++;
2178   }
2179
2180   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2181     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2182       if (IS_IN_GROUP(j, i))
2183         element_info[EL_GROUP_START + i].element_count +=
2184           element_info[j].element_count;
2185 }
2186
2187 void UpdateGameControlValues()
2188 {
2189   int i, k;
2190   int time = (local_player->LevelSolved ?
2191               local_player->LevelSolved_CountingTime :
2192               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193               level.native_em_level->lev->time :
2194               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195               level.native_sp_level->game_sp->time_played :
2196               game.no_time_limit ? TimePlayed : TimeLeft);
2197   int score = (local_player->LevelSolved ?
2198                local_player->LevelSolved_CountingScore :
2199                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200                level.native_em_level->lev->score :
2201                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202                level.native_sp_level->game_sp->score :
2203                local_player->score);
2204   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205               level.native_em_level->lev->required :
2206               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2207               level.native_sp_level->game_sp->infotrons_still_needed :
2208               local_player->gems_still_needed);
2209   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210                      level.native_em_level->lev->required > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2212                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216
2217   UpdatePlayfieldElementCount();
2218
2219   /* update game panel control values */
2220
2221   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2222   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2223
2224   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2225   for (i = 0; i < MAX_NUM_KEYS; i++)
2226     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2227   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2228   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2229
2230   if (game.centered_player_nr == -1)
2231   {
2232     for (i = 0; i < MAX_PLAYERS; i++)
2233     {
2234       /* only one player in Supaplex game engine */
2235       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2236         break;
2237
2238       for (k = 0; k < MAX_NUM_KEYS; k++)
2239       {
2240         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241         {
2242           if (level.native_em_level->ply[i]->keys & (1 << k))
2243             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2244               get_key_element_from_nr(k);
2245         }
2246         else if (stored_player[i].key[k])
2247           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2248             get_key_element_from_nr(k);
2249       }
2250
2251       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           level.native_em_level->ply[i]->dynamite;
2254       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2255         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256           level.native_sp_level->game_sp->red_disk_count;
2257       else
2258         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259           stored_player[i].inventory_size;
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         level.native_em_level->ply[player_nr]->dynamite;
2289     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         level.native_sp_level->game_sp->red_disk_count;
2292     else
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         stored_player[player_nr].inventory_size;
2295
2296     if (stored_player[player_nr].num_white_keys > 0)
2297       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2298
2299     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2300       stored_player[player_nr].num_white_keys;
2301   }
2302
2303   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2304   {
2305     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2306       get_inventory_element_from_pos(local_player, i);
2307     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2308       get_inventory_element_from_pos(local_player, -i - 1);
2309   }
2310
2311   game_panel_controls[GAME_PANEL_SCORE].value = score;
2312   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2313
2314   game_panel_controls[GAME_PANEL_TIME].value = time;
2315
2316   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2317   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2318   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2319
2320   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321
2322   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324      EL_EMPTY);
2325   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326     local_player->shield_normal_time_left;
2327   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329      EL_EMPTY);
2330   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331     local_player->shield_deadly_time_left;
2332
2333   game_panel_controls[GAME_PANEL_EXIT].value =
2334     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335
2336   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340      EL_EMC_MAGIC_BALL_SWITCH);
2341
2342   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345     game.light_time_left;
2346
2347   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350     game.timegate_time_left;
2351
2352   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354
2355   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358     game.lenses_time_left;
2359
2360   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363     game.magnify_time_left;
2364
2365   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2367      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2369      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2370      EL_BALLOON_SWITCH_NONE);
2371
2372   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373     local_player->dynabomb_count;
2374   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375     local_player->dynabomb_size;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378
2379   game_panel_controls[GAME_PANEL_PENGUINS].value =
2380     local_player->friends_still_needed;
2381
2382   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383     local_player->sokobanfields_still_needed;
2384   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385     local_player->sokobanfields_still_needed;
2386
2387   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389
2390   for (i = 0; i < NUM_BELTS; i++)
2391   {
2392     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2397   }
2398
2399   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402     game.magic_wall_time_left;
2403
2404 #if USE_PLAYER_GRAVITY
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407 #else
2408   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2409 #endif
2410
2411   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2412     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2416       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2417        game.panel.element[i].id : EL_UNDEFINED);
2418
2419   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2420     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2421       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2422        element_info[game.panel.element_count[i].id].element_count : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2427        element_info[game.panel.ce_score[i].id].collect_score : 0);
2428
2429   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2430     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2431       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2432        element_info[game.panel.ce_score_element[i].id].collect_score :
2433        EL_UNDEFINED);
2434
2435   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2436   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2437   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2438
2439   /* update game panel control frames */
2440
2441   for (i = 0; game_panel_controls[i].nr != -1; i++)
2442   {
2443     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2444
2445     if (gpc->type == TYPE_ELEMENT)
2446     {
2447       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2448       {
2449         int last_anim_random_frame = gfx.anim_random_frame;
2450         int element = gpc->value;
2451         int graphic = el2panelimg(element);
2452
2453         if (gpc->value != gpc->last_value)
2454         {
2455           gpc->gfx_frame = 0;
2456           gpc->gfx_random = INIT_GFX_RANDOM();
2457         }
2458         else
2459         {
2460           gpc->gfx_frame++;
2461
2462           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2463               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2464             gpc->gfx_random = INIT_GFX_RANDOM();
2465         }
2466
2467         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2468           gfx.anim_random_frame = gpc->gfx_random;
2469
2470         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2471           gpc->gfx_frame = element_info[element].collect_score;
2472
2473         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2474                                               gpc->gfx_frame);
2475
2476         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2477           gfx.anim_random_frame = last_anim_random_frame;
2478       }
2479     }
2480   }
2481 }
2482
2483 void DisplayGameControlValues()
2484 {
2485   boolean redraw_panel = FALSE;
2486   int i;
2487
2488   for (i = 0; game_panel_controls[i].nr != -1; i++)
2489   {
2490     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2491
2492     if (PANEL_DEACTIVATED(gpc->pos))
2493       continue;
2494
2495     if (gpc->value == gpc->last_value &&
2496         gpc->frame == gpc->last_frame)
2497       continue;
2498
2499     redraw_panel = TRUE;
2500   }
2501
2502   if (!redraw_panel)
2503     return;
2504
2505   /* copy default game door content to main double buffer */
2506 #if 1
2507   /* !!! CHECK AGAIN !!! */
2508   SetPanelBackground();
2509   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2510   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2511 #else
2512   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2513              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2514 #endif
2515
2516   /* redraw game control buttons */
2517 #if 1
2518   RedrawGameButtons();
2519 #else
2520   UnmapGameButtons();
2521   MapGameButtons();
2522 #endif
2523
2524   game_status = GAME_MODE_PSEUDO_PANEL;
2525
2526 #if 1
2527   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2528 #else
2529   for (i = 0; game_panel_controls[i].nr != -1; i++)
2530 #endif
2531   {
2532 #if 1
2533     int nr = game_panel_order[i].nr;
2534     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535 #else
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2537     int nr = gpc->nr;
2538 #endif
2539     struct TextPosInfo *pos = gpc->pos;
2540     int type = gpc->type;
2541     int value = gpc->value;
2542     int frame = gpc->frame;
2543 #if 0
2544     int last_value = gpc->last_value;
2545     int last_frame = gpc->last_frame;
2546 #endif
2547     int size = pos->size;
2548     int font = pos->font;
2549     boolean draw_masked = pos->draw_masked;
2550     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2551
2552     if (PANEL_DEACTIVATED(pos))
2553       continue;
2554
2555 #if 0
2556     if (value == last_value && frame == last_frame)
2557       continue;
2558 #endif
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563 #if 0
2564     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2565 #endif
2566
2567     if (type == TYPE_INTEGER)
2568     {
2569       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2570           nr == GAME_PANEL_TIME)
2571       {
2572         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2573
2574         if (use_dynamic_size)           /* use dynamic number of digits */
2575         {
2576           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2577           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2578           int size2 = size1 + 1;
2579           int font1 = pos->font;
2580           int font2 = pos->font_alt;
2581
2582           size = (value < value_change ? size1 : size2);
2583           font = (value < value_change ? font1 : font2);
2584
2585 #if 0
2586           /* clear background if value just changed its size (dynamic digits) */
2587           if ((last_value < value_change) != (value < value_change))
2588           {
2589             int width1 = size1 * getFontWidth(font1);
2590             int width2 = size2 * getFontWidth(font2);
2591             int max_width = MAX(width1, width2);
2592             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2593
2594             pos->width = max_width;
2595
2596             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2597                                        max_width, max_height);
2598           }
2599 #endif
2600         }
2601       }
2602
2603 #if 1
2604       /* correct text size if "digits" is zero or less */
2605       if (size <= 0)
2606         size = strlen(int2str(value, size));
2607
2608       /* dynamically correct text alignment */
2609       pos->width = size * getFontWidth(font);
2610 #endif
2611
2612       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2613                   int2str(value, size), font, mask_mode);
2614     }
2615     else if (type == TYPE_ELEMENT)
2616     {
2617       int element, graphic;
2618       Bitmap *src_bitmap;
2619       int src_x, src_y;
2620       int width, height;
2621       int dst_x = PANEL_XPOS(pos);
2622       int dst_y = PANEL_YPOS(pos);
2623
2624 #if 1
2625       if (value != EL_UNDEFINED && value != EL_EMPTY)
2626       {
2627         element = value;
2628         graphic = el2panelimg(value);
2629
2630         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2631
2632 #if 1
2633         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2634           size = TILESIZE;
2635 #endif
2636
2637         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2638                               &src_x, &src_y);
2639
2640         width  = graphic_info[graphic].width  * size / TILESIZE;
2641         height = graphic_info[graphic].height * size / TILESIZE;
2642
2643         if (draw_masked)
2644         {
2645           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2646                         dst_x - src_x, dst_y - src_y);
2647           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2648                            dst_x, dst_y);
2649         }
2650         else
2651         {
2652           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2653                      dst_x, dst_y);
2654         }
2655       }
2656 #else
2657       if (value == EL_UNDEFINED || value == EL_EMPTY)
2658       {
2659         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2660         graphic = el2panelimg(element);
2661
2662         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2663         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2664         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2665       }
2666       else
2667       {
2668         element = value;
2669         graphic = el2panelimg(value);
2670
2671         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2672       }
2673
2674       width  = graphic_info[graphic].width  * size / TILESIZE;
2675       height = graphic_info[graphic].height * size / TILESIZE;
2676
2677       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2678 #endif
2679     }
2680     else if (type == TYPE_STRING)
2681     {
2682       boolean active = (value != 0);
2683       char *state_normal = "off";
2684       char *state_active = "on";
2685       char *state = (active ? state_active : state_normal);
2686       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2687                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2688                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2689                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2690
2691       if (nr == GAME_PANEL_GRAVITY_STATE)
2692       {
2693         int font1 = pos->font;          /* (used for normal state) */
2694         int font2 = pos->font_alt;      /* (used for active state) */
2695 #if 0
2696         int size1 = strlen(state_normal);
2697         int size2 = strlen(state_active);
2698         int width1 = size1 * getFontWidth(font1);
2699         int width2 = size2 * getFontWidth(font2);
2700         int max_width = MAX(width1, width2);
2701         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2702
2703         pos->width = max_width;
2704
2705         /* clear background for values that may have changed its size */
2706         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2707                                    max_width, max_height);
2708 #endif
2709
2710         font = (active ? font2 : font1);
2711       }
2712
2713       if (s != NULL)
2714       {
2715         char *s_cut;
2716
2717 #if 1
2718         if (size <= 0)
2719         {
2720           /* don't truncate output if "chars" is zero or less */
2721           size = strlen(s);
2722
2723           /* dynamically correct text alignment */
2724           pos->width = size * getFontWidth(font);
2725         }
2726 #endif
2727
2728         s_cut = getStringCopyN(s, size);
2729
2730         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                     s_cut, font, mask_mode);
2732
2733         free(s_cut);
2734       }
2735     }
2736
2737     redraw_mask |= REDRAW_DOOR_1;
2738   }
2739
2740   game_status = GAME_MODE_PLAYING;
2741 }
2742
2743 void UpdateAndDisplayGameControlValues()
2744 {
2745   if (tape.warp_forward)
2746     return;
2747
2748   UpdateGameControlValues();
2749   DisplayGameControlValues();
2750 }
2751
2752 void DrawGameValue_Emeralds(int value)
2753 {
2754   struct TextPosInfo *pos = &game.panel.gems;
2755   int font_nr = pos->font;
2756   int font_width = getFontWidth(font_nr);
2757   int chars = pos->size;
2758
2759 #if 1
2760   return;       /* !!! USE NEW STUFF !!! */
2761 #endif
2762
2763   if (PANEL_DEACTIVATED(pos))
2764     return;
2765
2766   pos->width = chars * font_width;
2767
2768   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2769 }
2770
2771 void DrawGameValue_Dynamite(int value)
2772 {
2773   struct TextPosInfo *pos = &game.panel.inventory_count;
2774   int font_nr = pos->font;
2775   int font_width = getFontWidth(font_nr);
2776   int chars = pos->size;
2777
2778 #if 1
2779   return;       /* !!! USE NEW STUFF !!! */
2780 #endif
2781
2782   if (PANEL_DEACTIVATED(pos))
2783     return;
2784
2785   pos->width = chars * font_width;
2786
2787   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2788 }
2789
2790 void DrawGameValue_Score(int value)
2791 {
2792   struct TextPosInfo *pos = &game.panel.score;
2793   int font_nr = pos->font;
2794   int font_width = getFontWidth(font_nr);
2795   int chars = pos->size;
2796
2797 #if 1
2798   return;       /* !!! USE NEW STUFF !!! */
2799 #endif
2800
2801   if (PANEL_DEACTIVATED(pos))
2802     return;
2803
2804   pos->width = chars * font_width;
2805
2806   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2807 }
2808
2809 void DrawGameValue_Time(int value)
2810 {
2811   struct TextPosInfo *pos = &game.panel.time;
2812   static int last_value = -1;
2813   int chars1 = 3;
2814   int chars2 = 4;
2815   int chars = pos->size;
2816   int font1_nr = pos->font;
2817   int font2_nr = pos->font_alt;
2818   int font_nr = font1_nr;
2819   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2820
2821 #if 1
2822   return;       /* !!! USE NEW STUFF !!! */
2823 #endif
2824
2825   if (PANEL_DEACTIVATED(pos))
2826     return;
2827
2828   if (use_dynamic_chars)                /* use dynamic number of chars */
2829   {
2830     chars   = (value < 1000 ? chars1   : chars2);
2831     font_nr = (value < 1000 ? font1_nr : font2_nr);
2832   }
2833
2834   /* clear background if value just changed its size (dynamic chars only) */
2835   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2836   {
2837     int width1 = chars1 * getFontWidth(font1_nr);
2838     int width2 = chars2 * getFontWidth(font2_nr);
2839     int max_width = MAX(width1, width2);
2840     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2841
2842     pos->width = max_width;
2843
2844     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2845                                max_width, max_height);
2846   }
2847
2848   pos->width = chars * getFontWidth(font_nr);
2849
2850   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2851
2852   last_value = value;
2853 }
2854
2855 void DrawGameValue_Level(int value)
2856 {
2857   struct TextPosInfo *pos = &game.panel.level_number;
2858   int chars1 = 2;
2859   int chars2 = 3;
2860   int chars = pos->size;
2861   int font1_nr = pos->font;
2862   int font2_nr = pos->font_alt;
2863   int font_nr = font1_nr;
2864   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2865
2866 #if 1
2867   return;       /* !!! USE NEW STUFF !!! */
2868 #endif
2869
2870   if (PANEL_DEACTIVATED(pos))
2871     return;
2872
2873   if (use_dynamic_chars)                /* use dynamic number of chars */
2874   {
2875     chars   = (level_nr < 100 ? chars1   : chars2);
2876     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2877   }
2878
2879   pos->width = chars * getFontWidth(font_nr);
2880
2881   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2882 }
2883
2884 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2885 {
2886   int i;
2887
2888 #if 1
2889   return;       /* !!! USE NEW STUFF !!! */
2890 #endif
2891
2892   for (i = 0; i < MAX_NUM_KEYS; i++)
2893   {
2894     struct TextPosInfo *pos = &game.panel.key[i];
2895     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2896     int src_y = DOOR_GFX_PAGEY1 + 123;
2897     int dst_x = PANEL_XPOS(pos);
2898     int dst_y = PANEL_YPOS(pos);
2899
2900     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2901                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2902                    EL_KEY_1) + i;
2903     int graphic = el2edimg(element);
2904
2905     if (PANEL_DEACTIVATED(pos))
2906       continue;
2907
2908 #if 0
2909     /* masked blit with tiles from half-size scaled bitmap does not work yet
2910        (no mask bitmap created for these sizes after loading and scaling) --
2911        solution: load without creating mask, scale, then create final mask */
2912
2913     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2914                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2915
2916     if (key[i])
2917     {
2918       Bitmap *src_bitmap;
2919       int src_x, src_y;
2920
2921       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2922
2923       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2924                     dst_x - src_x, dst_y - src_y);
2925       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2926                        dst_x, dst_y);
2927     }
2928 #else
2929     if (key[i])
2930       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2931     else
2932       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2933                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2934 #endif
2935   }
2936 }
2937
2938 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2939                        int key_bits)
2940 {
2941   int key[MAX_NUM_KEYS];
2942   int i;
2943
2944   /* prevent EM engine from updating time/score values parallel to GameWon() */
2945   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2946       local_player->LevelSolved)
2947     return;
2948
2949   for (i = 0; i < MAX_NUM_KEYS; i++)
2950     key[i] = key_bits & (1 << i);
2951
2952   DrawGameValue_Level(level_nr);
2953
2954   DrawGameValue_Emeralds(emeralds);
2955   DrawGameValue_Dynamite(dynamite);
2956   DrawGameValue_Score(score);
2957   DrawGameValue_Time(time);
2958
2959   DrawGameValue_Keys(key);
2960 }
2961
2962 void UpdateGameDoorValues()
2963 {
2964   UpdateGameControlValues();
2965 }
2966
2967 void DrawGameDoorValues()
2968 {
2969   DisplayGameControlValues();
2970 }
2971
2972 void DrawGameDoorValues_OLD()
2973 {
2974   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2975   int dynamite_value = 0;
2976   int score_value = (local_player->LevelSolved ? local_player->score_final :
2977                      local_player->score);
2978   int gems_value = local_player->gems_still_needed;
2979   int key_bits = 0;
2980   int i, j;
2981
2982   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2983   {
2984     DrawGameDoorValues_EM();
2985
2986     return;
2987   }
2988
2989   if (game.centered_player_nr == -1)
2990   {
2991     for (i = 0; i < MAX_PLAYERS; i++)
2992     {
2993       for (j = 0; j < MAX_NUM_KEYS; j++)
2994         if (stored_player[i].key[j])
2995           key_bits |= (1 << j);
2996
2997       dynamite_value += stored_player[i].inventory_size;
2998     }
2999   }
3000   else
3001   {
3002     int player_nr = game.centered_player_nr;
3003
3004     for (i = 0; i < MAX_NUM_KEYS; i++)
3005       if (stored_player[player_nr].key[i])
3006         key_bits |= (1 << i);
3007
3008     dynamite_value = stored_player[player_nr].inventory_size;
3009   }
3010
3011   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3012                     key_bits);
3013 }
3014
3015
3016 /*
3017   =============================================================================
3018   InitGameEngine()
3019   -----------------------------------------------------------------------------
3020   initialize game engine due to level / tape version number
3021   =============================================================================
3022 */
3023
3024 static void InitGameEngine()
3025 {
3026   int i, j, k, l, x, y;
3027
3028   /* set game engine from tape file when re-playing, else from level file */
3029   game.engine_version = (tape.playing ? tape.engine_version :
3030                          level.game_version);
3031
3032   /* set single or multi-player game mode (needed for re-playing tapes) */
3033   game.team_mode = setup.team_mode;
3034
3035   if (tape.playing)
3036   {
3037     int num_players = 0;
3038
3039     for (i = 0; i < MAX_PLAYERS; i++)
3040       if (tape.player_participates[i])
3041         num_players++;
3042
3043     /* multi-player tapes contain input data for more than one player */
3044     game.team_mode = (num_players > 1);
3045   }
3046
3047   /* ---------------------------------------------------------------------- */
3048   /* set flags for bugs and changes according to active game engine version */
3049   /* ---------------------------------------------------------------------- */
3050
3051   /*
3052     Summary of bugfix/change:
3053     Fixed handling for custom elements that change when pushed by the player.
3054
3055     Fixed/changed in version:
3056     3.1.0
3057
3058     Description:
3059     Before 3.1.0, custom elements that "change when pushing" changed directly
3060     after the player started pushing them (until then handled in "DigField()").
3061     Since 3.1.0, these custom elements are not changed until the "pushing"
3062     move of the element is finished (now handled in "ContinueMoving()").
3063
3064     Affected levels/tapes:
3065     The first condition is generally needed for all levels/tapes before version
3066     3.1.0, which might use the old behaviour before it was changed; known tapes
3067     that are affected are some tapes from the level set "Walpurgis Gardens" by
3068     Jamie Cullen.
3069     The second condition is an exception from the above case and is needed for
3070     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3071     above (including some development versions of 3.1.0), but before it was
3072     known that this change would break tapes like the above and was fixed in
3073     3.1.1, so that the changed behaviour was active although the engine version
3074     while recording maybe was before 3.1.0. There is at least one tape that is
3075     affected by this exception, which is the tape for the one-level set "Bug
3076     Machine" by Juergen Bonhagen.
3077   */
3078
3079   game.use_change_when_pushing_bug =
3080     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3081      !(tape.playing &&
3082        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3083        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3084
3085   /*
3086     Summary of bugfix/change:
3087     Fixed handling for blocking the field the player leaves when moving.
3088
3089     Fixed/changed in version:
3090     3.1.1
3091
3092     Description:
3093     Before 3.1.1, when "block last field when moving" was enabled, the field
3094     the player is leaving when moving was blocked for the time of the move,
3095     and was directly unblocked afterwards. This resulted in the last field
3096     being blocked for exactly one less than the number of frames of one player
3097     move. Additionally, even when blocking was disabled, the last field was
3098     blocked for exactly one frame.
3099     Since 3.1.1, due to changes in player movement handling, the last field
3100     is not blocked at all when blocking is disabled. When blocking is enabled,
3101     the last field is blocked for exactly the number of frames of one player
3102     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3103     last field is blocked for exactly one more than the number of frames of
3104     one player move.
3105
3106     Affected levels/tapes:
3107     (!!! yet to be determined -- probably many !!!)
3108   */
3109
3110   game.use_block_last_field_bug =
3111     (game.engine_version < VERSION_IDENT(3,1,1,0));
3112
3113   /*
3114     Summary of bugfix/change:
3115     Changed behaviour of CE changes with multiple changes per single frame.
3116
3117     Fixed/changed in version:
3118     3.2.0-6
3119
3120     Description:
3121     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3122     This resulted in race conditions where CEs seem to behave strange in some
3123     situations (where triggered CE changes were just skipped because there was
3124     already a CE change on that tile in the playfield in that engine frame).
3125     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3126     (The number of changes per frame must be limited in any case, because else
3127     it is easily possible to define CE changes that would result in an infinite
3128     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3129     should be set large enough so that it would only be reached in cases where
3130     the corresponding CE change conditions run into a loop. Therefore, it seems
3131     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3132     maximal number of change pages for custom elements.)
3133
3134     Affected levels/tapes:
3135     Probably many.
3136   */
3137
3138 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3139   game.max_num_changes_per_frame = 1;
3140 #else
3141   game.max_num_changes_per_frame =
3142     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3143 #endif
3144
3145   /* ---------------------------------------------------------------------- */
3146
3147   /* default scan direction: scan playfield from top/left to bottom/right */
3148   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3149
3150   /* dynamically adjust element properties according to game engine version */
3151   InitElementPropertiesEngine(game.engine_version);
3152
3153 #if 0
3154   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3155   printf("          tape version == %06d [%s] [file: %06d]\n",
3156          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3157          tape.file_version);
3158   printf("       => game.engine_version == %06d\n", game.engine_version);
3159 #endif
3160
3161   /* ---------- initialize player's initial move delay --------------------- */
3162
3163   /* dynamically adjust player properties according to level information */
3164   for (i = 0; i < MAX_PLAYERS; i++)
3165     game.initial_move_delay_value[i] =
3166       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3167
3168   /* dynamically adjust player properties according to game engine version */
3169   for (i = 0; i < MAX_PLAYERS; i++)
3170     game.initial_move_delay[i] =
3171       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3172        game.initial_move_delay_value[i] : 0);
3173
3174   /* ---------- initialize player's initial push delay --------------------- */
3175
3176   /* dynamically adjust player properties according to game engine version */
3177   game.initial_push_delay_value =
3178     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3179
3180   /* ---------- initialize changing elements ------------------------------- */
3181
3182   /* initialize changing elements information */
3183   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3184   {
3185     struct ElementInfo *ei = &element_info[i];
3186
3187     /* this pointer might have been changed in the level editor */
3188     ei->change = &ei->change_page[0];
3189
3190     if (!IS_CUSTOM_ELEMENT(i))
3191     {
3192       ei->change->target_element = EL_EMPTY_SPACE;
3193       ei->change->delay_fixed = 0;
3194       ei->change->delay_random = 0;
3195       ei->change->delay_frames = 1;
3196     }
3197
3198     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3199     {
3200       ei->has_change_event[j] = FALSE;
3201
3202       ei->event_page_nr[j] = 0;
3203       ei->event_page[j] = &ei->change_page[0];
3204     }
3205   }
3206
3207   /* add changing elements from pre-defined list */
3208   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3209   {
3210     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3211     struct ElementInfo *ei = &element_info[ch_delay->element];
3212
3213     ei->change->target_element       = ch_delay->target_element;
3214     ei->change->delay_fixed          = ch_delay->change_delay;
3215
3216     ei->change->pre_change_function  = ch_delay->pre_change_function;
3217     ei->change->change_function      = ch_delay->change_function;
3218     ei->change->post_change_function = ch_delay->post_change_function;
3219
3220     ei->change->can_change = TRUE;
3221     ei->change->can_change_or_has_action = TRUE;
3222
3223     ei->has_change_event[CE_DELAY] = TRUE;
3224
3225     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3226     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3227   }
3228
3229   /* ---------- initialize internal run-time variables --------------------- */
3230
3231   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3232   {
3233     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3234
3235     for (j = 0; j < ei->num_change_pages; j++)
3236     {
3237       ei->change_page[j].can_change_or_has_action =
3238         (ei->change_page[j].can_change |
3239          ei->change_page[j].has_action);
3240     }
3241   }
3242
3243   /* add change events from custom element configuration */
3244   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3245   {
3246     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3247
3248     for (j = 0; j < ei->num_change_pages; j++)
3249     {
3250       if (!ei->change_page[j].can_change_or_has_action)
3251         continue;
3252
3253       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3254       {
3255         /* only add event page for the first page found with this event */
3256         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3257         {
3258           ei->has_change_event[k] = TRUE;
3259
3260           ei->event_page_nr[k] = j;
3261           ei->event_page[k] = &ei->change_page[j];
3262         }
3263       }
3264     }
3265   }
3266
3267 #if 1
3268   /* ---------- initialize reference elements in change conditions --------- */
3269
3270   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3271   {
3272     int element = EL_CUSTOM_START + i;
3273     struct ElementInfo *ei = &element_info[element];
3274
3275     for (j = 0; j < ei->num_change_pages; j++)
3276     {
3277       int trigger_element = ei->change_page[j].initial_trigger_element;
3278
3279       if (trigger_element >= EL_PREV_CE_8 &&
3280           trigger_element <= EL_NEXT_CE_8)
3281         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3282
3283       ei->change_page[j].trigger_element = trigger_element;
3284     }
3285   }
3286 #endif
3287
3288   /* ---------- initialize run-time trigger player and element ------------- */
3289
3290   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3291   {
3292     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3293
3294     for (j = 0; j < ei->num_change_pages; j++)
3295     {
3296       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3297       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3298       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3299       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3300       ei->change_page[j].actual_trigger_ce_value = 0;
3301       ei->change_page[j].actual_trigger_ce_score = 0;
3302     }
3303   }
3304
3305   /* ---------- initialize trigger events ---------------------------------- */
3306
3307   /* initialize trigger events information */
3308   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3310       trigger_events[i][j] = FALSE;
3311
3312   /* add trigger events from element change event properties */
3313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3314   {
3315     struct ElementInfo *ei = &element_info[i];
3316
3317     for (j = 0; j < ei->num_change_pages; j++)
3318     {
3319       if (!ei->change_page[j].can_change_or_has_action)
3320         continue;
3321
3322       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3323       {
3324         int trigger_element = ei->change_page[j].trigger_element;
3325
3326         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3327         {
3328           if (ei->change_page[j].has_event[k])
3329           {
3330             if (IS_GROUP_ELEMENT(trigger_element))
3331             {
3332               struct ElementGroupInfo *group =
3333                 element_info[trigger_element].group;
3334
3335               for (l = 0; l < group->num_elements_resolved; l++)
3336                 trigger_events[group->element_resolved[l]][k] = TRUE;
3337             }
3338             else if (trigger_element == EL_ANY_ELEMENT)
3339               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3340                 trigger_events[l][k] = TRUE;
3341             else
3342               trigger_events[trigger_element][k] = TRUE;
3343           }
3344         }
3345       }
3346     }
3347   }
3348
3349   /* ---------- initialize push delay -------------------------------------- */
3350
3351   /* initialize push delay values to default */
3352   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3353   {
3354     if (!IS_CUSTOM_ELEMENT(i))
3355     {
3356       /* set default push delay values (corrected since version 3.0.7-1) */
3357       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3358       {
3359         element_info[i].push_delay_fixed = 2;
3360         element_info[i].push_delay_random = 8;
3361       }
3362       else
3363       {
3364         element_info[i].push_delay_fixed = 8;
3365         element_info[i].push_delay_random = 8;
3366       }
3367     }
3368   }
3369
3370   /* set push delay value for certain elements from pre-defined list */
3371   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3372   {
3373     int e = push_delay_list[i].element;
3374
3375     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3376     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3377   }
3378
3379   /* set push delay value for Supaplex elements for newer engine versions */
3380   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3381   {
3382     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3383     {
3384       if (IS_SP_ELEMENT(i))
3385       {
3386         /* set SP push delay to just enough to push under a falling zonk */
3387         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3388
3389         element_info[i].push_delay_fixed  = delay;
3390         element_info[i].push_delay_random = 0;
3391       }
3392     }
3393   }
3394
3395   /* ---------- initialize move stepsize ----------------------------------- */
3396
3397   /* initialize move stepsize values to default */
3398   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3399     if (!IS_CUSTOM_ELEMENT(i))
3400       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3401
3402   /* set move stepsize value for certain elements from pre-defined list */
3403   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3404   {
3405     int e = move_stepsize_list[i].element;
3406
3407     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3408   }
3409
3410   /* ---------- initialize collect score ----------------------------------- */
3411
3412   /* initialize collect score values for custom elements from initial value */
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414     if (IS_CUSTOM_ELEMENT(i))
3415       element_info[i].collect_score = element_info[i].collect_score_initial;
3416
3417   /* ---------- initialize collect count ----------------------------------- */
3418
3419   /* initialize collect count values for non-custom elements */
3420   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     if (!IS_CUSTOM_ELEMENT(i))
3422       element_info[i].collect_count_initial = 0;
3423
3424   /* add collect count values for all elements from pre-defined list */
3425   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3426     element_info[collect_count_list[i].element].collect_count_initial =
3427       collect_count_list[i].count;
3428
3429   /* ---------- initialize access direction -------------------------------- */
3430
3431   /* initialize access direction values to default (access from every side) */
3432   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433     if (!IS_CUSTOM_ELEMENT(i))
3434       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3435
3436   /* set access direction value for certain elements from pre-defined list */
3437   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3438     element_info[access_direction_list[i].element].access_direction =
3439       access_direction_list[i].direction;
3440
3441   /* ---------- initialize explosion content ------------------------------- */
3442   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3443   {
3444     if (IS_CUSTOM_ELEMENT(i))
3445       continue;
3446
3447     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3448     {
3449       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3450
3451       element_info[i].content.e[x][y] =
3452         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3453          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3454          i == EL_PLAYER_3 ? EL_EMERALD :
3455          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3456          i == EL_MOLE ? EL_EMERALD_RED :
3457          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3458          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3459          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3460          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3461          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3462          i == EL_WALL_EMERALD ? EL_EMERALD :
3463          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3464          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3465          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3466          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3467          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3468          i == EL_WALL_PEARL ? EL_PEARL :
3469          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3470          EL_EMPTY);
3471     }
3472   }
3473
3474   /* ---------- initialize recursion detection ------------------------------ */
3475   recursion_loop_depth = 0;
3476   recursion_loop_detected = FALSE;
3477   recursion_loop_element = EL_UNDEFINED;
3478
3479   /* ---------- initialize graphics engine ---------------------------------- */
3480   game.scroll_delay_value =
3481     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3482      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3483   game.scroll_delay_value =
3484     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3485 }
3486
3487 int get_num_special_action(int element, int action_first, int action_last)
3488 {
3489   int num_special_action = 0;
3490   int i, j;
3491
3492   for (i = action_first; i <= action_last; i++)
3493   {
3494     boolean found = FALSE;
3495
3496     for (j = 0; j < NUM_DIRECTIONS; j++)
3497       if (el_act_dir2img(element, i, j) !=
3498           el_act_dir2img(element, ACTION_DEFAULT, j))
3499         found = TRUE;
3500
3501     if (found)
3502       num_special_action++;
3503     else
3504       break;
3505   }
3506
3507   return num_special_action;
3508 }
3509
3510
3511 /*
3512   =============================================================================
3513   InitGame()
3514   -----------------------------------------------------------------------------
3515   initialize and start new game
3516   =============================================================================
3517 */
3518
3519 void InitGame()
3520 {
3521   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3522   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3523
3524   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3525   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3526   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3527 #if 0
3528   boolean do_fading = (game_status == GAME_MODE_MAIN);
3529 #endif
3530 #if 1
3531   int initial_move_dir = MV_DOWN;
3532 #else
3533   int initial_move_dir = MV_NONE;
3534 #endif
3535   int i, j, x, y;
3536
3537 #if 1
3538   game_status = GAME_MODE_PLAYING;
3539 #endif
3540
3541 #if 1
3542
3543   StopAnimation();
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548 #if 1
3549   if (level_editor_test_game)
3550     FadeSkipNextFadeIn();
3551   else
3552     FadeSetEnterScreen();
3553 #else
3554   if (level_editor_test_game)
3555     fading = fading_none;
3556   else
3557     fading = menu.destination;
3558 #endif
3559
3560 #if 1
3561   FadeOut(REDRAW_FIELD);
3562 #else
3563   if (do_fading)
3564     FadeOut(REDRAW_FIELD);
3565 #endif
3566
3567 #endif
3568
3569 #if 0
3570   printf("::: FADING OUT: DONE\n");
3571   Delay(1000);
3572 #endif
3573
3574 #if 0
3575   game_status = GAME_MODE_PLAYING;
3576 #endif
3577
3578 #if 1
3579   /* needed if different viewport properties defined for playing */
3580   ChangeViewportPropertiesIfNeeded();
3581 #endif
3582
3583 #if 1
3584   DrawCompleteVideoDisplay();
3585 #endif
3586
3587   InitGameEngine();
3588   InitGameControlValues();
3589
3590   /* don't play tapes over network */
3591   network_playing = (options.network && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607
3608     player->action = 0;
3609     player->effective_action = 0;
3610     player->programmed_action = 0;
3611
3612     player->score = 0;
3613     player->score_final = 0;
3614
3615     player->gems_still_needed = level.gems_needed;
3616     player->sokobanfields_still_needed = 0;
3617     player->lights_still_needed = 0;
3618     player->friends_still_needed = 0;
3619
3620     for (j = 0; j < MAX_NUM_KEYS; j++)
3621       player->key[j] = FALSE;
3622
3623     player->num_white_keys = 0;
3624
3625     player->dynabomb_count = 0;
3626     player->dynabomb_size = 1;
3627     player->dynabombs_left = 0;
3628     player->dynabomb_xl = FALSE;
3629
3630     player->MovDir = initial_move_dir;
3631     player->MovPos = 0;
3632     player->GfxPos = 0;
3633     player->GfxDir = initial_move_dir;
3634     player->GfxAction = ACTION_DEFAULT;
3635     player->Frame = 0;
3636     player->StepFrame = 0;
3637
3638     player->initial_element = player->element_nr;
3639     player->artwork_element =
3640       (level.use_artwork_element[i] ? level.artwork_element[i] :
3641        player->element_nr);
3642     player->use_murphy = FALSE;
3643
3644     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3645     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3646
3647     player->gravity = level.initial_player_gravity[i];
3648
3649     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3650
3651     player->actual_frame_counter = 0;
3652
3653     player->step_counter = 0;
3654
3655     player->last_move_dir = initial_move_dir;
3656
3657     player->is_active = FALSE;
3658
3659     player->is_waiting = FALSE;
3660     player->is_moving = FALSE;
3661     player->is_auto_moving = FALSE;
3662     player->is_digging = FALSE;
3663     player->is_snapping = FALSE;
3664     player->is_collecting = FALSE;
3665     player->is_pushing = FALSE;
3666     player->is_switching = FALSE;
3667     player->is_dropping = FALSE;
3668     player->is_dropping_pressed = FALSE;
3669
3670     player->is_bored = FALSE;
3671     player->is_sleeping = FALSE;
3672
3673     player->frame_counter_bored = -1;
3674     player->frame_counter_sleeping = -1;
3675
3676     player->anim_delay_counter = 0;
3677     player->post_delay_counter = 0;
3678
3679     player->dir_waiting = initial_move_dir;
3680     player->action_waiting = ACTION_DEFAULT;
3681     player->last_action_waiting = ACTION_DEFAULT;
3682     player->special_action_bored = ACTION_DEFAULT;
3683     player->special_action_sleeping = ACTION_DEFAULT;
3684
3685     player->switch_x = -1;
3686     player->switch_y = -1;
3687
3688     player->drop_x = -1;
3689     player->drop_y = -1;
3690
3691     player->show_envelope = 0;
3692
3693     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3694
3695     player->push_delay       = -1;      /* initialized when pushing starts */
3696     player->push_delay_value = game.initial_push_delay_value;
3697
3698     player->drop_delay = 0;
3699     player->drop_pressed_delay = 0;
3700
3701     player->last_jx = -1;
3702     player->last_jy = -1;
3703     player->jx = -1;
3704     player->jy = -1;
3705
3706     player->shield_normal_time_left = 0;
3707     player->shield_deadly_time_left = 0;
3708
3709     player->inventory_infinite_element = EL_UNDEFINED;
3710     player->inventory_size = 0;
3711
3712     if (level.use_initial_inventory[i])
3713     {
3714       for (j = 0; j < level.initial_inventory_size[i]; j++)
3715       {
3716         int element = level.initial_inventory_content[i][j];
3717         int collect_count = element_info[element].collect_count_initial;
3718         int k;
3719
3720         if (!IS_CUSTOM_ELEMENT(element))
3721           collect_count = 1;
3722
3723         if (collect_count == 0)
3724           player->inventory_infinite_element = element;
3725         else
3726           for (k = 0; k < collect_count; k++)
3727             if (player->inventory_size < MAX_INVENTORY_SIZE)
3728               player->inventory_element[player->inventory_size++] = element;
3729       }
3730     }
3731
3732     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3733     SnapField(player, 0, 0);
3734
3735     player->LevelSolved = FALSE;
3736     player->GameOver = FALSE;
3737
3738     player->LevelSolved_GameWon = FALSE;
3739     player->LevelSolved_GameEnd = FALSE;
3740     player->LevelSolved_PanelOff = FALSE;
3741     player->LevelSolved_SaveTape = FALSE;
3742     player->LevelSolved_SaveScore = FALSE;
3743     player->LevelSolved_CountingTime = 0;
3744     player->LevelSolved_CountingScore = 0;
3745
3746     map_player_action[i] = i;
3747   }
3748
3749   network_player_action_received = FALSE;
3750
3751 #if defined(NETWORK_AVALIABLE)
3752   /* initial null action */
3753   if (network_playing)
3754     SendToServer_MovePlayer(MV_NONE);
3755 #endif
3756
3757   ZX = ZY = -1;
3758   ExitX = ExitY = -1;
3759
3760   FrameCounter = 0;
3761   TimeFrames = 0;
3762   TimePlayed = 0;
3763   TimeLeft = level.time;
3764   TapeTime = 0;
3765
3766   ScreenMovDir = MV_NONE;
3767   ScreenMovPos = 0;
3768   ScreenGfxPos = 0;
3769
3770   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3771
3772   AllPlayersGone = FALSE;
3773
3774   game.no_time_limit = (level.time == 0);
3775
3776   game.yamyam_content_nr = 0;
3777   game.robot_wheel_active = FALSE;
3778   game.magic_wall_active = FALSE;
3779   game.magic_wall_time_left = 0;
3780   game.light_time_left = 0;
3781   game.timegate_time_left = 0;
3782   game.switchgate_pos = 0;
3783   game.wind_direction = level.wind_direction_initial;
3784
3785 #if !USE_PLAYER_GRAVITY
3786   game.gravity = FALSE;
3787   game.explosions_delayed = TRUE;
3788 #endif
3789
3790   game.lenses_time_left = 0;
3791   game.magnify_time_left = 0;
3792
3793   game.ball_state = level.ball_state_initial;
3794   game.ball_content_nr = 0;
3795
3796   game.envelope_active = FALSE;
3797
3798   /* set focus to local player for network games, else to all players */
3799   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3800   game.centered_player_nr_next = game.centered_player_nr;
3801   game.set_centered_player = FALSE;
3802
3803   if (network_playing && tape.recording)
3804   {
3805     /* store client dependent player focus when recording network games */
3806     tape.centered_player_nr_next = game.centered_player_nr_next;
3807     tape.set_centered_player = TRUE;
3808   }
3809
3810   for (i = 0; i < NUM_BELTS; i++)
3811   {
3812     game.belt_dir[i] = MV_NONE;
3813     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3814   }
3815
3816   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3817     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3818
3819 #if DEBUG_INIT_PLAYER
3820   if (options.debug)
3821   {
3822     printf("Player status at level initialization:\n");
3823   }
3824 #endif
3825
3826   SCAN_PLAYFIELD(x, y)
3827   {
3828     Feld[x][y] = level.field[x][y];
3829     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3830     ChangeDelay[x][y] = 0;
3831     ChangePage[x][y] = -1;
3832 #if USE_NEW_CUSTOM_VALUE
3833     CustomValue[x][y] = 0;              /* initialized in InitField() */
3834 #endif
3835     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3836     AmoebaNr[x][y] = 0;
3837     WasJustMoving[x][y] = 0;
3838     WasJustFalling[x][y] = 0;
3839     CheckCollision[x][y] = 0;
3840     CheckImpact[x][y] = 0;
3841     Stop[x][y] = FALSE;
3842     Pushed[x][y] = FALSE;
3843
3844     ChangeCount[x][y] = 0;
3845     ChangeEvent[x][y] = -1;
3846
3847     ExplodePhase[x][y] = 0;
3848     ExplodeDelay[x][y] = 0;
3849     ExplodeField[x][y] = EX_TYPE_NONE;
3850
3851     RunnerVisit[x][y] = 0;
3852     PlayerVisit[x][y] = 0;
3853
3854     GfxFrame[x][y] = 0;
3855     GfxRandom[x][y] = INIT_GFX_RANDOM();
3856     GfxElement[x][y] = EL_UNDEFINED;
3857     GfxAction[x][y] = ACTION_DEFAULT;
3858     GfxDir[x][y] = MV_NONE;
3859     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3860   }
3861
3862   SCAN_PLAYFIELD(x, y)
3863   {
3864     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3865       emulate_bd = FALSE;
3866     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3867       emulate_sb = FALSE;
3868     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3869       emulate_sp = FALSE;
3870
3871     InitField(x, y, TRUE);
3872
3873     ResetGfxAnimation(x, y);
3874   }
3875
3876   InitBeltMovement();
3877
3878   for (i = 0; i < MAX_PLAYERS; i++)
3879   {
3880     struct PlayerInfo *player = &stored_player[i];
3881
3882     /* set number of special actions for bored and sleeping animation */
3883     player->num_special_action_bored =
3884       get_num_special_action(player->artwork_element,
3885                              ACTION_BORING_1, ACTION_BORING_LAST);
3886     player->num_special_action_sleeping =
3887       get_num_special_action(player->artwork_element,
3888                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3889   }
3890
3891   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3892                     emulate_sb ? EMU_SOKOBAN :
3893                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3894
3895 #if USE_NEW_ALL_SLIPPERY
3896   /* initialize type of slippery elements */
3897   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3898   {
3899     if (!IS_CUSTOM_ELEMENT(i))
3900     {
3901       /* default: elements slip down either to the left or right randomly */
3902       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3903
3904       /* SP style elements prefer to slip down on the left side */
3905       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3906         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3907
3908       /* BD style elements prefer to slip down on the left side */
3909       if (game.emulation == EMU_BOULDERDASH)
3910         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3911     }
3912   }
3913 #endif
3914
3915   /* initialize explosion and ignition delay */
3916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3917   {
3918     if (!IS_CUSTOM_ELEMENT(i))
3919     {
3920       int num_phase = 8;
3921       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3922                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3923                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3924       int last_phase = (num_phase + 1) * delay;
3925       int half_phase = (num_phase / 2) * delay;
3926
3927       element_info[i].explosion_delay = last_phase - 1;
3928       element_info[i].ignition_delay = half_phase;
3929
3930       if (i == EL_BLACK_ORB)
3931         element_info[i].ignition_delay = 1;
3932     }
3933
3934 #if 0
3935     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3936       element_info[i].explosion_delay = 1;
3937
3938     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3939       element_info[i].ignition_delay = 1;
3940 #endif
3941   }
3942
3943   /* correct non-moving belts to start moving left */
3944   for (i = 0; i < NUM_BELTS; i++)
3945     if (game.belt_dir[i] == MV_NONE)
3946       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3947
3948 #if USE_NEW_PLAYER_ASSIGNMENTS
3949   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3950   /* choose default local player */
3951   local_player = &stored_player[0];
3952
3953   for (i = 0; i < MAX_PLAYERS; i++)
3954     stored_player[i].connected = FALSE;
3955
3956   local_player->connected = TRUE;
3957   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3958
3959 #if 0
3960   printf("::: TEAM MODE: %d\n", game.team_mode);
3961 #endif
3962
3963   if (tape.playing)
3964   {
3965 #if 1
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       stored_player[i].connected = tape.player_participates[i];
3968 #else
3969     /* try to guess locally connected team mode players (needed for correct
3970        assignment of player figures from level to locally playing players) */
3971
3972     for (i = 0; i < MAX_PLAYERS; i++)
3973       if (tape.player_participates[i])
3974         stored_player[i].connected = TRUE;
3975 #endif
3976   }
3977   else if (game.team_mode && !options.network)
3978   {
3979     /* try to guess locally connected team mode players (needed for correct
3980        assignment of player figures from level to locally playing players) */
3981
3982     for (i = 0; i < MAX_PLAYERS; i++)
3983       if (setup.input[i].use_joystick ||
3984           setup.input[i].key.left != KSYM_UNDEFINED)
3985         stored_player[i].connected = TRUE;
3986   }
3987
3988 #if DEBUG_INIT_PLAYER
3989   if (options.debug)
3990   {
3991     printf("Player status after level initialization:\n");
3992
3993     for (i = 0; i < MAX_PLAYERS; i++)
3994     {
3995       struct PlayerInfo *player = &stored_player[i];
3996
3997       printf("- player %d: present == %d, connected == %d, active == %d",
3998              i + 1,
3999              player->present,
4000              player->connected,
4001              player->active);
4002
4003       if (local_player == player)
4004         printf(" (local player)");
4005
4006       printf("\n");
4007     }
4008   }
4009 #endif
4010
4011 #if DEBUG_INIT_PLAYER
4012   if (options.debug)
4013     printf("Reassigning players ...\n");
4014 #endif
4015
4016   /* check if any connected player was not found in playfield */
4017   for (i = 0; i < MAX_PLAYERS; i++)
4018   {
4019     struct PlayerInfo *player = &stored_player[i];
4020
4021     if (player->connected && !player->present)
4022     {
4023       struct PlayerInfo *field_player = NULL;
4024
4025 #if DEBUG_INIT_PLAYER
4026       if (options.debug)
4027         printf("- looking for field player for player %d ...\n", i + 1);
4028 #endif
4029
4030       /* assign first free player found that is present in the playfield */
4031
4032 #if 1
4033       /* first try: look for unmapped playfield player that is not connected */
4034       for (j = 0; j < MAX_PLAYERS; j++)
4035         if (field_player == NULL &&
4036             stored_player[j].present &&
4037             !stored_player[j].mapped &&
4038             !stored_player[j].connected)
4039           field_player = &stored_player[j];
4040
4041       /* second try: look for *any* unmapped playfield player */
4042       for (j = 0; j < MAX_PLAYERS; j++)
4043         if (field_player == NULL &&
4044             stored_player[j].present &&
4045             !stored_player[j].mapped)
4046           field_player = &stored_player[j];
4047 #else
4048       /* first try: look for unmapped playfield player that is not connected */
4049       if (field_player == NULL)
4050         for (j = 0; j < MAX_PLAYERS; j++)
4051           if (stored_player[j].present &&
4052               !stored_player[j].mapped &&
4053               !stored_player[j].connected)
4054             field_player = &stored_player[j];
4055
4056       /* second try: look for *any* unmapped playfield player */
4057       if (field_player == NULL)
4058         for (j = 0; j < MAX_PLAYERS; j++)
4059           if (stored_player[j].present &&
4060               !stored_player[j].mapped)
4061             field_player = &stored_player[j];
4062 #endif
4063
4064       if (field_player != NULL)
4065       {
4066         int jx = field_player->jx, jy = field_player->jy;
4067
4068 #if DEBUG_INIT_PLAYER
4069         if (options.debug)
4070           printf("- found player %d\n", field_player->index_nr + 1);
4071 #endif
4072
4073         player->present = FALSE;
4074         player->active = FALSE;
4075
4076         field_player->present = TRUE;
4077         field_player->active = TRUE;
4078
4079         /*
4080         player->initial_element = field_player->initial_element;
4081         player->artwork_element = field_player->artwork_element;
4082
4083         player->block_last_field       = field_player->block_last_field;
4084         player->block_delay_adjustment = field_player->block_delay_adjustment;
4085         */
4086
4087         StorePlayer[jx][jy] = field_player->element_nr;
4088
4089         field_player->jx = field_player->last_jx = jx;
4090         field_player->jy = field_player->last_jy = jy;
4091
4092         if (local_player == player)
4093           local_player = field_player;
4094
4095         map_player_action[field_player->index_nr] = i;
4096
4097         field_player->mapped = TRUE;
4098
4099 #if DEBUG_INIT_PLAYER
4100         if (options.debug)
4101           printf("- map_player_action[%d] == %d\n",
4102                  field_player->index_nr + 1, i + 1);
4103 #endif
4104       }
4105     }
4106
4107     if (player->connected && player->present)
4108       player->mapped = TRUE;
4109   }
4110
4111 #if DEBUG_INIT_PLAYER
4112   if (options.debug)
4113   {
4114     printf("Player status after player assignment (first stage):\n");
4115
4116     for (i = 0; i < MAX_PLAYERS; i++)
4117     {
4118       struct PlayerInfo *player = &stored_player[i];
4119
4120       printf("- player %d: present == %d, connected == %d, active == %d",
4121              i + 1,
4122              player->present,
4123              player->connected,
4124              player->active);
4125
4126       if (local_player == player)
4127         printf(" (local player)");
4128
4129       printf("\n");
4130     }
4131   }
4132 #endif
4133
4134 #else
4135
4136   /* check if any connected player was not found in playfield */
4137   for (i = 0; i < MAX_PLAYERS; i++)
4138   {
4139     struct PlayerInfo *player = &stored_player[i];
4140
4141     if (player->connected && !player->present)
4142     {
4143       for (j = 0; j < MAX_PLAYERS; j++)
4144       {
4145         struct PlayerInfo *field_player = &stored_player[j];
4146         int jx = field_player->jx, jy = field_player->jy;
4147
4148         /* assign first free player found that is present in the playfield */
4149         if (field_player->present && !field_player->connected)
4150         {
4151           player->present = TRUE;
4152           player->active = TRUE;
4153
4154           field_player->present = FALSE;
4155           field_player->active = FALSE;
4156
4157           player->initial_element = field_player->initial_element;
4158           player->artwork_element = field_player->artwork_element;
4159
4160           player->block_last_field       = field_player->block_last_field;
4161           player->block_delay_adjustment = field_player->block_delay_adjustment;
4162
4163           StorePlayer[jx][jy] = player->element_nr;
4164
4165           player->jx = player->last_jx = jx;
4166           player->jy = player->last_jy = jy;
4167
4168           break;
4169         }
4170       }
4171     }
4172   }
4173 #endif
4174
4175 #if 0
4176   printf("::: local_player->present == %d\n", local_player->present);
4177 #endif
4178
4179   if (tape.playing)
4180   {
4181     /* when playing a tape, eliminate all players who do not participate */
4182
4183 #if USE_NEW_PLAYER_ASSIGNMENTS
4184
4185 #if 1
4186     if (!game.team_mode)
4187 #endif
4188
4189     for (i = 0; i < MAX_PLAYERS; i++)
4190     {
4191       if (stored_player[i].active &&
4192           !tape.player_participates[map_player_action[i]])
4193       {
4194         struct PlayerInfo *player = &stored_player[i];
4195         int jx = player->jx, jy = player->jy;
4196
4197 #if DEBUG_INIT_PLAYER
4198         if (options.debug)
4199           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4200 #endif
4201
4202         player->active = FALSE;
4203         StorePlayer[jx][jy] = 0;
4204         Feld[jx][jy] = EL_EMPTY;
4205       }
4206     }
4207
4208 #else
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       if (stored_player[i].active &&
4213           !tape.player_participates[i])
4214       {
4215         struct PlayerInfo *player = &stored_player[i];
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         StorePlayer[jx][jy] = 0;
4220         Feld[jx][jy] = EL_EMPTY;
4221       }
4222     }
4223 #endif
4224   }
4225   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4226   {
4227     /* when in single player mode, eliminate all but the first active player */
4228
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active)
4232       {
4233         for (j = i + 1; j < MAX_PLAYERS; j++)
4234         {
4235           if (stored_player[j].active)
4236           {
4237             struct PlayerInfo *player = &stored_player[j];
4238             int jx = player->jx, jy = player->jy;
4239
4240             player->active = FALSE;
4241             player->present = FALSE;
4242
4243             StorePlayer[jx][jy] = 0;
4244             Feld[jx][jy] = EL_EMPTY;
4245           }
4246         }
4247       }
4248     }
4249   }
4250
4251   /* when recording the game, store which players take part in the game */
4252   if (tape.recording)
4253   {
4254 #if USE_NEW_PLAYER_ASSIGNMENTS
4255     for (i = 0; i < MAX_PLAYERS; i++)
4256       if (stored_player[i].connected)
4257         tape.player_participates[i] = TRUE;
4258 #else
4259     for (i = 0; i < MAX_PLAYERS; i++)
4260       if (stored_player[i].active)
4261         tape.player_participates[i] = TRUE;
4262 #endif
4263   }
4264
4265 #if DEBUG_INIT_PLAYER
4266   if (options.debug)
4267   {
4268     printf("Player status after player assignment (final stage):\n");
4269
4270     for (i = 0; i < MAX_PLAYERS; i++)
4271     {
4272       struct PlayerInfo *player = &stored_player[i];
4273
4274       printf("- player %d: present == %d, connected == %d, active == %d",
4275              i + 1,
4276              player->present,
4277              player->connected,
4278              player->active);
4279
4280       if (local_player == player)
4281         printf(" (local player)");
4282
4283       printf("\n");
4284     }
4285   }
4286 #endif
4287
4288   if (BorderElement == EL_EMPTY)
4289   {
4290     SBX_Left = 0;
4291     SBX_Right = lev_fieldx - SCR_FIELDX;
4292     SBY_Upper = 0;
4293     SBY_Lower = lev_fieldy - SCR_FIELDY;
4294   }
4295   else
4296   {
4297     SBX_Left = -1;
4298     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4299     SBY_Upper = -1;
4300     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4301   }
4302
4303 #if NEW_TILESIZE
4304
4305   // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
4306   // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
4307
4308 #if 1
4309   if (full_lev_fieldx <= SCR_FIELDX)
4310     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4311
4312   if (full_lev_fieldy <= SCR_FIELDY)
4313     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4314 #else
4315   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320 #endif
4321
4322   /*
4323   printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
4324          SBX_Right - SBX_Left + 1);
4325   */
4326
4327 #if 1
4328   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4329     SBX_Left--;
4330   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4331     SBY_Upper--;
4332 #else
4333   if (EVEN(SCR_FIELDX))
4334     SBX_Left--;
4335   if (EVEN(SCR_FIELDY))
4336     SBY_Upper--;
4337 #endif
4338
4339 #if 0
4340   printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
4341   printf("\n");
4342 #endif
4343
4344 #else
4345
4346   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4347     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4348
4349   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4350     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4351 #endif
4352
4353   /* if local player not found, look for custom element that might create
4354      the player (make some assumptions about the right custom element) */
4355   if (!local_player->present)
4356   {
4357     int start_x = 0, start_y = 0;
4358     int found_rating = 0;
4359     int found_element = EL_UNDEFINED;
4360     int player_nr = local_player->index_nr;
4361
4362     SCAN_PLAYFIELD(x, y)
4363     {
4364       int element = Feld[x][y];
4365       int content;
4366       int xx, yy;
4367       boolean is_player;
4368
4369       if (level.use_start_element[player_nr] &&
4370           level.start_element[player_nr] == element &&
4371           found_rating < 4)
4372       {
4373         start_x = x;
4374         start_y = y;
4375
4376         found_rating = 4;
4377         found_element = element;
4378       }
4379
4380       if (!IS_CUSTOM_ELEMENT(element))
4381         continue;
4382
4383       if (CAN_CHANGE(element))
4384       {
4385         for (i = 0; i < element_info[element].num_change_pages; i++)
4386         {
4387           /* check for player created from custom element as single target */
4388           content = element_info[element].change_page[i].target_element;
4389           is_player = ELEM_IS_PLAYER(content);
4390
4391           if (is_player && (found_rating < 3 ||
4392                             (found_rating == 3 && element < found_element)))
4393           {
4394             start_x = x;
4395             start_y = y;
4396
4397             found_rating = 3;
4398             found_element = element;
4399           }
4400         }
4401       }
4402
4403       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4404       {
4405         /* check for player created from custom element as explosion content */
4406         content = element_info[element].content.e[xx][yy];
4407         is_player = ELEM_IS_PLAYER(content);
4408
4409         if (is_player && (found_rating < 2 ||
4410                           (found_rating == 2 && element < found_element)))
4411         {
4412           start_x = x + xx - 1;
4413           start_y = y + yy - 1;
4414
4415           found_rating = 2;
4416           found_element = element;
4417         }
4418
4419         if (!CAN_CHANGE(element))
4420           continue;
4421
4422         for (i = 0; i < element_info[element].num_change_pages; i++)
4423         {
4424           /* check for player created from custom element as extended target */
4425           content =
4426             element_info[element].change_page[i].target_content.e[xx][yy];
4427
4428           is_player = ELEM_IS_PLAYER(content);
4429
4430           if (is_player && (found_rating < 1 ||
4431                             (found_rating == 1 && element < found_element)))
4432           {
4433             start_x = x + xx - 1;
4434             start_y = y + yy - 1;
4435
4436             found_rating = 1;
4437             found_element = element;
4438           }
4439         }
4440       }
4441     }
4442
4443     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4444                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4445                 start_x - MIDPOSX);
4446
4447     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4448                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4449                 start_y - MIDPOSY);
4450   }
4451   else
4452   {
4453     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4454                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4455                 local_player->jx - MIDPOSX);
4456
4457     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4458                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4459                 local_player->jy - MIDPOSY);
4460   }
4461
4462 #if 0
4463   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4464 #endif
4465
4466 #if 0
4467   /* do not use PLAYING mask for fading out from main screen */
4468   game_status = GAME_MODE_MAIN;
4469 #endif
4470
4471 #if 0
4472
4473   StopAnimation();
4474
4475   if (!game.restart_level)
4476     CloseDoor(DOOR_CLOSE_1);
4477
4478 #if 1
4479   if (level_editor_test_game)
4480     FadeSkipNextFadeIn();
4481   else
4482     FadeSetEnterScreen();
4483 #else
4484   if (level_editor_test_game)
4485     fading = fading_none;
4486   else
4487     fading = menu.destination;
4488 #endif
4489
4490 #if 1
4491   FadeOut(REDRAW_FIELD);
4492 #else
4493   if (do_fading)
4494     FadeOut(REDRAW_FIELD);
4495 #endif
4496
4497 #endif
4498
4499 #if 0
4500   game_status = GAME_MODE_PLAYING;
4501 #endif
4502
4503   /* !!! FIX THIS (START) !!! */
4504   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4505   {
4506     InitGameEngine_EM();
4507
4508 #if 0
4509     /* blit playfield from scroll buffer to normal back buffer for fading in */
4510     BlitScreenToBitmap_EM(backbuffer);
4511 #endif
4512   }
4513   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4514   {
4515     InitGameEngine_SP();
4516
4517 #if 0
4518     /* blit playfield from scroll buffer to normal back buffer for fading in */
4519     BlitScreenToBitmap_SP(backbuffer);
4520 #endif
4521   }
4522   else
4523   {
4524     DrawLevel(REDRAW_FIELD);
4525     DrawAllPlayers();
4526
4527     /* after drawing the level, correct some elements */
4528     if (game.timegate_time_left == 0)
4529       CloseAllOpenTimegates();
4530
4531 #if 0
4532     /* blit playfield from scroll buffer to normal back buffer for fading in */
4533 #if NEW_TILESIZE
4534     BlitScreenToBitmap(backbuffer);
4535 #else
4536     if (setup.soft_scrolling)
4537       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4538 #endif
4539 #endif
4540
4541 #if 0
4542     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4543 #endif
4544   }
4545 #if 1
4546   /* blit playfield from scroll buffer to normal back buffer for fading in */
4547   BlitScreenToBitmap(backbuffer);
4548
4549   redraw_mask |= REDRAW_FROM_BACKBUFFER;
4550 #endif
4551   /* !!! FIX THIS (END) !!! */
4552
4553 #if 1
4554   FadeIn(REDRAW_FIELD);
4555 #else
4556   if (do_fading)
4557     FadeIn(REDRAW_FIELD);
4558
4559   BackToFront();
4560 #endif
4561
4562   if (!game.restart_level)
4563   {
4564     /* copy default game door content to main double buffer */
4565 #if 1
4566 #if 1
4567     /* !!! CHECK AGAIN !!! */
4568     SetPanelBackground();
4569     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4570     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4571 #else
4572     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4573
4574     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4575     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4576     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4577                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4578 #endif
4579 #else
4580     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4581                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4582 #endif
4583   }
4584
4585   SetPanelBackground();
4586   SetDrawBackgroundMask(REDRAW_DOOR_1);
4587
4588 #if 1
4589   UpdateAndDisplayGameControlValues();
4590 #else
4591   UpdateGameDoorValues();
4592   DrawGameDoorValues();
4593 #endif
4594
4595   if (!game.restart_level)
4596   {
4597     UnmapGameButtons();
4598     UnmapTapeButtons();
4599     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4600     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4601     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4602     MapGameButtons();
4603     MapTapeButtons();
4604
4605     /* copy actual game door content to door double buffer for OpenDoor() */
4606 #if 1
4607     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4608 #else
4609     BlitBitmap(drawto, bitmap_db_door,
4610                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4611 #endif
4612
4613     OpenDoor(DOOR_OPEN_ALL);
4614
4615     PlaySound(SND_GAME_STARTING);
4616
4617     if (setup.sound_music)
4618       PlayLevelMusic();
4619
4620     KeyboardAutoRepeatOffUnlessAutoplay();
4621
4622 #if DEBUG_INIT_PLAYER
4623     if (options.debug)
4624     {
4625       printf("Player status (final):\n");
4626
4627       for (i = 0; i < MAX_PLAYERS; i++)
4628       {
4629         struct PlayerInfo *player = &stored_player[i];
4630
4631         printf("- player %d: present == %d, connected == %d, active == %d",
4632                i + 1,
4633                player->present,
4634                player->connected,
4635                player->active);
4636
4637         if (local_player == player)
4638           printf(" (local player)");
4639
4640         printf("\n");
4641       }
4642     }
4643 #endif
4644   }
4645
4646 #if 1
4647   UnmapAllGadgets();
4648
4649   MapGameButtons();
4650   MapTapeButtons();
4651 #endif
4652
4653   if (!game.restart_level && !tape.playing)
4654   {
4655     LevelStats_incPlayed(level_nr);
4656
4657     SaveLevelSetup_SeriesInfo();
4658
4659 #if 0
4660     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4661 #endif
4662   }
4663
4664   game.restart_level = FALSE;
4665 }
4666
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4668 {
4669   /* this is used for non-R'n'D game engines to update certain engine values */
4670
4671   /* needed to determine if sounds are played within the visible screen area */
4672   scroll_x = actual_scroll_x;
4673   scroll_y = actual_scroll_y;
4674 }
4675
4676 void InitMovDir(int x, int y)
4677 {
4678   int i, element = Feld[x][y];
4679   static int xy[4][2] =
4680   {
4681     {  0, +1 },
4682     { +1,  0 },
4683     {  0, -1 },
4684     { -1,  0 }
4685   };
4686   static int direction[3][4] =
4687   {
4688     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4689     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4690     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4691   };
4692
4693   switch (element)
4694   {
4695     case EL_BUG_RIGHT:
4696     case EL_BUG_UP:
4697     case EL_BUG_LEFT:
4698     case EL_BUG_DOWN:
4699       Feld[x][y] = EL_BUG;
4700       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4701       break;
4702
4703     case EL_SPACESHIP_RIGHT:
4704     case EL_SPACESHIP_UP:
4705     case EL_SPACESHIP_LEFT:
4706     case EL_SPACESHIP_DOWN:
4707       Feld[x][y] = EL_SPACESHIP;
4708       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4709       break;
4710
4711     case EL_BD_BUTTERFLY_RIGHT:
4712     case EL_BD_BUTTERFLY_UP:
4713     case EL_BD_BUTTERFLY_LEFT:
4714     case EL_BD_BUTTERFLY_DOWN:
4715       Feld[x][y] = EL_BD_BUTTERFLY;
4716       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4717       break;
4718
4719     case EL_BD_FIREFLY_RIGHT:
4720     case EL_BD_FIREFLY_UP:
4721     case EL_BD_FIREFLY_LEFT:
4722     case EL_BD_FIREFLY_DOWN:
4723       Feld[x][y] = EL_BD_FIREFLY;
4724       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4725       break;
4726
4727     case EL_PACMAN_RIGHT:
4728     case EL_PACMAN_UP:
4729     case EL_PACMAN_LEFT:
4730     case EL_PACMAN_DOWN:
4731       Feld[x][y] = EL_PACMAN;
4732       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4733       break;
4734
4735     case EL_YAMYAM_LEFT:
4736     case EL_YAMYAM_RIGHT:
4737     case EL_YAMYAM_UP:
4738     case EL_YAMYAM_DOWN:
4739       Feld[x][y] = EL_YAMYAM;
4740       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4741       break;
4742
4743     case EL_SP_SNIKSNAK:
4744       MovDir[x][y] = MV_UP;
4745       break;
4746
4747     case EL_SP_ELECTRON:
4748       MovDir[x][y] = MV_LEFT;
4749       break;
4750
4751     case EL_MOLE_LEFT:
4752     case EL_MOLE_RIGHT:
4753     case EL_MOLE_UP:
4754     case EL_MOLE_DOWN:
4755       Feld[x][y] = EL_MOLE;
4756       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4757       break;
4758
4759     default:
4760       if (IS_CUSTOM_ELEMENT(element))
4761       {
4762         struct ElementInfo *ei = &element_info[element];
4763         int move_direction_initial = ei->move_direction_initial;
4764         int move_pattern = ei->move_pattern;
4765
4766         if (move_direction_initial == MV_START_PREVIOUS)
4767         {
4768           if (MovDir[x][y] != MV_NONE)
4769             return;
4770
4771           move_direction_initial = MV_START_AUTOMATIC;
4772         }
4773
4774         if (move_direction_initial == MV_START_RANDOM)
4775           MovDir[x][y] = 1 << RND(4);
4776         else if (move_direction_initial & MV_ANY_DIRECTION)
4777           MovDir[x][y] = move_direction_initial;
4778         else if (move_pattern == MV_ALL_DIRECTIONS ||
4779                  move_pattern == MV_TURNING_LEFT ||
4780                  move_pattern == MV_TURNING_RIGHT ||
4781                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4782                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4783                  move_pattern == MV_TURNING_RANDOM)
4784           MovDir[x][y] = 1 << RND(4);
4785         else if (move_pattern == MV_HORIZONTAL)
4786           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787         else if (move_pattern == MV_VERTICAL)
4788           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789         else if (move_pattern & MV_ANY_DIRECTION)
4790           MovDir[x][y] = element_info[element].move_pattern;
4791         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792                  move_pattern == MV_ALONG_RIGHT_SIDE)
4793         {
4794           /* use random direction as default start direction */
4795           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796             MovDir[x][y] = 1 << RND(4);
4797
4798           for (i = 0; i < NUM_DIRECTIONS; i++)
4799           {
4800             int x1 = x + xy[i][0];
4801             int y1 = y + xy[i][1];
4802
4803             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4804             {
4805               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806                 MovDir[x][y] = direction[0][i];
4807               else
4808                 MovDir[x][y] = direction[1][i];
4809
4810               break;
4811             }
4812           }
4813         }                
4814       }
4815       else
4816       {
4817         MovDir[x][y] = 1 << RND(4);
4818
4819         if (element != EL_BUG &&
4820             element != EL_SPACESHIP &&
4821             element != EL_BD_BUTTERFLY &&
4822             element != EL_BD_FIREFLY)
4823           break;
4824
4825         for (i = 0; i < NUM_DIRECTIONS; i++)
4826         {
4827           int x1 = x + xy[i][0];
4828           int y1 = y + xy[i][1];
4829
4830           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4831           {
4832             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4833             {
4834               MovDir[x][y] = direction[0][i];
4835               break;
4836             }
4837             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4839             {
4840               MovDir[x][y] = direction[1][i];
4841               break;
4842             }
4843           }
4844         }
4845       }
4846       break;
4847   }
4848
4849   GfxDir[x][y] = MovDir[x][y];
4850 }
4851
4852 void InitAmoebaNr(int x, int y)
4853 {
4854   int i;
4855   int group_nr = AmoebeNachbarNr(x, y);
4856
4857   if (group_nr == 0)
4858   {
4859     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4860     {
4861       if (AmoebaCnt[i] == 0)
4862       {
4863         group_nr = i;
4864         break;
4865       }
4866     }
4867   }
4868
4869   AmoebaNr[x][y] = group_nr;
4870   AmoebaCnt[group_nr]++;
4871   AmoebaCnt2[group_nr]++;
4872 }
4873
4874 static void PlayerWins(struct PlayerInfo *player)
4875 {
4876   player->LevelSolved = TRUE;
4877   player->GameOver = TRUE;
4878
4879   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880                          level.native_em_level->lev->score : player->score);
4881
4882   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4883                                       TimeLeft);
4884   player->LevelSolved_CountingScore = player->score_final;
4885 }
4886
4887 void GameWon()
4888 {
4889   static int time, time_final;
4890   static int score, score_final;
4891   static int game_over_delay_1 = 0;
4892   static int game_over_delay_2 = 0;
4893   int game_over_delay_value_1 = 50;
4894   int game_over_delay_value_2 = 50;
4895
4896   if (!local_player->LevelSolved_GameWon)
4897   {
4898     int i;
4899
4900     /* do not start end game actions before the player stops moving (to exit) */
4901     if (local_player->MovPos)
4902       return;
4903
4904     local_player->LevelSolved_GameWon = TRUE;
4905     local_player->LevelSolved_SaveTape = tape.recording;
4906     local_player->LevelSolved_SaveScore = !tape.playing;
4907
4908     if (!tape.playing)
4909     {
4910       LevelStats_incSolved(level_nr);
4911
4912       SaveLevelSetup_SeriesInfo();
4913
4914 #if 0
4915       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4916 #endif
4917     }
4918
4919     if (tape.auto_play)         /* tape might already be stopped here */
4920       tape.auto_play_level_solved = TRUE;
4921
4922 #if 1
4923     TapeStop();
4924 #endif
4925
4926     game_over_delay_1 = game_over_delay_value_1;
4927     game_over_delay_2 = game_over_delay_value_2;
4928
4929     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930     score = score_final = local_player->score_final;
4931
4932     if (TimeLeft > 0)
4933     {
4934       time_final = 0;
4935       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4936     }
4937     else if (game.no_time_limit && TimePlayed < 999)
4938     {
4939       time_final = 999;
4940       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4941     }
4942
4943     local_player->score_final = score_final;
4944
4945     if (level_editor_test_game)
4946     {
4947       time = time_final;
4948       score = score_final;
4949
4950 #if 1
4951       local_player->LevelSolved_CountingTime = time;
4952       local_player->LevelSolved_CountingScore = score;
4953
4954       game_panel_controls[GAME_PANEL_TIME].value = time;
4955       game_panel_controls[GAME_PANEL_SCORE].value = score;
4956
4957       DisplayGameControlValues();
4958 #else
4959       DrawGameValue_Time(time);
4960       DrawGameValue_Score(score);
4961 #endif
4962     }
4963
4964     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4965     {
4966       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4967       {
4968         /* close exit door after last player */
4969         if ((AllPlayersGone &&
4970              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4975         {
4976           int element = Feld[ExitX][ExitY];
4977
4978 #if 0
4979           if (element == EL_EM_EXIT_OPEN ||
4980               element == EL_EM_STEEL_EXIT_OPEN)
4981           {
4982             Bang(ExitX, ExitY);
4983           }
4984           else
4985 #endif
4986           {
4987             Feld[ExitX][ExitY] =
4988               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4989                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4990                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4991                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4992                EL_EM_STEEL_EXIT_CLOSING);
4993
4994             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4995           }
4996         }
4997
4998         /* player disappears */
4999         DrawLevelField(ExitX, ExitY);
5000       }
5001
5002       for (i = 0; i < MAX_PLAYERS; i++)
5003       {
5004         struct PlayerInfo *player = &stored_player[i];
5005
5006         if (player->present)
5007         {
5008           RemovePlayer(player);
5009
5010           /* player disappears */
5011           DrawLevelField(player->jx, player->jy);
5012         }
5013       }
5014     }
5015
5016     PlaySound(SND_GAME_WINNING);
5017   }
5018
5019   if (game_over_delay_1 > 0)
5020   {
5021     game_over_delay_1--;
5022
5023     return;
5024   }
5025
5026   if (time != time_final)
5027   {
5028     int time_to_go = ABS(time_final - time);
5029     int time_count_dir = (time < time_final ? +1 : -1);
5030     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5031
5032     time  += time_count_steps * time_count_dir;
5033     score += time_count_steps * level.score[SC_TIME_BONUS];
5034
5035 #if 1
5036     local_player->LevelSolved_CountingTime = time;
5037     local_player->LevelSolved_CountingScore = score;
5038
5039     game_panel_controls[GAME_PANEL_TIME].value = time;
5040     game_panel_controls[GAME_PANEL_SCORE].value = score;
5041
5042     DisplayGameControlValues();
5043 #else
5044     DrawGameValue_Time(time);
5045     DrawGameValue_Score(score);
5046 #endif
5047
5048     if (time == time_final)
5049       StopSound(SND_GAME_LEVELTIME_BONUS);
5050     else if (setup.sound_loops)
5051       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5052     else
5053       PlaySound(SND_GAME_LEVELTIME_BONUS);
5054
5055     return;
5056   }
5057
5058   local_player->LevelSolved_PanelOff = TRUE;
5059
5060   if (game_over_delay_2 > 0)
5061   {
5062     game_over_delay_2--;
5063
5064     return;
5065   }
5066
5067 #if 1
5068   GameEnd();
5069 #endif
5070 }
5071
5072 void GameEnd()
5073 {
5074   int hi_pos;
5075   boolean raise_level = FALSE;
5076
5077   local_player->LevelSolved_GameEnd = TRUE;
5078
5079   CloseDoor(DOOR_CLOSE_1);
5080
5081   if (local_player->LevelSolved_SaveTape)
5082   {
5083 #if 0
5084     TapeStop();
5085 #endif
5086
5087 #if 1
5088     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5089 #else
5090     SaveTape(tape.level_nr);            /* ask to save tape */
5091 #endif
5092   }
5093
5094   if (level_editor_test_game)
5095   {
5096     game_status = GAME_MODE_MAIN;
5097
5098 #if 1
5099     DrawAndFadeInMainMenu(REDRAW_FIELD);
5100 #else
5101     DrawMainMenu();
5102 #endif
5103
5104     return;
5105   }
5106
5107   if (!local_player->LevelSolved_SaveScore)
5108   {
5109 #if 1
5110     FadeOut(REDRAW_FIELD);
5111 #endif
5112
5113     game_status = GAME_MODE_MAIN;
5114
5115     DrawAndFadeInMainMenu(REDRAW_FIELD);
5116
5117     return;
5118   }
5119
5120   if (level_nr == leveldir_current->handicap_level)
5121   {
5122     leveldir_current->handicap_level++;
5123
5124     SaveLevelSetup_SeriesInfo();
5125   }
5126
5127   if (level_nr < leveldir_current->last_level)
5128     raise_level = TRUE;                 /* advance to next level */
5129
5130   if ((hi_pos = NewHiScore()) >= 0) 
5131   {
5132     game_status = GAME_MODE_SCORES;
5133
5134     DrawHallOfFame(hi_pos);
5135
5136     if (raise_level)
5137     {
5138       level_nr++;
5139       TapeErase();
5140     }
5141   }
5142   else
5143   {
5144 #if 1
5145     FadeOut(REDRAW_FIELD);
5146 #endif
5147
5148     game_status = GAME_MODE_MAIN;
5149
5150     if (raise_level)
5151     {
5152       level_nr++;
5153       TapeErase();
5154     }
5155
5156     DrawAndFadeInMainMenu(REDRAW_FIELD);
5157   }
5158 }
5159
5160 int NewHiScore()
5161 {
5162   int k, l;
5163   int position = -1;
5164
5165   LoadScore(level_nr);
5166
5167   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5169     return -1;
5170
5171   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5172   {
5173     if (local_player->score_final > highscore[k].Score)
5174     {
5175       /* player has made it to the hall of fame */
5176
5177       if (k < MAX_SCORE_ENTRIES - 1)
5178       {
5179         int m = MAX_SCORE_ENTRIES - 1;
5180
5181 #ifdef ONE_PER_NAME
5182         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183           if (strEqual(setup.player_name, highscore[l].Name))
5184             m = l;
5185         if (m == k)     /* player's new highscore overwrites his old one */
5186           goto put_into_list;
5187 #endif
5188
5189         for (l = m; l > k; l--)
5190         {
5191           strcpy(highscore[l].Name, highscore[l - 1].Name);
5192           highscore[l].Score = highscore[l - 1].Score;
5193         }
5194       }
5195
5196 #ifdef ONE_PER_NAME
5197       put_into_list:
5198 #endif
5199       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201       highscore[k].Score = local_player->score_final; 
5202       position = k;
5203       break;
5204     }
5205
5206 #ifdef ONE_PER_NAME
5207     else if (!strncmp(setup.player_name, highscore[k].Name,
5208                       MAX_PLAYER_NAME_LEN))
5209       break;    /* player already there with a higher score */
5210 #endif
5211
5212   }
5213
5214   if (position >= 0) 
5215     SaveScore(level_nr);
5216
5217   return position;
5218 }
5219
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5221 {
5222   int element = Feld[x][y];
5223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5225   int horiz_move = (dx != 0);
5226   int sign = (horiz_move ? dx : dy);
5227   int step = sign * element_info[element].move_stepsize;
5228
5229   /* special values for move stepsize for spring and things on conveyor belt */
5230   if (horiz_move)
5231   {
5232     if (CAN_FALL(element) &&
5233         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235     else if (element == EL_SPRING)
5236       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5237   }
5238
5239   return step;
5240 }
5241
5242 inline static int getElementMoveStepsize(int x, int y)
5243 {
5244   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5245 }
5246
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5248 {
5249   if (player->GfxAction != action || player->GfxDir != dir)
5250   {
5251 #if 0
5252     printf("Player frame reset! (%d => %d, %d => %d)\n",
5253            player->GfxAction, action, player->GfxDir, dir);
5254 #endif
5255
5256     player->GfxAction = action;
5257     player->GfxDir = dir;
5258     player->Frame = 0;
5259     player->StepFrame = 0;
5260   }
5261 }
5262
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5265 {
5266   int element = Feld[x][y];
5267   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268   int last_gfx_frame = GfxFrame[x][y];
5269
5270   if (graphic_info[graphic].anim_global_sync)
5271     GfxFrame[x][y] = FrameCounter;
5272   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273     GfxFrame[x][y] = CustomValue[x][y];
5274   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275     GfxFrame[x][y] = element_info[element].collect_score;
5276   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277     GfxFrame[x][y] = ChangeDelay[x][y];
5278
5279   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280     DrawLevelGraphicAnimation(x, y, graphic);
5281 }
5282 #endif
5283
5284 static void ResetGfxAnimation(int x, int y)
5285 {
5286   GfxAction[x][y] = ACTION_DEFAULT;
5287   GfxDir[x][y] = MovDir[x][y];
5288   GfxFrame[x][y] = 0;
5289
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291   ResetGfxFrame(x, y, FALSE);
5292 #endif
5293 }
5294
5295 static void ResetRandomAnimationValue(int x, int y)
5296 {
5297   GfxRandom[x][y] = INIT_GFX_RANDOM();
5298 }
5299
5300 void InitMovingField(int x, int y, int direction)
5301 {
5302   int element = Feld[x][y];
5303   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5305   int newx = x + dx;
5306   int newy = y + dy;
5307   boolean is_moving_before, is_moving_after;
5308 #if 0
5309   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5310 #endif
5311
5312   /* check if element was/is moving or being moved before/after mode change */
5313 #if 1
5314 #if 1
5315   is_moving_before = (WasJustMoving[x][y] != 0);
5316 #else
5317   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318   is_moving_before = WasJustMoving[x][y];
5319 #endif
5320 #else
5321   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5322 #endif
5323   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5324
5325   /* reset animation only for moving elements which change direction of moving
5326      or which just started or stopped moving
5327      (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5329 #if 1
5330   if (is_moving_before != is_moving_after ||
5331       direction != MovDir[x][y])
5332     ResetGfxAnimation(x, y);
5333 #else
5334   if ((is_moving_before || is_moving_after) && !continues_moving)
5335     ResetGfxAnimation(x, y);
5336 #endif
5337 #else
5338   if (!continues_moving)
5339     ResetGfxAnimation(x, y);
5340 #endif
5341
5342   MovDir[x][y] = direction;
5343   GfxDir[x][y] = direction;
5344
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347                      direction == MV_DOWN && CAN_FALL(element) ?
5348                      ACTION_FALLING : ACTION_MOVING);
5349 #else
5350   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351                      ACTION_FALLING : ACTION_MOVING);
5352 #endif
5353
5354   /* this is needed for CEs with property "can move" / "not moving" */
5355
5356   if (is_moving_after)
5357   {
5358     if (Feld[newx][newy] == EL_EMPTY)
5359       Feld[newx][newy] = EL_BLOCKED;
5360
5361     MovDir[newx][newy] = MovDir[x][y];
5362
5363 #if USE_NEW_CUSTOM_VALUE
5364     CustomValue[newx][newy] = CustomValue[x][y];
5365 #endif
5366
5367     GfxFrame[newx][newy] = GfxFrame[x][y];
5368     GfxRandom[newx][newy] = GfxRandom[x][y];
5369     GfxAction[newx][newy] = GfxAction[x][y];
5370     GfxDir[newx][newy] = GfxDir[x][y];
5371   }
5372 }
5373
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5375 {
5376   int direction = MovDir[x][y];
5377   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5379
5380   *goes_to_x = newx;
5381   *goes_to_y = newy;
5382 }
5383
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5385 {
5386   int oldx = x, oldy = y;
5387   int direction = MovDir[x][y];
5388
5389   if (direction == MV_LEFT)
5390     oldx++;
5391   else if (direction == MV_RIGHT)
5392     oldx--;
5393   else if (direction == MV_UP)
5394     oldy++;
5395   else if (direction == MV_DOWN)
5396     oldy--;
5397
5398   *comes_from_x = oldx;
5399   *comes_from_y = oldy;
5400 }
5401
5402 int MovingOrBlocked2Element(int x, int y)
5403 {
5404   int element = Feld[x][y];
5405
5406   if (element == EL_BLOCKED)
5407   {
5408     int oldx, oldy;
5409
5410     Blocked2Moving(x, y, &oldx, &oldy);
5411     return Feld[oldx][oldy];
5412   }
5413   else
5414     return element;
5415 }
5416
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5418 {
5419   /* like MovingOrBlocked2Element(), but if element is moving
5420      and (x,y) is the field the moving element is just leaving,
5421      return EL_BLOCKED instead of the element value */
5422   int element = Feld[x][y];
5423
5424   if (IS_MOVING(x, y))
5425   {
5426     if (element == EL_BLOCKED)
5427     {
5428       int oldx, oldy;
5429
5430       Blocked2Moving(x, y, &oldx, &oldy);
5431       return Feld[oldx][oldy];
5432     }
5433     else
5434       return EL_BLOCKED;
5435   }
5436   else
5437     return element;
5438 }
5439
5440 static void RemoveField(int x, int y)
5441 {
5442   Feld[x][y] = EL_EMPTY;
5443
5444   MovPos[x][y] = 0;
5445   MovDir[x][y] = 0;
5446   MovDelay[x][y] = 0;
5447
5448 #if USE_NEW_CUSTOM_VALUE
5449   CustomValue[x][y] = 0;
5450 #endif
5451
5452   AmoebaNr[x][y] = 0;
5453   ChangeDelay[x][y] = 0;
5454   ChangePage[x][y] = -1;
5455   Pushed[x][y] = FALSE;
5456
5457 #if 0
5458   ExplodeField[x][y] = EX_TYPE_NONE;
5459 #endif
5460
5461   GfxElement[x][y] = EL_UNDEFINED;
5462   GfxAction[x][y] = ACTION_DEFAULT;
5463   GfxDir[x][y] = MV_NONE;
5464 #if 0
5465   /* !!! this would prevent the removed tile from being redrawn !!! */
5466   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5467 #endif
5468 }
5469
5470 void RemoveMovingField(int x, int y)
5471 {
5472   int oldx = x, oldy = y, newx = x, newy = y;
5473   int element = Feld[x][y];
5474   int next_element = EL_UNDEFINED;
5475
5476   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5477     return;
5478
5479   if (IS_MOVING(x, y))
5480   {
5481     Moving2Blocked(x, y, &newx, &newy);
5482
5483     if (Feld[newx][newy] != EL_BLOCKED)
5484     {
5485       /* element is moving, but target field is not free (blocked), but
5486          already occupied by something different (example: acid pool);
5487          in this case, only remove the moving field, but not the target */
5488
5489       RemoveField(oldx, oldy);
5490
5491       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5492
5493       TEST_DrawLevelField(oldx, oldy);
5494
5495       return;
5496     }
5497   }
5498   else if (element == EL_BLOCKED)
5499   {
5500     Blocked2Moving(x, y, &oldx, &oldy);
5501     if (!IS_MOVING(oldx, oldy))
5502       return;
5503   }
5504
5505   if (element == EL_BLOCKED &&
5506       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512     next_element = get_next_element(Feld[oldx][oldy]);
5513
5514   RemoveField(oldx, oldy);
5515   RemoveField(newx, newy);
5516
5517   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5518
5519   if (next_element != EL_UNDEFINED)
5520     Feld[oldx][oldy] = next_element;
5521
5522   TEST_DrawLevelField(oldx, oldy);
5523   TEST_DrawLevelField(newx, newy);
5524 }
5525
5526 void DrawDynamite(int x, int y)
5527 {
5528   int sx = SCREENX(x), sy = SCREENY(y);
5529   int graphic = el2img(Feld[x][y]);
5530   int frame;
5531
5532   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5533     return;
5534
5535   if (IS_WALKABLE_INSIDE(Back[x][y]))
5536     return;
5537
5538   if (Back[x][y])
5539     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540   else if (Store[x][y])
5541     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5542
5543   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5544
5545   if (Back[x][y] || Store[x][y])
5546     DrawGraphicThruMask(sx, sy, graphic, frame);
5547   else
5548     DrawGraphic(sx, sy, graphic, frame);
5549 }
5550
5551 void CheckDynamite(int x, int y)
5552 {
5553   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5554   {
5555     MovDelay[x][y]--;
5556
5557     if (MovDelay[x][y] != 0)
5558     {
5559       DrawDynamite(x, y);
5560       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5561
5562       return;
5563     }
5564   }
5565
5566   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5567
5568   Bang(x, y);
5569 }
5570
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5572 {
5573   boolean num_checked_players = 0;
5574   int i;
5575
5576   for (i = 0; i < MAX_PLAYERS; i++)
5577   {
5578     if (stored_player[i].active)
5579     {
5580       int sx = stored_player[i].jx;
5581       int sy = stored_player[i].jy;
5582
5583       if (num_checked_players == 0)
5584       {
5585         *sx1 = *sx2 = sx;
5586         *sy1 = *sy2 = sy;
5587       }
5588       else
5589       {
5590         *sx1 = MIN(*sx1, sx);
5591         *sy1 = MIN(*sy1, sy);
5592         *sx2 = MAX(*sx2, sx);
5593         *sy2 = MAX(*sy2, sy);
5594       }
5595
5596       num_checked_players++;
5597     }
5598   }
5599 }
5600
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5602 {
5603   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5604
5605   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5606
5607   return (sx2 - sx1 < SCR_FIELDX &&
5608           sy2 - sy1 < SCR_FIELDY);
5609 }
5610
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5612 {
5613   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5614
5615   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5616
5617   *sx = (sx1 + sx2) / 2;
5618   *sy = (sy1 + sy2) / 2;
5619 }
5620
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622                         boolean center_screen, boolean quick_relocation)
5623 {
5624   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625   boolean no_delay = (tape.warp_forward);
5626   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5628
5629   if (quick_relocation)
5630   {
5631     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5632     {
5633       if (!level.shifted_relocation || center_screen)
5634       {
5635         /* quick relocation (without scrolling), with centering of screen */
5636
5637         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5638                     x > SBX_Right + MIDPOSX ? SBX_Right :
5639                     x - MIDPOSX);
5640
5641         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643                     y - MIDPOSY);
5644       }
5645       else
5646       {
5647         /* quick relocation (without scrolling), but do not center screen */
5648
5649         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5650                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5651                                old_x - MIDPOSX);
5652
5653         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655                                old_y - MIDPOSY);
5656
5657         int offset_x = x + (scroll_x - center_scroll_x);
5658         int offset_y = y + (scroll_y - center_scroll_y);
5659
5660         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5661                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662                     offset_x - MIDPOSX);
5663
5664         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666                     offset_y - MIDPOSY);
5667       }
5668     }
5669     else
5670     {
5671 #if 1
5672       if (!level.shifted_relocation || center_screen)
5673       {
5674         /* quick relocation (without scrolling), with centering of screen */
5675
5676         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5677                     x > SBX_Right + MIDPOSX ? SBX_Right :
5678                     x - MIDPOSX);
5679
5680         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5682                     y - MIDPOSY);
5683       }
5684       else
5685       {
5686         /* quick relocation (without scrolling), but do not center screen */
5687
5688         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5689                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5690                                old_x - MIDPOSX);
5691
5692         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5694                                old_y - MIDPOSY);
5695
5696         int offset_x = x + (scroll_x - center_scroll_x);
5697         int offset_y = y + (scroll_y - center_scroll_y);
5698
5699         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5700                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701                     offset_x - MIDPOSX);
5702
5703         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705                     offset_y - MIDPOSY);
5706       }
5707 #else
5708       /* quick relocation (without scrolling), inside visible screen area */
5709
5710       int offset = game.scroll_delay_value;
5711
5712       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5713           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5715
5716       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5717           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5719
5720       /* don't scroll over playfield boundaries */
5721       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5723
5724       /* don't scroll over playfield boundaries */
5725       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5727 #endif
5728     }
5729
5730     RedrawPlayfield(TRUE, 0,0,0,0);
5731   }
5732   else
5733   {
5734 #if 1
5735     int scroll_xx, scroll_yy;
5736
5737     if (!level.shifted_relocation || center_screen)
5738     {
5739       /* visible relocation (with scrolling), with centering of screen */
5740
5741       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5742                    x > SBX_Right + MIDPOSX ? SBX_Right :
5743                    x - MIDPOSX);
5744
5745       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5747                    y - MIDPOSY);
5748     }
5749     else
5750     {
5751       /* visible relocation (with scrolling), but do not center screen */
5752
5753       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5754                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5755                              old_x - MIDPOSX);
5756
5757       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5759                              old_y - MIDPOSY);
5760
5761       int offset_x = x + (scroll_x - center_scroll_x);
5762       int offset_y = y + (scroll_y - center_scroll_y);
5763
5764       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5765                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766                    offset_x - MIDPOSX);
5767
5768       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770                    offset_y - MIDPOSY);
5771     }
5772
5773 #else
5774
5775     /* visible relocation (with scrolling), with centering of screen */
5776
5777     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5778                      x > SBX_Right + MIDPOSX ? SBX_Right :
5779                      x - MIDPOSX);
5780
5781     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5783                      y - MIDPOSY);
5784 #endif
5785
5786     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5787
5788     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5789     {
5790       int dx = 0, dy = 0;
5791       int fx = FX, fy = FY;
5792
5793       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5795
5796       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5797         break;
5798
5799       scroll_x -= dx;
5800       scroll_y -= dy;
5801
5802       fx += dx * TILEX / 2;
5803       fy += dy * TILEY / 2;
5804
5805       ScrollLevel(dx, dy);
5806       DrawAllPlayers();
5807
5808       /* scroll in two steps of half tile size to make things smoother */
5809       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5810       FlushDisplay();
5811       Delay(wait_delay_value);
5812
5813       /* scroll second step to align at full tile size */
5814       BackToFront();
5815       Delay(wait_delay_value);
5816     }
5817
5818     DrawAllPlayers();
5819     BackToFront();
5820     Delay(wait_delay_value);
5821   }
5822 }
5823
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5825 {
5826   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827   int player_nr = GET_PLAYER_NR(el_player);
5828   struct PlayerInfo *player = &stored_player[player_nr];
5829   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830   boolean no_delay = (tape.warp_forward);
5831   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833   int old_jx = player->jx;
5834   int old_jy = player->jy;
5835   int old_element = Feld[old_jx][old_jy];
5836   int element = Feld[jx][jy];
5837   boolean player_relocated = (old_jx != jx || old_jy != jy);
5838
5839   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5841   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5843   int leave_side_horiz = move_dir_horiz;
5844   int leave_side_vert  = move_dir_vert;
5845   int enter_side = enter_side_horiz | enter_side_vert;
5846   int leave_side = leave_side_horiz | leave_side_vert;
5847
5848   if (player->GameOver)         /* do not reanimate dead player */
5849     return;
5850
5851   if (!player_relocated)        /* no need to relocate the player */
5852     return;
5853
5854   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5855   {
5856     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5857     DrawLevelField(jx, jy);
5858   }
5859
5860   if (player->present)
5861   {
5862     while (player->MovPos)
5863     {
5864       ScrollPlayer(player, SCROLL_GO_ON);
5865       ScrollScreen(NULL, SCROLL_GO_ON);
5866
5867       AdvanceFrameAndPlayerCounters(player->index_nr);
5868
5869       DrawPlayer(player);
5870
5871       BackToFront();
5872       Delay(wait_delay_value);
5873     }
5874
5875     DrawPlayer(player);         /* needed here only to cleanup last field */
5876     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5877
5878     player->is_moving = FALSE;
5879   }
5880
5881   if (IS_CUSTOM_ELEMENT(old_element))
5882     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5883                                CE_LEFT_BY_PLAYER,
5884                                player->index_bit, leave_side);
5885
5886   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5887                                       CE_PLAYER_LEAVES_X,
5888                                       player->index_bit, leave_side);
5889
5890   Feld[jx][jy] = el_player;
5891   InitPlayerField(jx, jy, el_player, TRUE);
5892
5893   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894      possible that the relocation target field did not contain a player element,
5895      but a walkable element, to which the new player was relocated -- in this
5896      case, restore that (already initialized!) element on the player field */
5897   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5898   {
5899     Feld[jx][jy] = element;     /* restore previously existing element */
5900 #if 0
5901     /* !!! do not initialize already initialized element a second time !!! */
5902     /* (this causes at least problems with "element creation" CE trigger for
5903        already existing elements, and existing Sokoban fields counted twice) */
5904     InitField(jx, jy, FALSE);
5905 #endif
5906   }
5907
5908   /* only visually relocate centered player */
5909   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910                      FALSE, level.instant_relocation);
5911
5912   TestIfPlayerTouchesBadThing(jx, jy);
5913   TestIfPlayerTouchesCustomElement(jx, jy);
5914
5915   if (IS_CUSTOM_ELEMENT(element))
5916     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917                                player->index_bit, enter_side);
5918
5919   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920                                       player->index_bit, enter_side);
5921
5922 #if 1
5923   if (player->is_switching)
5924   {
5925     /* ensure that relocation while still switching an element does not cause
5926        a new element to be treated as also switched directly after relocation
5927        (this is important for teleporter switches that teleport the player to
5928        a place where another teleporter switch is in the same direction, which
5929        would then incorrectly be treated as immediately switched before the
5930        direction key that caused the switch was released) */
5931
5932     player->switch_x += jx - old_jx;
5933     player->switch_y += jy - old_jy;
5934   }
5935 #endif
5936 }
5937
5938 void Explode(int ex, int ey, int phase, int mode)
5939 {
5940   int x, y;
5941   int last_phase;
5942   int border_element;
5943
5944   /* !!! eliminate this variable !!! */
5945   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5946
5947   if (game.explosions_delayed)
5948   {
5949     ExplodeField[ex][ey] = mode;
5950     return;
5951   }
5952
5953   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5954   {
5955     int center_element = Feld[ex][ey];
5956     int artwork_element, explosion_element;     /* set these values later */
5957
5958 #if 0
5959     /* --- This is only really needed (and now handled) in "Impact()". --- */
5960     /* do not explode moving elements that left the explode field in time */
5961     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962         center_element == EL_EMPTY &&
5963         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5964       return;
5965 #endif
5966
5967 #if 0
5968     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969     if (mode == EX_TYPE_NORMAL ||
5970         mode == EX_TYPE_CENTER ||
5971         mode == EX_TYPE_CROSS)
5972       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5973 #endif
5974
5975     /* remove things displayed in background while burning dynamite */
5976     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5977       Back[ex][ey] = 0;
5978
5979     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5980     {
5981       /* put moving element to center field (and let it explode there) */
5982       center_element = MovingOrBlocked2Element(ex, ey);
5983       RemoveMovingField(ex, ey);
5984       Feld[ex][ey] = center_element;
5985     }
5986
5987     /* now "center_element" is finally determined -- set related values now */
5988     artwork_element = center_element;           /* for custom player artwork */
5989     explosion_element = center_element;         /* for custom player artwork */
5990
5991     if (IS_PLAYER(ex, ey))
5992     {
5993       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5994
5995       artwork_element = stored_player[player_nr].artwork_element;
5996
5997       if (level.use_explosion_element[player_nr])
5998       {
5999         explosion_element = level.explosion_element[player_nr];
6000         artwork_element = explosion_element;
6001       }
6002     }
6003
6004 #if 1
6005     if (mode == EX_TYPE_NORMAL ||
6006         mode == EX_TYPE_CENTER ||
6007         mode == EX_TYPE_CROSS)
6008       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6009 #endif
6010
6011     last_phase = element_info[explosion_element].explosion_delay + 1;
6012
6013     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6014     {
6015       int xx = x - ex + 1;
6016       int yy = y - ey + 1;
6017       int element;
6018
6019       if (!IN_LEV_FIELD(x, y) ||
6020           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6022         continue;
6023
6024       element = Feld[x][y];
6025
6026       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6027       {
6028         element = MovingOrBlocked2Element(x, y);
6029
6030         if (!IS_EXPLOSION_PROOF(element))
6031           RemoveMovingField(x, y);
6032       }
6033
6034       /* indestructible elements can only explode in center (but not flames) */
6035       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036                                            mode == EX_TYPE_BORDER)) ||
6037           element == EL_FLAMES)
6038         continue;
6039
6040       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041          behaviour, for example when touching a yamyam that explodes to rocks
6042          with active deadly shield, a rock is created under the player !!! */
6043       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6044 #if 0
6045       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6048 #else
6049       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6050 #endif
6051       {
6052         if (IS_ACTIVE_BOMB(element))
6053         {
6054           /* re-activate things under the bomb like gate or penguin */
6055           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6056           Back[x][y] = 0;
6057         }
6058
6059         continue;
6060       }
6061
6062       /* save walkable background elements while explosion on same tile */
6063       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065         Back[x][y] = element;
6066
6067       /* ignite explodable elements reached by other explosion */
6068       if (element == EL_EXPLOSION)
6069         element = Store2[x][y];
6070
6071       if (AmoebaNr[x][y] &&
6072           (element == EL_AMOEBA_FULL ||
6073            element == EL_BD_AMOEBA ||
6074            element == EL_AMOEBA_GROWING))
6075       {
6076         AmoebaCnt[AmoebaNr[x][y]]--;
6077         AmoebaCnt2[AmoebaNr[x][y]]--;
6078       }
6079
6080       RemoveField(x, y);
6081
6082       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6083       {
6084         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6085
6086         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6087
6088         if (PLAYERINFO(ex, ey)->use_murphy)
6089           Store[x][y] = EL_EMPTY;
6090       }
6091
6092       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094       else if (ELEM_IS_PLAYER(center_element))
6095         Store[x][y] = EL_EMPTY;
6096       else if (center_element == EL_YAMYAM)
6097         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099         Store[x][y] = element_info[center_element].content.e[xx][yy];
6100 #if 1
6101       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103          otherwise) -- FIX THIS !!! */
6104       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105         Store[x][y] = element_info[element].content.e[1][1];
6106 #else
6107       else if (!CAN_EXPLODE(element))
6108         Store[x][y] = element_info[element].content.e[1][1];
6109 #endif
6110       else
6111         Store[x][y] = EL_EMPTY;
6112
6113       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114           center_element == EL_AMOEBA_TO_DIAMOND)
6115         Store2[x][y] = element;
6116
6117       Feld[x][y] = EL_EXPLOSION;
6118       GfxElement[x][y] = artwork_element;
6119
6120       ExplodePhase[x][y] = 1;
6121       ExplodeDelay[x][y] = last_phase;
6122
6123       Stop[x][y] = TRUE;
6124     }
6125
6126     if (center_element == EL_YAMYAM)
6127       game.yamyam_content_nr =
6128         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6129
6130     return;
6131   }
6132
6133   if (Stop[ex][ey])
6134     return;
6135
6136   x = ex;
6137   y = ey;
6138
6139   if (phase == 1)
6140     GfxFrame[x][y] = 0;         /* restart explosion animation */
6141
6142   last_phase = ExplodeDelay[x][y];
6143
6144   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6145
6146 #ifdef DEBUG
6147
6148   /* activate this even in non-DEBUG version until cause for crash in
6149      getGraphicAnimationFrame() (see below) is found and eliminated */
6150
6151 #endif
6152 #if 1
6153
6154 #if 1
6155   /* this can happen if the player leaves an explosion just in time */
6156   if (GfxElement[x][y] == EL_UNDEFINED)
6157     GfxElement[x][y] = EL_EMPTY;
6158 #else
6159   if (GfxElement[x][y] == EL_UNDEFINED)
6160   {
6161     printf("\n\n");
6162     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163     printf("Explode(): This should never happen!\n");
6164     printf("\n\n");
6165
6166     GfxElement[x][y] = EL_EMPTY;
6167   }
6168 #endif
6169
6170 #endif
6171
6172   border_element = Store2[x][y];
6173   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174     border_element = StorePlayer[x][y];
6175
6176   if (phase == element_info[border_element].ignition_delay ||
6177       phase == last_phase)
6178   {
6179     boolean border_explosion = FALSE;
6180
6181     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182         !PLAYER_EXPLOSION_PROTECTED(x, y))
6183     {
6184       KillPlayerUnlessExplosionProtected(x, y);
6185       border_explosion = TRUE;
6186     }
6187     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6188     {
6189       Feld[x][y] = Store2[x][y];
6190       Store2[x][y] = 0;
6191       Bang(x, y);
6192       border_explosion = TRUE;
6193     }
6194     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6195     {
6196       AmoebeUmwandeln(x, y);
6197       Store2[x][y] = 0;
6198       border_explosion = TRUE;
6199     }
6200
6201     /* if an element just explodes due to another explosion (chain-reaction),
6202        do not immediately end the new explosion when it was the last frame of
6203        the explosion (as it would be done in the following "if"-statement!) */
6204     if (border_explosion && phase == last_phase)
6205       return;
6206   }
6207
6208   if (phase == last_phase)
6209   {
6210     int element;
6211
6212     element = Feld[x][y] = Store[x][y];
6213     Store[x][y] = Store2[x][y] = 0;
6214     GfxElement[x][y] = EL_UNDEFINED;
6215
6216     /* player can escape from explosions and might therefore be still alive */
6217     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218         element <= EL_PLAYER_IS_EXPLODING_4)
6219     {
6220       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221       int explosion_element = EL_PLAYER_1 + player_nr;
6222       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6224
6225       if (level.use_explosion_element[player_nr])
6226         explosion_element = level.explosion_element[player_nr];
6227
6228       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229                     element_info[explosion_element].content.e[xx][yy]);
6230     }
6231
6232     /* restore probably existing indestructible background element */
6233     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234       element = Feld[x][y] = Back[x][y];
6235     Back[x][y] = 0;
6236
6237     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238     GfxDir[x][y] = MV_NONE;
6239     ChangeDelay[x][y] = 0;
6240     ChangePage[x][y] = -1;
6241
6242 #if USE_NEW_CUSTOM_VALUE
6243     CustomValue[x][y] = 0;
6244 #endif
6245
6246     InitField_WithBug2(x, y, FALSE);
6247
6248     TEST_DrawLevelField(x, y);
6249
6250     TestIfElementTouchesCustomElement(x, y);
6251
6252     if (GFX_CRUMBLED(element))
6253       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6254
6255     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256       StorePlayer[x][y] = 0;
6257
6258     if (ELEM_IS_PLAYER(element))
6259       RelocatePlayer(x, y, element);
6260   }
6261   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6262   {
6263     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6265
6266     if (phase == delay)
6267       TEST_DrawLevelFieldCrumbled(x, y);
6268
6269     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6270     {
6271       DrawLevelElement(x, y, Back[x][y]);
6272       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6273     }
6274     else if (IS_WALKABLE_UNDER(Back[x][y]))
6275     {
6276       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277       DrawLevelElementThruMask(x, y, Back[x][y]);
6278     }
6279     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6281   }
6282 }
6283
6284 void DynaExplode(int ex, int ey)
6285 {
6286   int i, j;
6287   int dynabomb_element = Feld[ex][ey];
6288   int dynabomb_size = 1;
6289   boolean dynabomb_xl = FALSE;
6290   struct PlayerInfo *player;
6291   static int xy[4][2] =
6292   {
6293     { 0, -1 },
6294     { -1, 0 },
6295     { +1, 0 },
6296     { 0, +1 }
6297   };
6298
6299   if (IS_ACTIVE_BOMB(dynabomb_element))
6300   {
6301     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302     dynabomb_size = player->dynabomb_size;
6303     dynabomb_xl = player->dynabomb_xl;
6304     player->dynabombs_left++;
6305   }
6306
6307   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6308
6309   for (i = 0; i < NUM_DIRECTIONS; i++)
6310   {
6311     for (j = 1; j <= dynabomb_size; j++)
6312     {
6313       int x = ex + j * xy[i][0];
6314       int y = ey + j * xy[i][1];
6315       int element;
6316
6317       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6318         break;
6319
6320       element = Feld[x][y];
6321
6322       /* do not restart explosions of fields with active bombs */
6323       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6324         continue;
6325
6326       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6327
6328       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329           !IS_DIGGABLE(element) && !dynabomb_xl)
6330         break;
6331     }
6332   }
6333 }
6334
6335 void Bang(int x, int y)
6336 {
6337   int element = MovingOrBlocked2Element(x, y);
6338   int explosion_type = EX_TYPE_NORMAL;
6339
6340   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6341   {
6342     struct PlayerInfo *player = PLAYERINFO(x, y);
6343
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345     element = Feld[x][y] = player->initial_element;
6346 #else
6347     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348                             player->element_nr);
6349 #endif
6350
6351     if (level.use_explosion_element[player->index_nr])
6352     {
6353       int explosion_element = level.explosion_element[player->index_nr];
6354
6355       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356         explosion_type = EX_TYPE_CROSS;
6357       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358         explosion_type = EX_TYPE_CENTER;
6359     }
6360   }
6361
6362   switch (element)
6363   {
6364     case EL_BUG:
6365     case EL_SPACESHIP:
6366     case EL_BD_BUTTERFLY:
6367     case EL_BD_FIREFLY:
6368     case EL_YAMYAM:
6369     case EL_DARK_YAMYAM:
6370     case EL_ROBOT:
6371     case EL_PACMAN:
6372     case EL_MOLE:
6373       RaiseScoreElement(element);
6374       break;
6375
6376     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380     case EL_DYNABOMB_INCREASE_NUMBER:
6381     case EL_DYNABOMB_INCREASE_SIZE:
6382     case EL_DYNABOMB_INCREASE_POWER:
6383       explosion_type = EX_TYPE_DYNA;
6384       break;
6385
6386     case EL_DC_LANDMINE:
6387 #if 0
6388     case EL_EM_EXIT_OPEN:
6389     case EL_EM_STEEL_EXIT_OPEN:
6390 #endif
6391       explosion_type = EX_TYPE_CENTER;
6392       break;
6393
6394     case EL_PENGUIN:
6395     case EL_LAMP:
6396     case EL_LAMP_ACTIVE:
6397     case EL_AMOEBA_TO_DIAMOND:
6398       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6399         explosion_type = EX_TYPE_CENTER;
6400       break;
6401
6402     default:
6403       if (element_info[element].explosion_type == EXPLODES_CROSS)
6404         explosion_type = EX_TYPE_CROSS;
6405       else if (element_info[element].explosion_type == EXPLODES_1X1)
6406         explosion_type = EX_TYPE_CENTER;
6407       break;
6408   }
6409
6410   if (explosion_type == EX_TYPE_DYNA)
6411     DynaExplode(x, y);
6412   else
6413     Explode(x, y, EX_PHASE_START, explosion_type);
6414
6415   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6416 }
6417
6418 void SplashAcid(int x, int y)
6419 {
6420   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421       (!IN_LEV_FIELD(x - 1, y - 2) ||
6422        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6424
6425   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426       (!IN_LEV_FIELD(x + 1, y - 2) ||
6427        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6429
6430   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6431 }
6432
6433 static void InitBeltMovement()
6434 {
6435   static int belt_base_element[4] =
6436   {
6437     EL_CONVEYOR_BELT_1_LEFT,
6438     EL_CONVEYOR_BELT_2_LEFT,
6439     EL_CONVEYOR_BELT_3_LEFT,
6440     EL_CONVEYOR_BELT_4_LEFT
6441   };
6442   static int belt_base_active_element[4] =
6443   {
6444     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6448   };
6449
6450   int x, y, i, j;
6451
6452   /* set frame order for belt animation graphic according to belt direction */
6453   for (i = 0; i < NUM_BELTS; i++)
6454   {
6455     int belt_nr = i;
6456
6457     for (j = 0; j < NUM_BELT_PARTS; j++)
6458     {
6459       int element = belt_base_active_element[belt_nr] + j;
6460       int graphic_1 = el2img(element);
6461       int graphic_2 = el2panelimg(element);
6462
6463       if (game.belt_dir[i] == MV_LEFT)
6464       {
6465         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6467       }
6468       else
6469       {
6470         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6471         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6472       }
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(x, y)
6477   {
6478     int element = Feld[x][y];
6479
6480     for (i = 0; i < NUM_BELTS; i++)
6481     {
6482       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6483       {
6484         int e_belt_nr = getBeltNrFromBeltElement(element);
6485         int belt_nr = i;
6486
6487         if (e_belt_nr == belt_nr)
6488         {
6489           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6490
6491           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6492         }
6493       }
6494     }
6495   }
6496 }
6497
6498 static void ToggleBeltSwitch(int x, int y)
6499 {
6500   static int belt_base_element[4] =
6501   {
6502     EL_CONVEYOR_BELT_1_LEFT,
6503     EL_CONVEYOR_BELT_2_LEFT,
6504     EL_CONVEYOR_BELT_3_LEFT,
6505     EL_CONVEYOR_BELT_4_LEFT
6506   };
6507   static int belt_base_active_element[4] =
6508   {
6509     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6513   };
6514   static int belt_base_switch_element[4] =
6515   {
6516     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6520   };
6521   static int belt_move_dir[4] =
6522   {
6523     MV_LEFT,
6524     MV_NONE,
6525     MV_RIGHT,
6526     MV_NONE,
6527   };
6528
6529   int element = Feld[x][y];
6530   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532   int belt_dir = belt_move_dir[belt_dir_nr];
6533   int xx, yy, i;
6534
6535   if (!IS_BELT_SWITCH(element))
6536     return;
6537
6538   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539   game.belt_dir[belt_nr] = belt_dir;
6540
6541   if (belt_dir_nr == 3)
6542     belt_dir_nr = 1;
6543
6544   /* set frame order for belt animation graphic according to belt direction */
6545   for (i = 0; i < NUM_BELT_PARTS; i++)
6546   {
6547     int element = belt_base_active_element[belt_nr] + i;
6548     int graphic_1 = el2img(element);
6549     int graphic_2 = el2panelimg(element);
6550
6551     if (belt_dir == MV_LEFT)
6552     {
6553       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6555     }
6556     else
6557     {
6558       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6559       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6560     }
6561   }
6562
6563   SCAN_PLAYFIELD(xx, yy)
6564   {
6565     int element = Feld[xx][yy];
6566
6567     if (IS_BELT_SWITCH(element))
6568     {
6569       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6570
6571       if (e_belt_nr == belt_nr)
6572       {
6573         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574         TEST_DrawLevelField(xx, yy);
6575       }
6576     }
6577     else if (IS_BELT(element) && belt_dir != MV_NONE)
6578     {
6579       int e_belt_nr = getBeltNrFromBeltElement(element);
6580
6581       if (e_belt_nr == belt_nr)
6582       {
6583         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6584
6585         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586         TEST_DrawLevelField(xx, yy);
6587       }
6588     }
6589     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6590     {
6591       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6592
6593       if (e_belt_nr == belt_nr)
6594       {
6595         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6596
6597         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598         TEST_DrawLevelField(xx, yy);
6599       }
6600     }
6601   }
6602 }
6603
6604 static void ToggleSwitchgateSwitch(int x, int y)
6605 {
6606   int xx, yy;
6607
6608   game.switchgate_pos = !game.switchgate_pos;
6609
6610   SCAN_PLAYFIELD(xx, yy)
6611   {
6612     int element = Feld[xx][yy];
6613
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615     if (element == EL_SWITCHGATE_SWITCH_UP ||
6616         element == EL_SWITCHGATE_SWITCH_DOWN)
6617     {
6618       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619       TEST_DrawLevelField(xx, yy);
6620     }
6621     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6623     {
6624       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625       TEST_DrawLevelField(xx, yy);
6626     }
6627 #else
6628     if (element == EL_SWITCHGATE_SWITCH_UP)
6629     {
6630       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6634     {
6635       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636       TEST_DrawLevelField(xx, yy);
6637     }
6638     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6639     {
6640       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641       TEST_DrawLevelField(xx, yy);
6642     }
6643     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6644     {
6645       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646       TEST_DrawLevelField(xx, yy);
6647     }
6648 #endif
6649     else if (element == EL_SWITCHGATE_OPEN ||
6650              element == EL_SWITCHGATE_OPENING)
6651     {
6652       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6653
6654       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6655     }
6656     else if (element == EL_SWITCHGATE_CLOSED ||
6657              element == EL_SWITCHGATE_CLOSING)
6658     {
6659       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6660
6661       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6662     }
6663   }
6664 }
6665
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6667 {
6668   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6670           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6671           element);
6672 }
6673
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6675 {
6676   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6678           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6679           element);
6680 }
6681
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6683 {
6684   int x, y;
6685
6686   SCAN_PLAYFIELD(x, y)
6687   {
6688     int element = Feld[x][y];
6689
6690     if (element == EL_LIGHT_SWITCH &&
6691         game.light_time_left > 0)
6692     {
6693       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697              game.light_time_left == 0)
6698     {
6699       Feld[x][y] = EL_LIGHT_SWITCH;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_EMC_DRIPPER &&
6703              game.light_time_left > 0)
6704     {
6705       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709              game.light_time_left == 0)
6710     {
6711       Feld[x][y] = EL_EMC_DRIPPER;
6712       TEST_DrawLevelField(x, y);
6713     }
6714     else if (element == EL_INVISIBLE_STEELWALL ||
6715              element == EL_INVISIBLE_WALL ||
6716              element == EL_INVISIBLE_SAND)
6717     {
6718       if (game.light_time_left > 0)
6719         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6720
6721       TEST_DrawLevelField(x, y);
6722
6723       /* uncrumble neighbour fields, if needed */
6724       if (element == EL_INVISIBLE_SAND)
6725         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6726     }
6727     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728              element == EL_INVISIBLE_WALL_ACTIVE ||
6729              element == EL_INVISIBLE_SAND_ACTIVE)
6730     {
6731       if (game.light_time_left == 0)
6732         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6733
6734       TEST_DrawLevelField(x, y);
6735
6736       /* re-crumble neighbour fields, if needed */
6737       if (element == EL_INVISIBLE_SAND)
6738         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6739     }
6740   }
6741 }
6742
6743 static void RedrawAllInvisibleElementsForLenses()
6744 {
6745   int x, y;
6746
6747   SCAN_PLAYFIELD(x, y)
6748   {
6749     int element = Feld[x][y];
6750
6751     if (element == EL_EMC_DRIPPER &&
6752         game.lenses_time_left > 0)
6753     {
6754       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755       TEST_DrawLevelField(x, y);
6756     }
6757     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758              game.lenses_time_left == 0)
6759     {
6760       Feld[x][y] = EL_EMC_DRIPPER;
6761       TEST_DrawLevelField(x, y);
6762     }
6763     else if (element == EL_INVISIBLE_STEELWALL ||
6764              element == EL_INVISIBLE_WALL ||
6765              element == EL_INVISIBLE_SAND)
6766     {
6767       if (game.lenses_time_left > 0)
6768         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6769
6770       TEST_DrawLevelField(x, y);
6771
6772       /* uncrumble neighbour fields, if needed */
6773       if (element == EL_INVISIBLE_SAND)
6774         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6775     }
6776     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777              element == EL_INVISIBLE_WALL_ACTIVE ||
6778              element == EL_INVISIBLE_SAND_ACTIVE)
6779     {
6780       if (game.lenses_time_left == 0)
6781         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6782
6783       TEST_DrawLevelField(x, y);
6784
6785       /* re-crumble neighbour fields, if needed */
6786       if (element == EL_INVISIBLE_SAND)
6787         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6788     }
6789   }
6790 }
6791
6792 static void RedrawAllInvisibleElementsForMagnifier()
6793 {
6794   int x, y;
6795
6796   SCAN_PLAYFIELD(x, y)
6797   {
6798     int element = Feld[x][y];
6799
6800     if (element == EL_EMC_FAKE_GRASS &&
6801         game.magnify_time_left > 0)
6802     {
6803       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804       TEST_DrawLevelField(x, y);
6805     }
6806     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807              game.magnify_time_left == 0)
6808     {
6809       Feld[x][y] = EL_EMC_FAKE_GRASS;
6810       TEST_DrawLevelField(x, y);
6811     }
6812     else if (IS_GATE_GRAY(element) &&
6813              game.magnify_time_left > 0)
6814     {
6815       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817                     IS_EM_GATE_GRAY(element) ?
6818                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819                     IS_EMC_GATE_GRAY(element) ?
6820                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821                     IS_DC_GATE_GRAY(element) ?
6822                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6823                     element);
6824       TEST_DrawLevelField(x, y);
6825     }
6826     else if (IS_GATE_GRAY_ACTIVE(element) &&
6827              game.magnify_time_left == 0)
6828     {
6829       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6832                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6836                     EL_DC_GATE_WHITE_GRAY :
6837                     element);
6838       TEST_DrawLevelField(x, y);
6839     }
6840   }
6841 }
6842
6843 static void ToggleLightSwitch(int x, int y)
6844 {
6845   int element = Feld[x][y];
6846
6847   game.light_time_left =
6848     (element == EL_LIGHT_SWITCH ?
6849      level.time_light * FRAMES_PER_SECOND : 0);
6850
6851   RedrawAllLightSwitchesAndInvisibleElements();
6852 }
6853
6854 static void ActivateTimegateSwitch(int x, int y)
6855 {
6856   int xx, yy;
6857
6858   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6859
6860   SCAN_PLAYFIELD(xx, yy)
6861   {
6862     int element = Feld[xx][yy];
6863
6864     if (element == EL_TIMEGATE_CLOSED ||
6865         element == EL_TIMEGATE_CLOSING)
6866     {
6867       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6869     }
6870
6871     /*
6872     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6873     {
6874       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875       TEST_DrawLevelField(xx, yy);
6876     }
6877     */
6878
6879   }
6880
6881 #if 1
6882   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6884 #else
6885   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6886 #endif
6887 }
6888
6889 void Impact(int x, int y)
6890 {
6891   boolean last_line = (y == lev_fieldy - 1);
6892   boolean object_hit = FALSE;
6893   boolean impact = (last_line || object_hit);
6894   int element = Feld[x][y];
6895   int smashed = EL_STEELWALL;
6896
6897   if (!last_line)       /* check if element below was hit */
6898   {
6899     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6900       return;
6901
6902     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903                                          MovDir[x][y + 1] != MV_DOWN ||
6904                                          MovPos[x][y + 1] <= TILEY / 2));
6905
6906     /* do not smash moving elements that left the smashed field in time */
6907     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6909       object_hit = FALSE;
6910
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6913     {
6914       RemoveMovingField(x, y + 1);
6915       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916       Feld[x][y + 2] = EL_ROCK;
6917       TEST_DrawLevelField(x, y + 2);
6918
6919       object_hit = TRUE;
6920     }
6921
6922     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6923     {
6924       RemoveMovingField(x, y + 1);
6925       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926       Feld[x][y + 2] = EL_ROCK;
6927       TEST_DrawLevelField(x, y + 2);
6928
6929       object_hit = TRUE;
6930     }
6931 #endif
6932
6933     if (object_hit)
6934       smashed = MovingOrBlocked2Element(x, y + 1);
6935
6936     impact = (last_line || object_hit);
6937   }
6938
6939   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6940   {
6941     SplashAcid(x, y + 1);
6942     return;
6943   }
6944
6945   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946   /* only reset graphic animation if graphic really changes after impact */
6947   if (impact &&
6948       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6949   {
6950     ResetGfxAnimation(x, y);
6951     TEST_DrawLevelField(x, y);
6952   }
6953
6954   if (impact && CAN_EXPLODE_IMPACT(element))
6955   {
6956     Bang(x, y);
6957     return;
6958   }
6959   else if (impact && element == EL_PEARL &&
6960            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6961   {
6962     ResetGfxAnimation(x, y);
6963
6964     Feld[x][y] = EL_PEARL_BREAKING;
6965     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6966     return;
6967   }
6968   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6969   {
6970     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6971
6972     return;
6973   }
6974
6975   if (impact && element == EL_AMOEBA_DROP)
6976   {
6977     if (object_hit && IS_PLAYER(x, y + 1))
6978       KillPlayerUnlessEnemyProtected(x, y + 1);
6979     else if (object_hit && smashed == EL_PENGUIN)
6980       Bang(x, y + 1);
6981     else
6982     {
6983       Feld[x][y] = EL_AMOEBA_GROWING;
6984       Store[x][y] = EL_AMOEBA_WET;
6985
6986       ResetRandomAnimationValue(x, y);
6987     }
6988     return;
6989   }
6990
6991   if (object_hit)               /* check which object was hit */
6992   {
6993     if ((CAN_PASS_MAGIC_WALL(element) && 
6994          (smashed == EL_MAGIC_WALL ||
6995           smashed == EL_BD_MAGIC_WALL)) ||
6996         (CAN_PASS_DC_MAGIC_WALL(element) &&
6997          smashed == EL_DC_MAGIC_WALL))
6998     {
6999       int xx, yy;
7000       int activated_magic_wall =
7001         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003          EL_DC_MAGIC_WALL_ACTIVE);
7004
7005       /* activate magic wall / mill */
7006       SCAN_PLAYFIELD(xx, yy)
7007       {
7008         if (Feld[xx][yy] == smashed)
7009           Feld[xx][yy] = activated_magic_wall;
7010       }
7011
7012       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013       game.magic_wall_active = TRUE;
7014
7015       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016                             SND_MAGIC_WALL_ACTIVATING :
7017                             smashed == EL_BD_MAGIC_WALL ?
7018                             SND_BD_MAGIC_WALL_ACTIVATING :
7019                             SND_DC_MAGIC_WALL_ACTIVATING));
7020     }
7021
7022     if (IS_PLAYER(x, y + 1))
7023     {
7024       if (CAN_SMASH_PLAYER(element))
7025       {
7026         KillPlayerUnlessEnemyProtected(x, y + 1);
7027         return;
7028       }
7029     }
7030     else if (smashed == EL_PENGUIN)
7031     {
7032       if (CAN_SMASH_PLAYER(element))
7033       {
7034         Bang(x, y + 1);
7035         return;
7036       }
7037     }
7038     else if (element == EL_BD_DIAMOND)
7039     {
7040       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7041       {
7042         Bang(x, y + 1);
7043         return;
7044       }
7045     }
7046     else if (((element == EL_SP_INFOTRON ||
7047                element == EL_SP_ZONK) &&
7048               (smashed == EL_SP_SNIKSNAK ||
7049                smashed == EL_SP_ELECTRON ||
7050                smashed == EL_SP_DISK_ORANGE)) ||
7051              (element == EL_SP_INFOTRON &&
7052               smashed == EL_SP_DISK_YELLOW))
7053     {
7054       Bang(x, y + 1);
7055       return;
7056     }
7057     else if (CAN_SMASH_EVERYTHING(element))
7058     {
7059       if (IS_CLASSIC_ENEMY(smashed) ||
7060           CAN_EXPLODE_SMASHED(smashed))
7061       {
7062         Bang(x, y + 1);
7063         return;
7064       }
7065       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7066       {
7067         if (smashed == EL_LAMP ||
7068             smashed == EL_LAMP_ACTIVE)
7069         {
7070           Bang(x, y + 1);
7071           return;
7072         }
7073         else if (smashed == EL_NUT)
7074         {
7075           Feld[x][y + 1] = EL_NUT_BREAKING;
7076           PlayLevelSound(x, y, SND_NUT_BREAKING);
7077           RaiseScoreElement(EL_NUT);
7078           return;
7079         }
7080         else if (smashed == EL_PEARL)
7081         {
7082           ResetGfxAnimation(x, y);
7083
7084           Feld[x][y + 1] = EL_PEARL_BREAKING;
7085           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7086           return;
7087         }
7088         else if (smashed == EL_DIAMOND)
7089         {
7090           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7092           return;
7093         }
7094         else if (IS_BELT_SWITCH(smashed))
7095         {
7096           ToggleBeltSwitch(x, y + 1);
7097         }
7098         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7102         {
7103           ToggleSwitchgateSwitch(x, y + 1);
7104         }
7105         else if (smashed == EL_LIGHT_SWITCH ||
7106                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7107         {
7108           ToggleLightSwitch(x, y + 1);
7109         }
7110         else
7111         {
7112 #if 0
7113           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7114 #endif
7115
7116           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7117
7118           CheckElementChangeBySide(x, y + 1, smashed, element,
7119                                    CE_SWITCHED, CH_SIDE_TOP);
7120           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7121                                             CH_SIDE_TOP);
7122         }
7123       }
7124       else
7125       {
7126         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7127       }
7128     }
7129   }
7130
7131   /* play sound of magic wall / mill */
7132   if (!last_line &&
7133       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7136   {
7137     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7143
7144     return;
7145   }
7146
7147   /* play sound of object that hits the ground */
7148   if (last_line || object_hit)
7149     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7150 }
7151
7152 inline static void TurnRoundExt(int x, int y)
7153 {
7154   static struct
7155   {
7156     int dx, dy;
7157   } move_xy[] =
7158   {
7159     {  0,  0 },
7160     { -1,  0 },
7161     { +1,  0 },
7162     {  0,  0 },
7163     {  0, -1 },
7164     {  0,  0 }, { 0, 0 }, { 0, 0 },
7165     {  0, +1 }
7166   };
7167   static struct
7168   {
7169     int left, right, back;
7170   } turn[] =
7171   {
7172     { 0,        0,              0        },
7173     { MV_DOWN,  MV_UP,          MV_RIGHT },
7174     { MV_UP,    MV_DOWN,        MV_LEFT  },
7175     { 0,        0,              0        },
7176     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7177     { 0,        0,              0        },
7178     { 0,        0,              0        },
7179     { 0,        0,              0        },
7180     { MV_RIGHT, MV_LEFT,        MV_UP    }
7181   };
7182
7183   int element = Feld[x][y];
7184   int move_pattern = element_info[element].move_pattern;
7185
7186   int old_move_dir = MovDir[x][y];
7187   int left_dir  = turn[old_move_dir].left;
7188   int right_dir = turn[old_move_dir].right;
7189   int back_dir  = turn[old_move_dir].back;
7190
7191   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7192   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7193   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7194   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7195
7196   int left_x  = x + left_dx,  left_y  = y + left_dy;
7197   int right_x = x + right_dx, right_y = y + right_dy;
7198   int move_x  = x + move_dx,  move_y  = y + move_dy;
7199
7200   int xx, yy;
7201
7202   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7203   {
7204     TestIfBadThingTouchesOtherBadThing(x, y);
7205
7206     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207       MovDir[x][y] = right_dir;
7208     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209       MovDir[x][y] = left_dir;
7210
7211     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7212       MovDelay[x][y] = 9;
7213     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7214       MovDelay[x][y] = 1;
7215   }
7216   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7217   {
7218     TestIfBadThingTouchesOtherBadThing(x, y);
7219
7220     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221       MovDir[x][y] = left_dir;
7222     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223       MovDir[x][y] = right_dir;
7224
7225     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7226       MovDelay[x][y] = 9;
7227     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7228       MovDelay[x][y] = 1;
7229   }
7230   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7231   {
7232     TestIfBadThingTouchesOtherBadThing(x, y);
7233
7234     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235       MovDir[x][y] = left_dir;
7236     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237       MovDir[x][y] = right_dir;
7238
7239     if (MovDir[x][y] != old_move_dir)
7240       MovDelay[x][y] = 9;
7241   }
7242   else if (element == EL_YAMYAM)
7243   {
7244     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7246
7247     if (can_turn_left && can_turn_right)
7248       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249     else if (can_turn_left)
7250       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251     else if (can_turn_right)
7252       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7253     else
7254       MovDir[x][y] = back_dir;
7255
7256     MovDelay[x][y] = 16 + 16 * RND(3);
7257   }
7258   else if (element == EL_DARK_YAMYAM)
7259   {
7260     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7261                                                          left_x, left_y);
7262     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7263                                                          right_x, right_y);
7264
7265     if (can_turn_left && can_turn_right)
7266       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267     else if (can_turn_left)
7268       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269     else if (can_turn_right)
7270       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7271     else
7272       MovDir[x][y] = back_dir;
7273
7274     MovDelay[x][y] = 16 + 16 * RND(3);
7275   }
7276   else if (element == EL_PACMAN)
7277   {
7278     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7280
7281     if (can_turn_left && can_turn_right)
7282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283     else if (can_turn_left)
7284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285     else if (can_turn_right)
7286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7287     else
7288       MovDir[x][y] = back_dir;
7289
7290     MovDelay[x][y] = 6 + RND(40);
7291   }
7292   else if (element == EL_PIG)
7293   {
7294     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297     boolean should_turn_left, should_turn_right, should_move_on;
7298     int rnd_value = 24;
7299     int rnd = RND(rnd_value);
7300
7301     should_turn_left = (can_turn_left &&
7302                         (!can_move_on ||
7303                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304                                                    y + back_dy + left_dy)));
7305     should_turn_right = (can_turn_right &&
7306                          (!can_move_on ||
7307                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308                                                     y + back_dy + right_dy)));
7309     should_move_on = (can_move_on &&
7310                       (!can_turn_left ||
7311                        !can_turn_right ||
7312                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313                                                  y + move_dy + left_dy) ||
7314                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315                                                  y + move_dy + right_dy)));
7316
7317     if (should_turn_left || should_turn_right || should_move_on)
7318     {
7319       if (should_turn_left && should_turn_right && should_move_on)
7320         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7321                         rnd < 2 * rnd_value / 3 ? right_dir :
7322                         old_move_dir);
7323       else if (should_turn_left && should_turn_right)
7324         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325       else if (should_turn_left && should_move_on)
7326         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327       else if (should_turn_right && should_move_on)
7328         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329       else if (should_turn_left)
7330         MovDir[x][y] = left_dir;
7331       else if (should_turn_right)
7332         MovDir[x][y] = right_dir;
7333       else if (should_move_on)
7334         MovDir[x][y] = old_move_dir;
7335     }
7336     else if (can_move_on && rnd > rnd_value / 8)
7337       MovDir[x][y] = old_move_dir;
7338     else if (can_turn_left && can_turn_right)
7339       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340     else if (can_turn_left && rnd > rnd_value / 8)
7341       MovDir[x][y] = left_dir;
7342     else if (can_turn_right && rnd > rnd_value/8)
7343       MovDir[x][y] = right_dir;
7344     else
7345       MovDir[x][y] = back_dir;
7346
7347     xx = x + move_xy[MovDir[x][y]].dx;
7348     yy = y + move_xy[MovDir[x][y]].dy;
7349
7350     if (!IN_LEV_FIELD(xx, yy) ||
7351         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352       MovDir[x][y] = old_move_dir;
7353
7354     MovDelay[x][y] = 0;
7355   }
7356   else if (element == EL_DRAGON)
7357   {
7358     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7361     int rnd_value = 24;
7362     int rnd = RND(rnd_value);
7363
7364     if (can_move_on && rnd > rnd_value / 8)
7365       MovDir[x][y] = old_move_dir;
7366     else if (can_turn_left && can_turn_right)
7367       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368     else if (can_turn_left && rnd > rnd_value / 8)
7369       MovDir[x][y] = left_dir;
7370     else if (can_turn_right && rnd > rnd_value / 8)
7371       MovDir[x][y] = right_dir;
7372     else
7373       MovDir[x][y] = back_dir;
7374
7375     xx = x + move_xy[MovDir[x][y]].dx;
7376     yy = y + move_xy[MovDir[x][y]].dy;
7377
7378     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379       MovDir[x][y] = old_move_dir;
7380
7381     MovDelay[x][y] = 0;
7382   }
7383   else if (element == EL_MOLE)
7384   {
7385     boolean can_move_on =
7386       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7388                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7389     if (!can_move_on)
7390     {
7391       boolean can_turn_left =
7392         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393                               IS_AMOEBOID(Feld[left_x][left_y])));
7394
7395       boolean can_turn_right =
7396         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397                               IS_AMOEBOID(Feld[right_x][right_y])));
7398
7399       if (can_turn_left && can_turn_right)
7400         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401       else if (can_turn_left)
7402         MovDir[x][y] = left_dir;
7403       else
7404         MovDir[x][y] = right_dir;
7405     }
7406
7407     if (MovDir[x][y] != old_move_dir)
7408       MovDelay[x][y] = 9;
7409   }
7410   else if (element == EL_BALLOON)
7411   {
7412     MovDir[x][y] = game.wind_direction;
7413     MovDelay[x][y] = 0;
7414   }
7415   else if (element == EL_SPRING)
7416   {
7417 #if USE_NEW_SPRING_BUMPER
7418     if (MovDir[x][y] & MV_HORIZONTAL)
7419     {
7420       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7422       {
7423         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424         ResetGfxAnimation(move_x, move_y);
7425         TEST_DrawLevelField(move_x, move_y);
7426
7427         MovDir[x][y] = back_dir;
7428       }
7429       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431         MovDir[x][y] = MV_NONE;
7432     }
7433 #else
7434     if (MovDir[x][y] & MV_HORIZONTAL &&
7435         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437       MovDir[x][y] = MV_NONE;
7438 #endif
7439
7440     MovDelay[x][y] = 0;
7441   }
7442   else if (element == EL_ROBOT ||
7443            element == EL_SATELLITE ||
7444            element == EL_PENGUIN ||
7445            element == EL_EMC_ANDROID)
7446   {
7447     int attr_x = -1, attr_y = -1;
7448
7449     if (AllPlayersGone)
7450     {
7451       attr_x = ExitX;
7452       attr_y = ExitY;
7453     }
7454     else
7455     {
7456       int i;
7457
7458       for (i = 0; i < MAX_PLAYERS; i++)
7459       {
7460         struct PlayerInfo *player = &stored_player[i];
7461         int jx = player->jx, jy = player->jy;
7462
7463         if (!player->active)
7464           continue;
7465
7466         if (attr_x == -1 ||
7467             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7468         {
7469           attr_x = jx;
7470           attr_y = jy;
7471         }
7472       }
7473     }
7474
7475     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477          game.engine_version < VERSION_IDENT(3,1,0,0)))
7478     {
7479       attr_x = ZX;
7480       attr_y = ZY;
7481     }
7482
7483     if (element == EL_PENGUIN)
7484     {
7485       int i;
7486       static int xy[4][2] =
7487       {
7488         { 0, -1 },
7489         { -1, 0 },
7490         { +1, 0 },
7491         { 0, +1 }
7492       };
7493
7494       for (i = 0; i < NUM_DIRECTIONS; i++)
7495       {
7496         int ex = x + xy[i][0];
7497         int ey = y + xy[i][1];
7498
7499         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7503         {
7504           attr_x = ex;
7505           attr_y = ey;
7506           break;
7507         }
7508       }
7509     }
7510
7511     MovDir[x][y] = MV_NONE;
7512     if (attr_x < x)
7513       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514     else if (attr_x > x)
7515       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7516     if (attr_y < y)
7517       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518     else if (attr_y > y)
7519       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7520
7521     if (element == EL_ROBOT)
7522     {
7523       int newx, newy;
7524
7525       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527       Moving2Blocked(x, y, &newx, &newy);
7528
7529       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530         MovDelay[x][y] = 8 + 8 * !RND(3);
7531       else
7532         MovDelay[x][y] = 16;
7533     }
7534     else if (element == EL_PENGUIN)
7535     {
7536       int newx, newy;
7537
7538       MovDelay[x][y] = 1;
7539
7540       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7541       {
7542         boolean first_horiz = RND(2);
7543         int new_move_dir = MovDir[x][y];
7544
7545         MovDir[x][y] =
7546           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547         Moving2Blocked(x, y, &newx, &newy);
7548
7549         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7550           return;
7551
7552         MovDir[x][y] =
7553           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554         Moving2Blocked(x, y, &newx, &newy);
7555
7556         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7557           return;
7558
7559         MovDir[x][y] = old_move_dir;
7560         return;
7561       }
7562     }
7563     else if (element == EL_SATELLITE)
7564     {
7565       int newx, newy;
7566
7567       MovDelay[x][y] = 1;
7568
7569       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7570       {
7571         boolean first_horiz = RND(2);
7572         int new_move_dir = MovDir[x][y];
7573
7574         MovDir[x][y] =
7575           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576         Moving2Blocked(x, y, &newx, &newy);
7577
7578         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7579           return;
7580
7581         MovDir[x][y] =
7582           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583         Moving2Blocked(x, y, &newx, &newy);
7584
7585         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7586           return;
7587
7588         MovDir[x][y] = old_move_dir;
7589         return;
7590       }
7591     }
7592     else if (element == EL_EMC_ANDROID)
7593     {
7594       static int check_pos[16] =
7595       {
7596         -1,             /*  0 => (invalid)          */
7597         7,              /*  1 => MV_LEFT            */
7598         3,              /*  2 => MV_RIGHT           */
7599         -1,             /*  3 => (invalid)          */
7600         1,              /*  4 =>            MV_UP   */
7601         0,              /*  5 => MV_LEFT  | MV_UP   */
7602         2,              /*  6 => MV_RIGHT | MV_UP   */
7603         -1,             /*  7 => (invalid)          */
7604         5,              /*  8 =>            MV_DOWN */
7605         6,              /*  9 => MV_LEFT  | MV_DOWN */
7606         4,              /* 10 => MV_RIGHT | MV_DOWN */
7607         -1,             /* 11 => (invalid)          */
7608         -1,             /* 12 => (invalid)          */
7609         -1,             /* 13 => (invalid)          */
7610         -1,             /* 14 => (invalid)          */
7611         -1,             /* 15 => (invalid)          */
7612       };
7613       static struct
7614       {
7615         int dx, dy;
7616         int dir;
7617       } check_xy[8] =
7618       {
7619         { -1, -1,       MV_LEFT  | MV_UP   },
7620         {  0, -1,                  MV_UP   },
7621         { +1, -1,       MV_RIGHT | MV_UP   },
7622         { +1,  0,       MV_RIGHT           },
7623         { +1, +1,       MV_RIGHT | MV_DOWN },
7624         {  0, +1,                  MV_DOWN },
7625         { -1, +1,       MV_LEFT  | MV_DOWN },
7626         { -1,  0,       MV_LEFT            },
7627       };
7628       int start_pos, check_order;
7629       boolean can_clone = FALSE;
7630       int i;
7631
7632       /* check if there is any free field around current position */
7633       for (i = 0; i < 8; i++)
7634       {
7635         int newx = x + check_xy[i].dx;
7636         int newy = y + check_xy[i].dy;
7637
7638         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7639         {
7640           can_clone = TRUE;
7641
7642           break;
7643         }
7644       }
7645
7646       if (can_clone)            /* randomly find an element to clone */
7647       {
7648         can_clone = FALSE;
7649
7650         start_pos = check_pos[RND(8)];
7651         check_order = (RND(2) ? -1 : +1);
7652
7653         for (i = 0; i < 8; i++)
7654         {
7655           int pos_raw = start_pos + i * check_order;
7656           int pos = (pos_raw + 8) % 8;
7657           int newx = x + check_xy[pos].dx;
7658           int newy = y + check_xy[pos].dy;
7659
7660           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7661           {
7662             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7664
7665             Store[x][y] = Feld[newx][newy];
7666
7667             can_clone = TRUE;
7668
7669             break;
7670           }
7671         }
7672       }
7673
7674       if (can_clone)            /* randomly find a direction to move */
7675       {
7676         can_clone = FALSE;
7677
7678         start_pos = check_pos[RND(8)];
7679         check_order = (RND(2) ? -1 : +1);
7680
7681         for (i = 0; i < 8; i++)
7682         {
7683           int pos_raw = start_pos + i * check_order;
7684           int pos = (pos_raw + 8) % 8;
7685           int newx = x + check_xy[pos].dx;
7686           int newy = y + check_xy[pos].dy;
7687           int new_move_dir = check_xy[pos].dir;
7688
7689           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7690           {
7691             MovDir[x][y] = new_move_dir;
7692             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7693
7694             can_clone = TRUE;
7695
7696             break;
7697           }
7698         }
7699       }
7700
7701       if (can_clone)            /* cloning and moving successful */
7702         return;
7703
7704       /* cannot clone -- try to move towards player */
7705
7706       start_pos = check_pos[MovDir[x][y] & 0x0f];
7707       check_order = (RND(2) ? -1 : +1);
7708
7709       for (i = 0; i < 3; i++)
7710       {
7711         /* first check start_pos, then previous/next or (next/previous) pos */
7712         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713         int pos = (pos_raw + 8) % 8;
7714         int newx = x + check_xy[pos].dx;
7715         int newy = y + check_xy[pos].dy;
7716         int new_move_dir = check_xy[pos].dir;
7717
7718         if (IS_PLAYER(newx, newy))
7719           break;
7720
7721         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7722         {
7723           MovDir[x][y] = new_move_dir;
7724           MovDelay[x][y] = level.android_move_time * 8 + 1;
7725
7726           break;
7727         }
7728       }
7729     }
7730   }
7731   else if (move_pattern == MV_TURNING_LEFT ||
7732            move_pattern == MV_TURNING_RIGHT ||
7733            move_pattern == MV_TURNING_LEFT_RIGHT ||
7734            move_pattern == MV_TURNING_RIGHT_LEFT ||
7735            move_pattern == MV_TURNING_RANDOM ||
7736            move_pattern == MV_ALL_DIRECTIONS)
7737   {
7738     boolean can_turn_left =
7739       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740     boolean can_turn_right =
7741       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7742
7743     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7744       return;
7745
7746     if (move_pattern == MV_TURNING_LEFT)
7747       MovDir[x][y] = left_dir;
7748     else if (move_pattern == MV_TURNING_RIGHT)
7749       MovDir[x][y] = right_dir;
7750     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754     else if (move_pattern == MV_TURNING_RANDOM)
7755       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756                       can_turn_right && !can_turn_left ? right_dir :
7757                       RND(2) ? left_dir : right_dir);
7758     else if (can_turn_left && can_turn_right)
7759       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760     else if (can_turn_left)
7761       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762     else if (can_turn_right)
7763       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7764     else
7765       MovDir[x][y] = back_dir;
7766
7767     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768   }
7769   else if (move_pattern == MV_HORIZONTAL ||
7770            move_pattern == MV_VERTICAL)
7771   {
7772     if (move_pattern & old_move_dir)
7773       MovDir[x][y] = back_dir;
7774     else if (move_pattern == MV_HORIZONTAL)
7775       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776     else if (move_pattern == MV_VERTICAL)
7777       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7778
7779     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7780   }
7781   else if (move_pattern & MV_ANY_DIRECTION)
7782   {
7783     MovDir[x][y] = move_pattern;
7784     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern & MV_WIND_DIRECTION)
7787   {
7788     MovDir[x][y] = game.wind_direction;
7789     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7790   }
7791   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7792   {
7793     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794       MovDir[x][y] = left_dir;
7795     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796       MovDir[x][y] = right_dir;
7797
7798     if (MovDir[x][y] != old_move_dir)
7799       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7800   }
7801   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7802   {
7803     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804       MovDir[x][y] = right_dir;
7805     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806       MovDir[x][y] = left_dir;
7807
7808     if (MovDir[x][y] != old_move_dir)
7809       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7810   }
7811   else if (move_pattern == MV_TOWARDS_PLAYER ||
7812            move_pattern == MV_AWAY_FROM_PLAYER)
7813   {
7814     int attr_x = -1, attr_y = -1;
7815     int newx, newy;
7816     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7817
7818     if (AllPlayersGone)
7819     {
7820       attr_x = ExitX;
7821       attr_y = ExitY;
7822     }
7823     else
7824     {
7825       int i;
7826
7827       for (i = 0; i < MAX_PLAYERS; i++)
7828       {
7829         struct PlayerInfo *player = &stored_player[i];
7830         int jx = player->jx, jy = player->jy;
7831
7832         if (!player->active)
7833           continue;
7834
7835         if (attr_x == -1 ||
7836             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7837         {
7838           attr_x = jx;
7839           attr_y = jy;
7840         }
7841       }
7842     }
7843
7844     MovDir[x][y] = MV_NONE;
7845     if (attr_x < x)
7846       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847     else if (attr_x > x)
7848       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7849     if (attr_y < y)
7850       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851     else if (attr_y > y)
7852       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7853
7854     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7855
7856     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7857     {
7858       boolean first_horiz = RND(2);
7859       int new_move_dir = MovDir[x][y];
7860
7861       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7862       {
7863         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7865
7866         return;
7867       }
7868
7869       MovDir[x][y] =
7870         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871       Moving2Blocked(x, y, &newx, &newy);
7872
7873       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7874         return;
7875
7876       MovDir[x][y] =
7877         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878       Moving2Blocked(x, y, &newx, &newy);
7879
7880       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7881         return;
7882
7883       MovDir[x][y] = old_move_dir;
7884     }
7885   }
7886   else if (move_pattern == MV_WHEN_PUSHED ||
7887            move_pattern == MV_WHEN_DROPPED)
7888   {
7889     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890       MovDir[x][y] = MV_NONE;
7891
7892     MovDelay[x][y] = 0;
7893   }
7894   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7895   {
7896     static int test_xy[7][2] =
7897     {
7898       { 0, -1 },
7899       { -1, 0 },
7900       { +1, 0 },
7901       { 0, +1 },
7902       { 0, -1 },
7903       { -1, 0 },
7904       { +1, 0 },
7905     };
7906     static int test_dir[7] =
7907     {
7908       MV_UP,
7909       MV_LEFT,
7910       MV_RIGHT,
7911       MV_DOWN,
7912       MV_UP,
7913       MV_LEFT,
7914       MV_RIGHT,
7915     };
7916     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917     int move_preference = -1000000;     /* start with very low preference */
7918     int new_move_dir = MV_NONE;
7919     int start_test = RND(4);
7920     int i;
7921
7922     for (i = 0; i < NUM_DIRECTIONS; i++)
7923     {
7924       int move_dir = test_dir[start_test + i];
7925       int move_dir_preference;
7926
7927       xx = x + test_xy[start_test + i][0];
7928       yy = y + test_xy[start_test + i][1];
7929
7930       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7932       {
7933         new_move_dir = move_dir;
7934
7935         break;
7936       }
7937
7938       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7939         continue;
7940
7941       move_dir_preference = -1 * RunnerVisit[xx][yy];
7942       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943         move_dir_preference = PlayerVisit[xx][yy];
7944
7945       if (move_dir_preference > move_preference)
7946       {
7947         /* prefer field that has not been visited for the longest time */
7948         move_preference = move_dir_preference;
7949         new_move_dir = move_dir;
7950       }
7951       else if (move_dir_preference == move_preference &&
7952                move_dir == old_move_dir)
7953       {
7954         /* prefer last direction when all directions are preferred equally */
7955         move_preference = move_dir_preference;
7956         new_move_dir = move_dir;
7957       }
7958     }
7959
7960     MovDir[x][y] = new_move_dir;
7961     if (old_move_dir != new_move_dir)
7962       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7963   }
7964 }
7965
7966 static void TurnRound(int x, int y)
7967 {
7968   int direction = MovDir[x][y];
7969
7970   TurnRoundExt(x, y);
7971
7972   GfxDir[x][y] = MovDir[x][y];
7973
7974   if (direction != MovDir[x][y])
7975     GfxFrame[x][y] = 0;
7976
7977   if (MovDelay[x][y])
7978     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7979
7980   ResetGfxFrame(x, y, FALSE);
7981 }
7982
7983 static boolean JustBeingPushed(int x, int y)
7984 {
7985   int i;
7986
7987   for (i = 0; i < MAX_PLAYERS; i++)
7988   {
7989     struct PlayerInfo *player = &stored_player[i];
7990
7991     if (player->active && player->is_pushing && player->MovPos)
7992     {
7993       int next_jx = player->jx + (player->jx - player->last_jx);
7994       int next_jy = player->jy + (player->jy - player->last_jy);
7995
7996       if (x == next_jx && y == next_jy)
7997         return TRUE;
7998     }
7999   }
8000
8001   return FALSE;
8002 }
8003
8004 void StartMoving(int x, int y)
8005 {
8006   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
8007   int element = Feld[x][y];
8008
8009   if (Stop[x][y])
8010     return;
8011
8012   if (MovDelay[x][y] == 0)
8013     GfxAction[x][y] = ACTION_DEFAULT;
8014
8015   if (CAN_FALL(element) && y < lev_fieldy - 1)
8016   {
8017     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8018         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019       if (JustBeingPushed(x, y))
8020         return;
8021
8022     if (element == EL_QUICKSAND_FULL)
8023     {
8024       if (IS_FREE(x, y + 1))
8025       {
8026         InitMovingField(x, y, MV_DOWN);
8027         started_moving = TRUE;
8028
8029         Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032           Store[x][y] = EL_ROCK;
8033 #else
8034         Store[x][y] = EL_ROCK;
8035 #endif
8036
8037         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8038       }
8039       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8040       {
8041         if (!MovDelay[x][y])
8042         {
8043           MovDelay[x][y] = TILEY + 1;
8044
8045           ResetGfxAnimation(x, y);
8046           ResetGfxAnimation(x, y + 1);
8047         }
8048
8049         if (MovDelay[x][y])
8050         {
8051           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8053
8054           MovDelay[x][y]--;
8055           if (MovDelay[x][y])
8056             return;
8057         }
8058
8059         Feld[x][y] = EL_QUICKSAND_EMPTY;
8060         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061         Store[x][y + 1] = Store[x][y];
8062         Store[x][y] = 0;
8063
8064         PlayLevelSoundAction(x, y, ACTION_FILLING);
8065       }
8066       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         if (!MovDelay[x][y])
8069         {
8070           MovDelay[x][y] = TILEY + 1;
8071
8072           ResetGfxAnimation(x, y);
8073           ResetGfxAnimation(x, y + 1);
8074         }
8075
8076         if (MovDelay[x][y])
8077         {
8078           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8080
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Feld[x][y] = EL_QUICKSAND_EMPTY;
8087         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088         Store[x][y + 1] = Store[x][y];
8089         Store[x][y] = 0;
8090
8091         PlayLevelSoundAction(x, y, ACTION_FILLING);
8092       }
8093     }
8094     else if (element == EL_QUICKSAND_FAST_FULL)
8095     {
8096       if (IS_FREE(x, y + 1))
8097       {
8098         InitMovingField(x, y, MV_DOWN);
8099         started_moving = TRUE;
8100
8101         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104           Store[x][y] = EL_ROCK;
8105 #else
8106         Store[x][y] = EL_ROCK;
8107 #endif
8108
8109         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8110       }
8111       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8112       {
8113         if (!MovDelay[x][y])
8114         {
8115           MovDelay[x][y] = TILEY + 1;
8116
8117           ResetGfxAnimation(x, y);
8118           ResetGfxAnimation(x, y + 1);
8119         }
8120
8121         if (MovDelay[x][y])
8122         {
8123           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8125
8126           MovDelay[x][y]--;
8127           if (MovDelay[x][y])
8128             return;
8129         }
8130
8131         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133         Store[x][y + 1] = Store[x][y];
8134         Store[x][y] = 0;
8135
8136         PlayLevelSoundAction(x, y, ACTION_FILLING);
8137       }
8138       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8139       {
8140         if (!MovDelay[x][y])
8141         {
8142           MovDelay[x][y] = TILEY + 1;
8143
8144           ResetGfxAnimation(x, y);
8145           ResetGfxAnimation(x, y + 1);
8146         }
8147
8148         if (MovDelay[x][y])
8149         {
8150           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8152
8153           MovDelay[x][y]--;
8154           if (MovDelay[x][y])
8155             return;
8156         }
8157
8158         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160         Store[x][y + 1] = Store[x][y];
8161         Store[x][y] = 0;
8162
8163         PlayLevelSoundAction(x, y, ACTION_FILLING);
8164       }
8165     }
8166     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8168     {
8169       InitMovingField(x, y, MV_DOWN);
8170       started_moving = TRUE;
8171
8172       Feld[x][y] = EL_QUICKSAND_FILLING;
8173       Store[x][y] = element;
8174
8175       PlayLevelSoundAction(x, y, ACTION_FILLING);
8176     }
8177     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8179     {
8180       InitMovingField(x, y, MV_DOWN);
8181       started_moving = TRUE;
8182
8183       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184       Store[x][y] = element;
8185
8186       PlayLevelSoundAction(x, y, ACTION_FILLING);
8187     }
8188     else if (element == EL_MAGIC_WALL_FULL)
8189     {
8190       if (IS_FREE(x, y + 1))
8191       {
8192         InitMovingField(x, y, MV_DOWN);
8193         started_moving = TRUE;
8194
8195         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196         Store[x][y] = EL_CHANGED(Store[x][y]);
8197       }
8198       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8199       {
8200         if (!MovDelay[x][y])
8201           MovDelay[x][y] = TILEY / 4 + 1;
8202
8203         if (MovDelay[x][y])
8204         {
8205           MovDelay[x][y]--;
8206           if (MovDelay[x][y])
8207             return;
8208         }
8209
8210         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8213         Store[x][y] = 0;
8214       }
8215     }
8216     else if (element == EL_BD_MAGIC_WALL_FULL)
8217     {
8218       if (IS_FREE(x, y + 1))
8219       {
8220         InitMovingField(x, y, MV_DOWN);
8221         started_moving = TRUE;
8222
8223         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8225       }
8226       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8227       {
8228         if (!MovDelay[x][y])
8229           MovDelay[x][y] = TILEY / 4 + 1;
8230
8231         if (MovDelay[x][y])
8232         {
8233           MovDelay[x][y]--;
8234           if (MovDelay[x][y])
8235             return;
8236         }
8237
8238         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8241         Store[x][y] = 0;
8242       }
8243     }
8244     else if (element == EL_DC_MAGIC_WALL_FULL)
8245     {
8246       if (IS_FREE(x, y + 1))
8247       {
8248         InitMovingField(x, y, MV_DOWN);
8249         started_moving = TRUE;
8250
8251         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8253       }
8254       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8255       {
8256         if (!MovDelay[x][y])
8257           MovDelay[x][y] = TILEY / 4 + 1;
8258
8259         if (MovDelay[x][y])
8260         {
8261           MovDelay[x][y]--;
8262           if (MovDelay[x][y])
8263             return;
8264         }
8265
8266         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8269         Store[x][y] = 0;
8270       }
8271     }
8272     else if ((CAN_PASS_MAGIC_WALL(element) &&
8273               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275              (CAN_PASS_DC_MAGIC_WALL(element) &&
8276               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8277
8278     {
8279       InitMovingField(x, y, MV_DOWN);
8280       started_moving = TRUE;
8281
8282       Feld[x][y] =
8283         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285          EL_DC_MAGIC_WALL_FILLING);
8286       Store[x][y] = element;
8287     }
8288     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8289     {
8290       SplashAcid(x, y + 1);
8291
8292       InitMovingField(x, y, MV_DOWN);
8293       started_moving = TRUE;
8294
8295       Store[x][y] = EL_ACID;
8296     }
8297     else if (
8298 #if USE_FIX_IMPACT_COLLISION
8299              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8301 #else
8302              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8304 #endif
8305              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306               CAN_FALL(element) && WasJustFalling[x][y] &&
8307               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8308
8309              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311               (Feld[x][y + 1] == EL_BLOCKED)))
8312     {
8313       /* this is needed for a special case not covered by calling "Impact()"
8314          from "ContinueMoving()": if an element moves to a tile directly below
8315          another element which was just falling on that tile (which was empty
8316          in the previous frame), the falling element above would just stop
8317          instead of smashing the element below (in previous version, the above
8318          element was just checked for "moving" instead of "falling", resulting
8319          in incorrect smashes caused by horizontal movement of the above
8320          element; also, the case of the player being the element to smash was
8321          simply not covered here... :-/ ) */
8322
8323       CheckCollision[x][y] = 0;
8324       CheckImpact[x][y] = 0;
8325
8326       Impact(x, y);
8327     }
8328     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8329     {
8330       if (MovDir[x][y] == MV_NONE)
8331       {
8332         InitMovingField(x, y, MV_DOWN);
8333         started_moving = TRUE;
8334       }
8335     }
8336     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8337     {
8338       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339         MovDir[x][y] = MV_DOWN;
8340
8341       InitMovingField(x, y, MV_DOWN);
8342       started_moving = TRUE;
8343     }
8344     else if (element == EL_AMOEBA_DROP)
8345     {
8346       Feld[x][y] = EL_AMOEBA_GROWING;
8347       Store[x][y] = EL_AMOEBA_WET;
8348     }
8349     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8353     {
8354       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8355                                 (IS_FREE(x - 1, y + 1) ||
8356                                  Feld[x - 1][y + 1] == EL_ACID));
8357       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358                                 (IS_FREE(x + 1, y + 1) ||
8359                                  Feld[x + 1][y + 1] == EL_ACID));
8360       boolean can_fall_any  = (can_fall_left || can_fall_right);
8361       boolean can_fall_both = (can_fall_left && can_fall_right);
8362       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8363
8364 #if USE_NEW_ALL_SLIPPERY
8365       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8366       {
8367         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368           can_fall_right = FALSE;
8369         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370           can_fall_left = FALSE;
8371         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372           can_fall_right = FALSE;
8373         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374           can_fall_left = FALSE;
8375
8376         can_fall_any  = (can_fall_left || can_fall_right);
8377         can_fall_both = FALSE;
8378       }
8379 #else
8380       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8381       {
8382         if (slippery_type == SLIPPERY_ONLY_LEFT)
8383           can_fall_right = FALSE;
8384         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385           can_fall_left = FALSE;
8386         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387           can_fall_right = FALSE;
8388         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389           can_fall_left = FALSE;
8390
8391         can_fall_any  = (can_fall_left || can_fall_right);
8392         can_fall_both = (can_fall_left && can_fall_right);
8393       }
8394 #endif
8395
8396 #if USE_NEW_ALL_SLIPPERY
8397 #else
8398 #if USE_NEW_SP_SLIPPERY
8399       /* !!! better use the same properties as for custom elements here !!! */
8400       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8402       {
8403         can_fall_right = FALSE;         /* slip down on left side */
8404         can_fall_both = FALSE;
8405       }
8406 #endif
8407 #endif
8408
8409 #if USE_NEW_ALL_SLIPPERY
8410       if (can_fall_both)
8411       {
8412         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413           can_fall_right = FALSE;       /* slip down on left side */
8414         else
8415           can_fall_left = !(can_fall_right = RND(2));
8416
8417         can_fall_both = FALSE;
8418       }
8419 #else
8420       if (can_fall_both)
8421       {
8422         if (game.emulation == EMU_BOULDERDASH ||
8423             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424           can_fall_right = FALSE;       /* slip down on left side */
8425         else
8426           can_fall_left = !(can_fall_right = RND(2));
8427
8428         can_fall_both = FALSE;
8429       }
8430 #endif
8431
8432       if (can_fall_any)
8433       {
8434         /* if not determined otherwise, prefer left side for slipping down */
8435         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436         started_moving = TRUE;
8437       }
8438     }
8439 #if 0
8440     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8441 #else
8442     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8443 #endif
8444     {
8445       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8446       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448       int belt_dir = game.belt_dir[belt_nr];
8449
8450       if ((belt_dir == MV_LEFT  && left_is_free) ||
8451           (belt_dir == MV_RIGHT && right_is_free))
8452       {
8453         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8454
8455         InitMovingField(x, y, belt_dir);
8456         started_moving = TRUE;
8457
8458         Pushed[x][y] = TRUE;
8459         Pushed[nextx][y] = TRUE;
8460
8461         GfxAction[x][y] = ACTION_DEFAULT;
8462       }
8463       else
8464       {
8465         MovDir[x][y] = 0;       /* if element was moving, stop it */
8466       }
8467     }
8468   }
8469
8470   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8471 #if 0
8472   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8473 #else
8474   if (CAN_MOVE(element) && !started_moving)
8475 #endif
8476   {
8477     int move_pattern = element_info[element].move_pattern;
8478     int newx, newy;
8479
8480 #if 0
8481 #if DEBUG
8482     if (MovDir[x][y] == MV_NONE)
8483     {
8484       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485              x, y, element, element_info[element].token_name);
8486       printf("StartMoving(): This should never happen!\n");
8487     }
8488 #endif
8489 #endif
8490
8491     Moving2Blocked(x, y, &newx, &newy);
8492
8493     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8494       return;
8495
8496     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8498     {
8499       WasJustMoving[x][y] = 0;
8500       CheckCollision[x][y] = 0;
8501
8502       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8503
8504       if (Feld[x][y] != element)        /* element has changed */
8505         return;
8506     }
8507
8508     if (!MovDelay[x][y])        /* start new movement phase */
8509     {
8510       /* all objects that can change their move direction after each step
8511          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8512
8513       if (element != EL_YAMYAM &&
8514           element != EL_DARK_YAMYAM &&
8515           element != EL_PACMAN &&
8516           !(move_pattern & MV_ANY_DIRECTION) &&
8517           move_pattern != MV_TURNING_LEFT &&
8518           move_pattern != MV_TURNING_RIGHT &&
8519           move_pattern != MV_TURNING_LEFT_RIGHT &&
8520           move_pattern != MV_TURNING_RIGHT_LEFT &&
8521           move_pattern != MV_TURNING_RANDOM)
8522       {
8523         TurnRound(x, y);
8524
8525         if (MovDelay[x][y] && (element == EL_BUG ||
8526                                element == EL_SPACESHIP ||
8527                                element == EL_SP_SNIKSNAK ||
8528                                element == EL_SP_ELECTRON ||
8529                                element == EL_MOLE))
8530           TEST_DrawLevelField(x, y);
8531       }
8532     }
8533
8534     if (MovDelay[x][y])         /* wait some time before next movement */
8535     {
8536       MovDelay[x][y]--;
8537
8538       if (element == EL_ROBOT ||
8539           element == EL_YAMYAM ||
8540           element == EL_DARK_YAMYAM)
8541       {
8542         DrawLevelElementAnimationIfNeeded(x, y, element);
8543         PlayLevelSoundAction(x, y, ACTION_WAITING);
8544       }
8545       else if (element == EL_SP_ELECTRON)
8546         DrawLevelElementAnimationIfNeeded(x, y, element);
8547       else if (element == EL_DRAGON)
8548       {
8549         int i;
8550         int dir = MovDir[x][y];
8551         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8553         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8554                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8555                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8556                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8558
8559         GfxAction[x][y] = ACTION_ATTACKING;
8560
8561         if (IS_PLAYER(x, y))
8562           DrawPlayerField(x, y);
8563         else
8564           TEST_DrawLevelField(x, y);
8565
8566         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8567
8568         for (i = 1; i <= 3; i++)
8569         {
8570           int xx = x + i * dx;
8571           int yy = y + i * dy;
8572           int sx = SCREENX(xx);
8573           int sy = SCREENY(yy);
8574           int flame_graphic = graphic + (i - 1);
8575
8576           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8577             break;
8578
8579           if (MovDelay[x][y])
8580           {
8581             int flamed = MovingOrBlocked2Element(xx, yy);
8582
8583             /* !!! */
8584 #if 0
8585             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8586               Bang(xx, yy);
8587             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588               RemoveMovingField(xx, yy);
8589             else
8590               RemoveField(xx, yy);
8591 #else
8592             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8593               Bang(xx, yy);
8594             else
8595               RemoveMovingField(xx, yy);
8596 #endif
8597
8598             ChangeDelay[xx][yy] = 0;
8599
8600             Feld[xx][yy] = EL_FLAMES;
8601
8602             if (IN_SCR_FIELD(sx, sy))
8603             {
8604               TEST_DrawLevelFieldCrumbled(xx, yy);
8605               DrawGraphic(sx, sy, flame_graphic, frame);
8606             }
8607           }
8608           else
8609           {
8610             if (Feld[xx][yy] == EL_FLAMES)
8611               Feld[xx][yy] = EL_EMPTY;
8612             TEST_DrawLevelField(xx, yy);
8613           }
8614         }
8615       }
8616
8617       if (MovDelay[x][y])       /* element still has to wait some time */
8618       {
8619         PlayLevelSoundAction(x, y, ACTION_WAITING);
8620
8621         return;
8622       }
8623     }
8624
8625     /* now make next step */
8626
8627     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8628
8629     if (DONT_COLLIDE_WITH(element) &&
8630         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631         !PLAYER_ENEMY_PROTECTED(newx, newy))
8632     {
8633       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8634
8635       return;
8636     }
8637
8638     else if (CAN_MOVE_INTO_ACID(element) &&
8639              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641              (MovDir[x][y] == MV_DOWN ||
8642               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8643     {
8644       SplashAcid(newx, newy);
8645       Store[x][y] = EL_ACID;
8646     }
8647     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8648     {
8649       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8653       {
8654         RemoveField(x, y);
8655         TEST_DrawLevelField(x, y);
8656
8657         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8660
8661         local_player->friends_still_needed--;
8662         if (!local_player->friends_still_needed &&
8663             !local_player->GameOver && AllPlayersGone)
8664           PlayerWins(local_player);
8665
8666         return;
8667       }
8668       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8669       {
8670         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671           TEST_DrawLevelField(newx, newy);
8672         else
8673           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8674       }
8675       else if (!IS_FREE(newx, newy))
8676       {
8677         GfxAction[x][y] = ACTION_WAITING;
8678
8679         if (IS_PLAYER(x, y))
8680           DrawPlayerField(x, y);
8681         else
8682           TEST_DrawLevelField(x, y);
8683
8684         return;
8685       }
8686     }
8687     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8688     {
8689       if (IS_FOOD_PIG(Feld[newx][newy]))
8690       {
8691         if (IS_MOVING(newx, newy))
8692           RemoveMovingField(newx, newy);
8693         else
8694         {
8695           Feld[newx][newy] = EL_EMPTY;
8696           TEST_DrawLevelField(newx, newy);
8697         }
8698
8699         PlayLevelSound(x, y, SND_PIG_DIGGING);
8700       }
8701       else if (!IS_FREE(newx, newy))
8702       {
8703         if (IS_PLAYER(x, y))
8704           DrawPlayerField(x, y);
8705         else
8706           TEST_DrawLevelField(x, y);
8707
8708         return;
8709       }
8710     }
8711     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8712     {
8713       if (Store[x][y] != EL_EMPTY)
8714       {
8715         boolean can_clone = FALSE;
8716         int xx, yy;
8717
8718         /* check if element to clone is still there */
8719         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8720         {
8721           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8722           {
8723             can_clone = TRUE;
8724
8725             break;
8726           }
8727         }
8728
8729         /* cannot clone or target field not free anymore -- do not clone */
8730         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731           Store[x][y] = EL_EMPTY;
8732       }
8733
8734       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8735       {
8736         if (IS_MV_DIAGONAL(MovDir[x][y]))
8737         {
8738           int diagonal_move_dir = MovDir[x][y];
8739           int stored = Store[x][y];
8740           int change_delay = 8;
8741           int graphic;
8742
8743           /* android is moving diagonally */
8744
8745           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8746
8747           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748           GfxElement[x][y] = EL_EMC_ANDROID;
8749           GfxAction[x][y] = ACTION_SHRINKING;
8750           GfxDir[x][y] = diagonal_move_dir;
8751           ChangeDelay[x][y] = change_delay;
8752
8753           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8754                                    GfxDir[x][y]);
8755
8756           DrawLevelGraphicAnimation(x, y, graphic);
8757           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8758
8759           if (Feld[newx][newy] == EL_ACID)
8760           {
8761             SplashAcid(newx, newy);
8762
8763             return;
8764           }
8765
8766           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8767
8768           Store[newx][newy] = EL_EMC_ANDROID;
8769           GfxElement[newx][newy] = EL_EMC_ANDROID;
8770           GfxAction[newx][newy] = ACTION_GROWING;
8771           GfxDir[newx][newy] = diagonal_move_dir;
8772           ChangeDelay[newx][newy] = change_delay;
8773
8774           graphic = el_act_dir2img(GfxElement[newx][newy],
8775                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8776
8777           DrawLevelGraphicAnimation(newx, newy, graphic);
8778           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8779
8780           return;
8781         }
8782         else
8783         {
8784           Feld[newx][newy] = EL_EMPTY;
8785           TEST_DrawLevelField(newx, newy);
8786
8787           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8788         }
8789       }
8790       else if (!IS_FREE(newx, newy))
8791       {
8792 #if 0
8793         if (IS_PLAYER(x, y))
8794           DrawPlayerField(x, y);
8795         else
8796           TEST_DrawLevelField(x, y);
8797 #endif
8798
8799         return;
8800       }
8801     }
8802     else if (IS_CUSTOM_ELEMENT(element) &&
8803              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8804     {
8805 #if 1
8806       if (!DigFieldByCE(newx, newy, element))
8807         return;
8808 #else
8809       int new_element = Feld[newx][newy];
8810
8811       if (!IS_FREE(newx, newy))
8812       {
8813         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8815                       ACTION_BREAKING);
8816
8817         /* no element can dig solid indestructible elements */
8818         if (IS_INDESTRUCTIBLE(new_element) &&
8819             !IS_DIGGABLE(new_element) &&
8820             !IS_COLLECTIBLE(new_element))
8821           return;
8822
8823         if (AmoebaNr[newx][newy] &&
8824             (new_element == EL_AMOEBA_FULL ||
8825              new_element == EL_BD_AMOEBA ||
8826              new_element == EL_AMOEBA_GROWING))
8827         {
8828           AmoebaCnt[AmoebaNr[newx][newy]]--;
8829           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8830         }
8831
8832         if (IS_MOVING(newx, newy))
8833           RemoveMovingField(newx, newy);
8834         else
8835         {
8836           RemoveField(newx, newy);
8837           TEST_DrawLevelField(newx, newy);
8838         }
8839
8840         /* if digged element was about to explode, prevent the explosion */
8841         ExplodeField[newx][newy] = EX_TYPE_NONE;
8842
8843         PlayLevelSoundAction(x, y, action);
8844       }
8845
8846       Store[newx][newy] = EL_EMPTY;
8847
8848 #if 1
8849       /* this makes it possible to leave the removed element again */
8850       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851         Store[newx][newy] = new_element;
8852 #else
8853       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8854       {
8855         int move_leave_element = element_info[element].move_leave_element;
8856
8857         /* this makes it possible to leave the removed element again */
8858         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859                              new_element : move_leave_element);
8860       }
8861 #endif
8862
8863 #endif
8864
8865       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8866       {
8867         RunnerVisit[x][y] = FrameCounter;
8868         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8869       }
8870     }
8871     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8872     {
8873       if (!IS_FREE(newx, newy))
8874       {
8875         if (IS_PLAYER(x, y))
8876           DrawPlayerField(x, y);
8877         else
8878           TEST_DrawLevelField(x, y);
8879
8880         return;
8881       }
8882       else
8883       {
8884         boolean wanna_flame = !RND(10);
8885         int dx = newx - x, dy = newy - y;
8886         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8892
8893         if ((wanna_flame ||
8894              IS_CLASSIC_ENEMY(element1) ||
8895              IS_CLASSIC_ENEMY(element2)) &&
8896             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897             element1 != EL_FLAMES && element2 != EL_FLAMES)
8898         {
8899           ResetGfxAnimation(x, y);
8900           GfxAction[x][y] = ACTION_ATTACKING;
8901
8902           if (IS_PLAYER(x, y))
8903             DrawPlayerField(x, y);
8904           else
8905             TEST_DrawLevelField(x, y);
8906
8907           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8908
8909           MovDelay[x][y] = 50;
8910
8911           /* !!! */
8912 #if 0
8913           RemoveField(newx, newy);
8914 #endif
8915           Feld[newx][newy] = EL_FLAMES;
8916           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8917           {
8918 #if 0
8919             RemoveField(newx1, newy1);
8920 #endif
8921             Feld[newx1][newy1] = EL_FLAMES;
8922           }
8923           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8924           {
8925 #if 0
8926             RemoveField(newx2, newy2);
8927 #endif
8928             Feld[newx2][newy2] = EL_FLAMES;
8929           }
8930
8931           return;
8932         }
8933       }
8934     }
8935     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936              Feld[newx][newy] == EL_DIAMOND)
8937     {
8938       if (IS_MOVING(newx, newy))
8939         RemoveMovingField(newx, newy);
8940       else
8941       {
8942         Feld[newx][newy] = EL_EMPTY;
8943         TEST_DrawLevelField(newx, newy);
8944       }
8945
8946       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8947     }
8948     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8950     {
8951       if (AmoebaNr[newx][newy])
8952       {
8953         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955             Feld[newx][newy] == EL_BD_AMOEBA)
8956           AmoebaCnt[AmoebaNr[newx][newy]]--;
8957       }
8958
8959 #if 0
8960       /* !!! test !!! */
8961       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8962       {
8963         RemoveMovingField(newx, newy);
8964       }
8965 #else
8966       if (IS_MOVING(newx, newy))
8967       {
8968         RemoveMovingField(newx, newy);
8969       }
8970 #endif
8971       else
8972       {
8973         Feld[newx][newy] = EL_EMPTY;
8974         TEST_DrawLevelField(newx, newy);
8975       }
8976
8977       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8978     }
8979     else if ((element == EL_PACMAN || element == EL_MOLE)
8980              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8981     {
8982       if (AmoebaNr[newx][newy])
8983       {
8984         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986             Feld[newx][newy] == EL_BD_AMOEBA)
8987           AmoebaCnt[AmoebaNr[newx][newy]]--;
8988       }
8989
8990       if (element == EL_MOLE)
8991       {
8992         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8994
8995         ResetGfxAnimation(x, y);
8996         GfxAction[x][y] = ACTION_DIGGING;
8997         TEST_DrawLevelField(x, y);
8998
8999         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
9000
9001         return;                         /* wait for shrinking amoeba */
9002       }
9003       else      /* element == EL_PACMAN */
9004       {
9005         Feld[newx][newy] = EL_EMPTY;
9006         TEST_DrawLevelField(newx, newy);
9007         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9008       }
9009     }
9010     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9013     {
9014       /* wait for shrinking amoeba to completely disappear */
9015       return;
9016     }
9017     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9018     {
9019       /* object was running against a wall */
9020
9021       TurnRound(x, y);
9022
9023 #if 0
9024       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025       if (move_pattern & MV_ANY_DIRECTION &&
9026           move_pattern == MovDir[x][y])
9027       {
9028         int blocking_element =
9029           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9030
9031         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9032                                  MovDir[x][y]);
9033
9034         element = Feld[x][y];   /* element might have changed */
9035       }
9036 #endif
9037
9038       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9039         DrawLevelElementAnimation(x, y, element);
9040
9041       if (DONT_TOUCH(element))
9042         TestIfBadThingTouchesPlayer(x, y);
9043
9044       return;
9045     }
9046
9047     InitMovingField(x, y, MovDir[x][y]);
9048
9049     PlayLevelSoundAction(x, y, ACTION_MOVING);
9050   }
9051
9052   if (MovDir[x][y])
9053     ContinueMoving(x, y);
9054 }
9055
9056 void ContinueMoving(int x, int y)
9057 {
9058   int element = Feld[x][y];
9059   struct ElementInfo *ei = &element_info[element];
9060   int direction = MovDir[x][y];
9061   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9063   int newx = x + dx, newy = y + dy;
9064   int stored = Store[x][y];
9065   int stored_new = Store[newx][newy];
9066   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9067   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068   boolean last_line = (newy == lev_fieldy - 1);
9069
9070   MovPos[x][y] += getElementMoveStepsize(x, y);
9071
9072   if (pushed_by_player) /* special case: moving object pushed by player */
9073     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9074
9075   if (ABS(MovPos[x][y]) < TILEX)
9076   {
9077 #if 0
9078     int ee = Feld[x][y];
9079     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9081
9082     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083            x, y, ABS(MovPos[x][y]),
9084            ee, gg, ff,
9085            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9086 #endif
9087
9088     TEST_DrawLevelField(x, y);
9089
9090     return;     /* element is still moving */
9091   }
9092
9093   /* element reached destination field */
9094
9095   Feld[x][y] = EL_EMPTY;
9096   Feld[newx][newy] = element;
9097   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9098
9099   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9100   {
9101     element = Feld[newx][newy] = EL_ACID;
9102   }
9103   else if (element == EL_MOLE)
9104   {
9105     Feld[x][y] = EL_SAND;
9106
9107     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9108   }
9109   else if (element == EL_QUICKSAND_FILLING)
9110   {
9111     element = Feld[newx][newy] = get_next_element(element);
9112     Store[newx][newy] = Store[x][y];
9113   }
9114   else if (element == EL_QUICKSAND_EMPTYING)
9115   {
9116     Feld[x][y] = get_next_element(element);
9117     element = Feld[newx][newy] = Store[x][y];
9118   }
9119   else if (element == EL_QUICKSAND_FAST_FILLING)
9120   {
9121     element = Feld[newx][newy] = get_next_element(element);
9122     Store[newx][newy] = Store[x][y];
9123   }
9124   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9125   {
9126     Feld[x][y] = get_next_element(element);
9127     element = Feld[newx][newy] = Store[x][y];
9128   }
9129   else if (element == EL_MAGIC_WALL_FILLING)
9130   {
9131     element = Feld[newx][newy] = get_next_element(element);
9132     if (!game.magic_wall_active)
9133       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134     Store[newx][newy] = Store[x][y];
9135   }
9136   else if (element == EL_MAGIC_WALL_EMPTYING)
9137   {
9138     Feld[x][y] = get_next_element(element);
9139     if (!game.magic_wall_active)
9140       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141     element = Feld[newx][newy] = Store[x][y];
9142
9143 #if USE_NEW_CUSTOM_VALUE
9144     InitField(newx, newy, FALSE);
9145 #endif
9146   }
9147   else if (element == EL_BD_MAGIC_WALL_FILLING)
9148   {
9149     element = Feld[newx][newy] = get_next_element(element);
9150     if (!game.magic_wall_active)
9151       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152     Store[newx][newy] = Store[x][y];
9153   }
9154   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9155   {
9156     Feld[x][y] = get_next_element(element);
9157     if (!game.magic_wall_active)
9158       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159     element = Feld[newx][newy] = Store[x][y];
9160
9161 #if USE_NEW_CUSTOM_VALUE
9162     InitField(newx, newy, FALSE);
9163 #endif
9164   }
9165   else if (element == EL_DC_MAGIC_WALL_FILLING)
9166   {
9167     element = Feld[newx][newy] = get_next_element(element);
9168     if (!game.magic_wall_active)
9169       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170     Store[newx][newy] = Store[x][y];
9171   }
9172   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9173   {
9174     Feld[x][y] = get_next_element(element);
9175     if (!game.magic_wall_active)
9176       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177     element = Feld[newx][newy] = Store[x][y];
9178
9179 #if USE_NEW_CUSTOM_VALUE
9180     InitField(newx, newy, FALSE);
9181 #endif
9182   }
9183   else if (element == EL_AMOEBA_DROPPING)
9184   {
9185     Feld[x][y] = get_next_element(element);
9186     element = Feld[newx][newy] = Store[x][y];
9187   }
9188   else if (element == EL_SOKOBAN_OBJECT)
9189   {
9190     if (Back[x][y])
9191       Feld[x][y] = Back[x][y];
9192
9193     if (Back[newx][newy])
9194       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9195
9196     Back[x][y] = Back[newx][newy] = 0;
9197   }
9198
9199   Store[x][y] = EL_EMPTY;
9200   MovPos[x][y] = 0;
9201   MovDir[x][y] = 0;
9202   MovDelay[x][y] = 0;
9203
9204   MovDelay[newx][newy] = 0;
9205
9206   if (CAN_CHANGE_OR_HAS_ACTION(element))
9207   {
9208     /* copy element change control values to new field */
9209     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210     ChangePage[newx][newy]  = ChangePage[x][y];
9211     ChangeCount[newx][newy] = ChangeCount[x][y];
9212     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9213   }
9214
9215 #if USE_NEW_CUSTOM_VALUE
9216   CustomValue[newx][newy] = CustomValue[x][y];
9217 #endif
9218
9219   ChangeDelay[x][y] = 0;
9220   ChangePage[x][y] = -1;
9221   ChangeCount[x][y] = 0;
9222   ChangeEvent[x][y] = -1;
9223
9224 #if USE_NEW_CUSTOM_VALUE
9225   CustomValue[x][y] = 0;
9226 #endif
9227
9228   /* copy animation control values to new field */
9229   GfxFrame[newx][newy]  = GfxFrame[x][y];
9230   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9231   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9232   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9233
9234   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9235
9236   /* some elements can leave other elements behind after moving */
9237 #if 1
9238   if (ei->move_leave_element != EL_EMPTY &&
9239       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9241 #else
9242   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9245 #endif
9246   {
9247     int move_leave_element = ei->move_leave_element;
9248
9249 #if 1
9250 #if 1
9251     /* this makes it possible to leave the removed element again */
9252     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9254 #else
9255     /* this makes it possible to leave the removed element again */
9256     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257       move_leave_element = stored;
9258 #endif
9259 #else
9260     /* this makes it possible to leave the removed element again */
9261     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263       move_leave_element = stored;
9264 #endif
9265
9266     Feld[x][y] = move_leave_element;
9267
9268     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269       MovDir[x][y] = direction;
9270
9271     InitField(x, y, FALSE);
9272
9273     if (GFX_CRUMBLED(Feld[x][y]))
9274       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9275
9276     if (ELEM_IS_PLAYER(move_leave_element))
9277       RelocatePlayer(x, y, move_leave_element);
9278   }
9279
9280   /* do this after checking for left-behind element */
9281   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9282
9283   if (!CAN_MOVE(element) ||
9284       (CAN_FALL(element) && direction == MV_DOWN &&
9285        (element == EL_SPRING ||
9286         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288     GfxDir[x][y] = MovDir[newx][newy] = 0;
9289
9290   TEST_DrawLevelField(x, y);
9291   TEST_DrawLevelField(newx, newy);
9292
9293   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9294
9295   /* prevent pushed element from moving on in pushed direction */
9296   if (pushed_by_player && CAN_MOVE(element) &&
9297       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298       !(element_info[element].move_pattern & direction))
9299     TurnRound(newx, newy);
9300
9301   /* prevent elements on conveyor belt from moving on in last direction */
9302   if (pushed_by_conveyor && CAN_FALL(element) &&
9303       direction & MV_HORIZONTAL)
9304     MovDir[newx][newy] = 0;
9305
9306   if (!pushed_by_player)
9307   {
9308     int nextx = newx + dx, nexty = newy + dy;
9309     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9310
9311     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9312
9313     if (CAN_FALL(element) && direction == MV_DOWN)
9314       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9315
9316     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9318
9319 #if USE_FIX_IMPACT_COLLISION
9320     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9322 #endif
9323   }
9324
9325   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9326   {
9327     TestIfBadThingTouchesPlayer(newx, newy);
9328     TestIfBadThingTouchesFriend(newx, newy);
9329
9330     if (!IS_CUSTOM_ELEMENT(element))
9331       TestIfBadThingTouchesOtherBadThing(newx, newy);
9332   }
9333   else if (element == EL_PENGUIN)
9334     TestIfFriendTouchesBadThing(newx, newy);
9335
9336   if (DONT_GET_HIT_BY(element))
9337   {
9338     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9339   }
9340
9341   /* give the player one last chance (one more frame) to move away */
9342   if (CAN_FALL(element) && direction == MV_DOWN &&
9343       (last_line || (!IS_FREE(x, newy + 1) &&
9344                      (!IS_PLAYER(x, newy + 1) ||
9345                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9346     Impact(x, newy);
9347
9348   if (pushed_by_player && !game.use_change_when_pushing_bug)
9349   {
9350     int push_side = MV_DIR_OPPOSITE(direction);
9351     struct PlayerInfo *player = PLAYERINFO(x, y);
9352
9353     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354                                player->index_bit, push_side);
9355     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356                                         player->index_bit, push_side);
9357   }
9358
9359   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9360     MovDelay[newx][newy] = 1;
9361
9362   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9363
9364   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9365
9366 #if 0
9367   if (ChangePage[newx][newy] != -1)             /* delayed change */
9368   {
9369     int page = ChangePage[newx][newy];
9370     struct ElementChangeInfo *change = &ei->change_page[page];
9371
9372     ChangePage[newx][newy] = -1;
9373
9374     if (change->can_change)
9375     {
9376       if (ChangeElement(newx, newy, element, page))
9377       {
9378         if (change->post_change_function)
9379           change->post_change_function(newx, newy);
9380       }
9381     }
9382
9383     if (change->has_action)
9384       ExecuteCustomElementAction(newx, newy, element, page);
9385   }
9386 #endif
9387
9388   TestIfElementHitsCustomElement(newx, newy, direction);
9389   TestIfPlayerTouchesCustomElement(newx, newy);
9390   TestIfElementTouchesCustomElement(newx, newy);
9391
9392   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395                              MV_DIR_OPPOSITE(direction));
9396 }
9397
9398 int AmoebeNachbarNr(int ax, int ay)
9399 {
9400   int i;
9401   int element = Feld[ax][ay];
9402   int group_nr = 0;
9403   static int xy[4][2] =
9404   {
9405     { 0, -1 },
9406     { -1, 0 },
9407     { +1, 0 },
9408     { 0, +1 }
9409   };
9410
9411   for (i = 0; i < NUM_DIRECTIONS; i++)
9412   {
9413     int x = ax + xy[i][0];
9414     int y = ay + xy[i][1];
9415
9416     if (!IN_LEV_FIELD(x, y))
9417       continue;
9418
9419     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420       group_nr = AmoebaNr[x][y];
9421   }
9422
9423   return group_nr;
9424 }
9425
9426 void AmoebenVereinigen(int ax, int ay)
9427 {
9428   int i, x, y, xx, yy;
9429   int new_group_nr = AmoebaNr[ax][ay];
9430   static int xy[4][2] =
9431   {
9432     { 0, -1 },
9433     { -1, 0 },
9434     { +1, 0 },
9435     { 0, +1 }
9436   };
9437
9438   if (new_group_nr == 0)
9439     return;
9440
9441   for (i = 0; i < NUM_DIRECTIONS; i++)
9442   {
9443     x = ax + xy[i][0];
9444     y = ay + xy[i][1];
9445
9446     if (!IN_LEV_FIELD(x, y))
9447       continue;
9448
9449     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450          Feld[x][y] == EL_BD_AMOEBA ||
9451          Feld[x][y] == EL_AMOEBA_DEAD) &&
9452         AmoebaNr[x][y] != new_group_nr)
9453     {
9454       int old_group_nr = AmoebaNr[x][y];
9455
9456       if (old_group_nr == 0)
9457         return;
9458
9459       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460       AmoebaCnt[old_group_nr] = 0;
9461       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462       AmoebaCnt2[old_group_nr] = 0;
9463
9464       SCAN_PLAYFIELD(xx, yy)
9465       {
9466         if (AmoebaNr[xx][yy] == old_group_nr)
9467           AmoebaNr[xx][yy] = new_group_nr;
9468       }
9469     }
9470   }
9471 }
9472
9473 void AmoebeUmwandeln(int ax, int ay)
9474 {
9475   int i, x, y;
9476
9477   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9478   {
9479     int group_nr = AmoebaNr[ax][ay];
9480
9481 #ifdef DEBUG
9482     if (group_nr == 0)
9483     {
9484       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485       printf("AmoebeUmwandeln(): This should never happen!\n");
9486       return;
9487     }
9488 #endif
9489
9490     SCAN_PLAYFIELD(x, y)
9491     {
9492       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9493       {
9494         AmoebaNr[x][y] = 0;
9495         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9496       }
9497     }
9498
9499     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500                             SND_AMOEBA_TURNING_TO_GEM :
9501                             SND_AMOEBA_TURNING_TO_ROCK));
9502     Bang(ax, ay);
9503   }
9504   else
9505   {
9506     static int xy[4][2] =
9507     {
9508       { 0, -1 },
9509       { -1, 0 },
9510       { +1, 0 },
9511       { 0, +1 }
9512     };
9513
9514     for (i = 0; i < NUM_DIRECTIONS; i++)
9515     {
9516       x = ax + xy[i][0];
9517       y = ay + xy[i][1];
9518
9519       if (!IN_LEV_FIELD(x, y))
9520         continue;
9521
9522       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9523       {
9524         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525                               SND_AMOEBA_TURNING_TO_GEM :
9526                               SND_AMOEBA_TURNING_TO_ROCK));
9527         Bang(x, y);
9528       }
9529     }
9530   }
9531 }
9532
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9534 {
9535   int x, y;
9536   int group_nr = AmoebaNr[ax][ay];
9537   boolean done = FALSE;
9538
9539 #ifdef DEBUG
9540   if (group_nr == 0)
9541   {
9542     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9544     return;
9545   }
9546 #endif
9547
9548   SCAN_PLAYFIELD(x, y)
9549   {
9550     if (AmoebaNr[x][y] == group_nr &&
9551         (Feld[x][y] == EL_AMOEBA_DEAD ||
9552          Feld[x][y] == EL_BD_AMOEBA ||
9553          Feld[x][y] == EL_AMOEBA_GROWING))
9554     {
9555       AmoebaNr[x][y] = 0;
9556       Feld[x][y] = new_element;
9557       InitField(x, y, FALSE);
9558       TEST_DrawLevelField(x, y);
9559       done = TRUE;
9560     }
9561   }
9562
9563   if (done)
9564     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9566                             SND_BD_AMOEBA_TURNING_TO_GEM));
9567 }
9568
9569 void AmoebeWaechst(int x, int y)
9570 {
9571   static unsigned int sound_delay = 0;
9572   static unsigned int sound_delay_value = 0;
9573
9574   if (!MovDelay[x][y])          /* start new growing cycle */
9575   {
9576     MovDelay[x][y] = 7;
9577
9578     if (DelayReached(&sound_delay, sound_delay_value))
9579     {
9580       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581       sound_delay_value = 30;
9582     }
9583   }
9584
9585   if (MovDelay[x][y])           /* wait some time before growing bigger */
9586   {
9587     MovDelay[x][y]--;
9588     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9589     {
9590       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591                                            6 - MovDelay[x][y]);
9592
9593       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9594     }
9595
9596     if (!MovDelay[x][y])
9597     {
9598       Feld[x][y] = Store[x][y];
9599       Store[x][y] = 0;
9600       TEST_DrawLevelField(x, y);
9601     }
9602   }
9603 }
9604
9605 void AmoebaDisappearing(int x, int y)
9606 {
9607   static unsigned int sound_delay = 0;
9608   static unsigned int sound_delay_value = 0;
9609
9610   if (!MovDelay[x][y])          /* start new shrinking cycle */
9611   {
9612     MovDelay[x][y] = 7;
9613
9614     if (DelayReached(&sound_delay, sound_delay_value))
9615       sound_delay_value = 30;
9616   }
9617
9618   if (MovDelay[x][y])           /* wait some time before shrinking */
9619   {
9620     MovDelay[x][y]--;
9621     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9622     {
9623       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624                                            6 - MovDelay[x][y]);
9625
9626       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9627     }
9628
9629     if (!MovDelay[x][y])
9630     {
9631       Feld[x][y] = EL_EMPTY;
9632       TEST_DrawLevelField(x, y);
9633
9634       /* don't let mole enter this field in this cycle;
9635          (give priority to objects falling to this field from above) */
9636       Stop[x][y] = TRUE;
9637     }
9638   }
9639 }
9640
9641 void AmoebeAbleger(int ax, int ay)
9642 {
9643   int i;
9644   int element = Feld[ax][ay];
9645   int graphic = el2img(element);
9646   int newax = ax, neway = ay;
9647   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648   static int xy[4][2] =
9649   {
9650     { 0, -1 },
9651     { -1, 0 },
9652     { +1, 0 },
9653     { 0, +1 }
9654   };
9655
9656   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9657   {
9658     Feld[ax][ay] = EL_AMOEBA_DEAD;
9659     TEST_DrawLevelField(ax, ay);
9660     return;
9661   }
9662
9663   if (IS_ANIMATED(graphic))
9664     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9665
9666   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9667     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9668
9669   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9670   {
9671     MovDelay[ax][ay]--;
9672     if (MovDelay[ax][ay])
9673       return;
9674   }
9675
9676   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9677   {
9678     int start = RND(4);
9679     int x = ax + xy[start][0];
9680     int y = ay + xy[start][1];
9681
9682     if (!IN_LEV_FIELD(x, y))
9683       return;
9684
9685     if (IS_FREE(x, y) ||
9686         CAN_GROW_INTO(Feld[x][y]) ||
9687         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9689     {
9690       newax = x;
9691       neway = y;
9692     }
9693
9694     if (newax == ax && neway == ay)
9695       return;
9696   }
9697   else                          /* normal or "filled" (BD style) amoeba */
9698   {
9699     int start = RND(4);
9700     boolean waiting_for_player = FALSE;
9701
9702     for (i = 0; i < NUM_DIRECTIONS; i++)
9703     {
9704       int j = (start + i) % 4;
9705       int x = ax + xy[j][0];
9706       int y = ay + xy[j][1];
9707
9708       if (!IN_LEV_FIELD(x, y))
9709         continue;
9710
9711       if (IS_FREE(x, y) ||
9712           CAN_GROW_INTO(Feld[x][y]) ||
9713           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9715       {
9716         newax = x;
9717         neway = y;
9718         break;
9719       }
9720       else if (IS_PLAYER(x, y))
9721         waiting_for_player = TRUE;
9722     }
9723
9724     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9725     {
9726       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9727       {
9728         Feld[ax][ay] = EL_AMOEBA_DEAD;
9729         TEST_DrawLevelField(ax, ay);
9730         AmoebaCnt[AmoebaNr[ax][ay]]--;
9731
9732         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9733         {
9734           if (element == EL_AMOEBA_FULL)
9735             AmoebeUmwandeln(ax, ay);
9736           else if (element == EL_BD_AMOEBA)
9737             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9738         }
9739       }
9740       return;
9741     }
9742     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9743     {
9744       /* amoeba gets larger by growing in some direction */
9745
9746       int new_group_nr = AmoebaNr[ax][ay];
9747
9748 #ifdef DEBUG
9749   if (new_group_nr == 0)
9750   {
9751     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752     printf("AmoebeAbleger(): This should never happen!\n");
9753     return;
9754   }
9755 #endif
9756
9757       AmoebaNr[newax][neway] = new_group_nr;
9758       AmoebaCnt[new_group_nr]++;
9759       AmoebaCnt2[new_group_nr]++;
9760
9761       /* if amoeba touches other amoeba(s) after growing, unify them */
9762       AmoebenVereinigen(newax, neway);
9763
9764       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9765       {
9766         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9767         return;
9768       }
9769     }
9770   }
9771
9772   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773       (neway == lev_fieldy - 1 && newax != ax))
9774   {
9775     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9776     Store[newax][neway] = element;
9777   }
9778   else if (neway == ay || element == EL_EMC_DRIPPER)
9779   {
9780     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9781
9782     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9783   }
9784   else
9785   {
9786     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9787     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788     Store[ax][ay] = EL_AMOEBA_DROP;
9789     ContinueMoving(ax, ay);
9790     return;
9791   }
9792
9793   TEST_DrawLevelField(newax, neway);
9794 }
9795
9796 void Life(int ax, int ay)
9797 {
9798   int x1, y1, x2, y2;
9799   int life_time = 40;
9800   int element = Feld[ax][ay];
9801   int graphic = el2img(element);
9802   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9803                          level.biomaze);
9804   boolean changed = FALSE;
9805
9806   if (IS_ANIMATED(graphic))
9807     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9808
9809   if (Stop[ax][ay])
9810     return;
9811
9812   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9813     MovDelay[ax][ay] = life_time;
9814
9815   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9816   {
9817     MovDelay[ax][ay]--;
9818     if (MovDelay[ax][ay])
9819       return;
9820   }
9821
9822   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9823   {
9824     int xx = ax+x1, yy = ay+y1;
9825     int nachbarn = 0;
9826
9827     if (!IN_LEV_FIELD(xx, yy))
9828       continue;
9829
9830     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9831     {
9832       int x = xx+x2, y = yy+y2;
9833
9834       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9835         continue;
9836
9837       if (((Feld[x][y] == element ||
9838             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9839            !Stop[x][y]) ||
9840           (IS_FREE(x, y) && Stop[x][y]))
9841         nachbarn++;
9842     }
9843
9844     if (xx == ax && yy == ay)           /* field in the middle */
9845     {
9846       if (nachbarn < life_parameter[0] ||
9847           nachbarn > life_parameter[1])
9848       {
9849         Feld[xx][yy] = EL_EMPTY;
9850         if (!Stop[xx][yy])
9851           TEST_DrawLevelField(xx, yy);
9852         Stop[xx][yy] = TRUE;
9853         changed = TRUE;
9854       }
9855     }
9856     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857     {                                   /* free border field */
9858       if (nachbarn >= life_parameter[2] &&
9859           nachbarn <= life_parameter[3])
9860       {
9861         Feld[xx][yy] = element;
9862         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9863         if (!Stop[xx][yy])
9864           TEST_DrawLevelField(xx, yy);
9865         Stop[xx][yy] = TRUE;
9866         changed = TRUE;
9867       }
9868     }
9869   }
9870
9871   if (changed)
9872     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873                    SND_GAME_OF_LIFE_GROWING);
9874 }
9875
9876 static void InitRobotWheel(int x, int y)
9877 {
9878   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9879 }
9880
9881 static void RunRobotWheel(int x, int y)
9882 {
9883   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9884 }
9885
9886 static void StopRobotWheel(int x, int y)
9887 {
9888   if (ZX == x && ZY == y)
9889   {
9890     ZX = ZY = -1;
9891
9892     game.robot_wheel_active = FALSE;
9893   }
9894 }
9895
9896 static void InitTimegateWheel(int x, int y)
9897 {
9898   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9899 }
9900
9901 static void RunTimegateWheel(int x, int y)
9902 {
9903   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9904 }
9905
9906 static void InitMagicBallDelay(int x, int y)
9907 {
9908 #if 1
9909   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9910 #else
9911   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9912 #endif
9913 }
9914
9915 static void ActivateMagicBall(int bx, int by)
9916 {
9917   int x, y;
9918
9919   if (level.ball_random)
9920   {
9921     int pos_border = RND(8);    /* select one of the eight border elements */
9922     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923     int xx = pos_content % 3;
9924     int yy = pos_content / 3;
9925
9926     x = bx - 1 + xx;
9927     y = by - 1 + yy;
9928
9929     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9931   }
9932   else
9933   {
9934     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9935     {
9936       int xx = x - bx + 1;
9937       int yy = y - by + 1;
9938
9939       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9941     }
9942   }
9943
9944   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9945 }
9946
9947 void CheckExit(int x, int y)
9948 {
9949   if (local_player->gems_still_needed > 0 ||
9950       local_player->sokobanfields_still_needed > 0 ||
9951       local_player->lights_still_needed > 0)
9952   {
9953     int element = Feld[x][y];
9954     int graphic = el2img(element);
9955
9956     if (IS_ANIMATED(graphic))
9957       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958
9959     return;
9960   }
9961
9962   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9963     return;
9964
9965   Feld[x][y] = EL_EXIT_OPENING;
9966
9967   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9968 }
9969
9970 void CheckExitEM(int x, int y)
9971 {
9972   if (local_player->gems_still_needed > 0 ||
9973       local_player->sokobanfields_still_needed > 0 ||
9974       local_player->lights_still_needed > 0)
9975   {
9976     int element = Feld[x][y];
9977     int graphic = el2img(element);
9978
9979     if (IS_ANIMATED(graphic))
9980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9981
9982     return;
9983   }
9984
9985   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9986     return;
9987
9988   Feld[x][y] = EL_EM_EXIT_OPENING;
9989
9990   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9991 }
9992
9993 void CheckExitSteel(int x, int y)
9994 {
9995   if (local_player->gems_still_needed > 0 ||
9996       local_player->sokobanfields_still_needed > 0 ||
9997       local_player->lights_still_needed > 0)
9998   {
9999     int element = Feld[x][y];
10000     int graphic = el2img(element);
10001
10002     if (IS_ANIMATED(graphic))
10003       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10004
10005     return;
10006   }
10007
10008   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10009     return;
10010
10011   Feld[x][y] = EL_STEEL_EXIT_OPENING;
10012
10013   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10014 }
10015
10016 void CheckExitSteelEM(int x, int y)
10017 {
10018   if (local_player->gems_still_needed > 0 ||
10019       local_player->sokobanfields_still_needed > 0 ||
10020       local_player->lights_still_needed > 0)
10021   {
10022     int element = Feld[x][y];
10023     int graphic = el2img(element);
10024
10025     if (IS_ANIMATED(graphic))
10026       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10027
10028     return;
10029   }
10030
10031   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10032     return;
10033
10034   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10035
10036   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10037 }
10038
10039 void CheckExitSP(int x, int y)
10040 {
10041   if (local_player->gems_still_needed > 0)
10042   {
10043     int element = Feld[x][y];
10044     int graphic = el2img(element);
10045
10046     if (IS_ANIMATED(graphic))
10047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10048
10049     return;
10050   }
10051
10052   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10053     return;
10054
10055   Feld[x][y] = EL_SP_EXIT_OPENING;
10056
10057   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10058 }
10059
10060 static void CloseAllOpenTimegates()
10061 {
10062   int x, y;
10063
10064   SCAN_PLAYFIELD(x, y)
10065   {
10066     int element = Feld[x][y];
10067
10068     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10069     {
10070       Feld[x][y] = EL_TIMEGATE_CLOSING;
10071
10072       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10073     }
10074   }
10075 }
10076
10077 void DrawTwinkleOnField(int x, int y)
10078 {
10079   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10080     return;
10081
10082   if (Feld[x][y] == EL_BD_DIAMOND)
10083     return;
10084
10085   if (MovDelay[x][y] == 0)      /* next animation frame */
10086     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10087
10088   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10089   {
10090     MovDelay[x][y]--;
10091
10092     DrawLevelElementAnimation(x, y, Feld[x][y]);
10093
10094     if (MovDelay[x][y] != 0)
10095     {
10096       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097                                            10 - MovDelay[x][y]);
10098
10099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10100     }
10101   }
10102 }
10103
10104 void MauerWaechst(int x, int y)
10105 {
10106   int delay = 6;
10107
10108   if (!MovDelay[x][y])          /* next animation frame */
10109     MovDelay[x][y] = 3 * delay;
10110
10111   if (MovDelay[x][y])           /* wait some time before next frame */
10112   {
10113     MovDelay[x][y]--;
10114
10115     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10116     {
10117       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10119
10120       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10121     }
10122
10123     if (!MovDelay[x][y])
10124     {
10125       if (MovDir[x][y] == MV_LEFT)
10126       {
10127         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128           TEST_DrawLevelField(x - 1, y);
10129       }
10130       else if (MovDir[x][y] == MV_RIGHT)
10131       {
10132         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133           TEST_DrawLevelField(x + 1, y);
10134       }
10135       else if (MovDir[x][y] == MV_UP)
10136       {
10137         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138           TEST_DrawLevelField(x, y - 1);
10139       }
10140       else
10141       {
10142         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143           TEST_DrawLevelField(x, y + 1);
10144       }
10145
10146       Feld[x][y] = Store[x][y];
10147       Store[x][y] = 0;
10148       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149       TEST_DrawLevelField(x, y);
10150     }
10151   }
10152 }
10153
10154 void MauerAbleger(int ax, int ay)
10155 {
10156   int element = Feld[ax][ay];
10157   int graphic = el2img(element);
10158   boolean oben_frei = FALSE, unten_frei = FALSE;
10159   boolean links_frei = FALSE, rechts_frei = FALSE;
10160   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162   boolean new_wall = FALSE;
10163
10164   if (IS_ANIMATED(graphic))
10165     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10166
10167   if (!MovDelay[ax][ay])        /* start building new wall */
10168     MovDelay[ax][ay] = 6;
10169
10170   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10171   {
10172     MovDelay[ax][ay]--;
10173     if (MovDelay[ax][ay])
10174       return;
10175   }
10176
10177   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10178     oben_frei = TRUE;
10179   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10180     unten_frei = TRUE;
10181   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10182     links_frei = TRUE;
10183   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184     rechts_frei = TRUE;
10185
10186   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187       element == EL_EXPANDABLE_WALL_ANY)
10188   {
10189     if (oben_frei)
10190     {
10191       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192       Store[ax][ay-1] = element;
10193       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10197       new_wall = TRUE;
10198     }
10199     if (unten_frei)
10200     {
10201       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202       Store[ax][ay+1] = element;
10203       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10207       new_wall = TRUE;
10208     }
10209   }
10210
10211   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212       element == EL_EXPANDABLE_WALL_ANY ||
10213       element == EL_EXPANDABLE_WALL ||
10214       element == EL_BD_EXPANDABLE_WALL)
10215   {
10216     if (links_frei)
10217     {
10218       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219       Store[ax-1][ay] = element;
10220       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10224       new_wall = TRUE;
10225     }
10226
10227     if (rechts_frei)
10228     {
10229       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230       Store[ax+1][ay] = element;
10231       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10235       new_wall = TRUE;
10236     }
10237   }
10238
10239   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240     TEST_DrawLevelField(ax, ay);
10241
10242   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243     oben_massiv = TRUE;
10244   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245     unten_massiv = TRUE;
10246   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247     links_massiv = TRUE;
10248   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249     rechts_massiv = TRUE;
10250
10251   if (((oben_massiv && unten_massiv) ||
10252        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253        element == EL_EXPANDABLE_WALL) &&
10254       ((links_massiv && rechts_massiv) ||
10255        element == EL_EXPANDABLE_WALL_VERTICAL))
10256     Feld[ax][ay] = EL_WALL;
10257
10258   if (new_wall)
10259     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10260 }
10261
10262 void MauerAblegerStahl(int ax, int ay)
10263 {
10264   int element = Feld[ax][ay];
10265   int graphic = el2img(element);
10266   boolean oben_frei = FALSE, unten_frei = FALSE;
10267   boolean links_frei = FALSE, rechts_frei = FALSE;
10268   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270   boolean new_wall = FALSE;
10271
10272   if (IS_ANIMATED(graphic))
10273     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10274
10275   if (!MovDelay[ax][ay])        /* start building new wall */
10276     MovDelay[ax][ay] = 6;
10277
10278   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10279   {
10280     MovDelay[ax][ay]--;
10281     if (MovDelay[ax][ay])
10282       return;
10283   }
10284
10285   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10286     oben_frei = TRUE;
10287   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10288     unten_frei = TRUE;
10289   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10290     links_frei = TRUE;
10291   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292     rechts_frei = TRUE;
10293
10294   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295       element == EL_EXPANDABLE_STEELWALL_ANY)
10296   {
10297     if (oben_frei)
10298     {
10299       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300       Store[ax][ay-1] = element;
10301       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10305       new_wall = TRUE;
10306     }
10307     if (unten_frei)
10308     {
10309       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310       Store[ax][ay+1] = element;
10311       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10315       new_wall = TRUE;
10316     }
10317   }
10318
10319   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320       element == EL_EXPANDABLE_STEELWALL_ANY)
10321   {
10322     if (links_frei)
10323     {
10324       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325       Store[ax-1][ay] = element;
10326       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10330       new_wall = TRUE;
10331     }
10332
10333     if (rechts_frei)
10334     {
10335       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336       Store[ax+1][ay] = element;
10337       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10341       new_wall = TRUE;
10342     }
10343   }
10344
10345   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346     oben_massiv = TRUE;
10347   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348     unten_massiv = TRUE;
10349   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350     links_massiv = TRUE;
10351   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352     rechts_massiv = TRUE;
10353
10354   if (((oben_massiv && unten_massiv) ||
10355        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356       ((links_massiv && rechts_massiv) ||
10357        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358     Feld[ax][ay] = EL_STEELWALL;
10359
10360   if (new_wall)
10361     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10362 }
10363
10364 void CheckForDragon(int x, int y)
10365 {
10366   int i, j;
10367   boolean dragon_found = FALSE;
10368   static int xy[4][2] =
10369   {
10370     { 0, -1 },
10371     { -1, 0 },
10372     { +1, 0 },
10373     { 0, +1 }
10374   };
10375
10376   for (i = 0; i < NUM_DIRECTIONS; i++)
10377   {
10378     for (j = 0; j < 4; j++)
10379     {
10380       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10381
10382       if (IN_LEV_FIELD(xx, yy) &&
10383           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10384       {
10385         if (Feld[xx][yy] == EL_DRAGON)
10386           dragon_found = TRUE;
10387       }
10388       else
10389         break;
10390     }
10391   }
10392
10393   if (!dragon_found)
10394   {
10395     for (i = 0; i < NUM_DIRECTIONS; i++)
10396     {
10397       for (j = 0; j < 3; j++)
10398       {
10399         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10400   
10401         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10402         {
10403           Feld[xx][yy] = EL_EMPTY;
10404           TEST_DrawLevelField(xx, yy);
10405         }
10406         else
10407           break;
10408       }
10409     }
10410   }
10411 }
10412
10413 static void InitBuggyBase(int x, int y)
10414 {
10415   int element = Feld[x][y];
10416   int activating_delay = FRAMES_PER_SECOND / 4;
10417
10418   ChangeDelay[x][y] =
10419     (element == EL_SP_BUGGY_BASE ?
10420      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10422      activating_delay :
10423      element == EL_SP_BUGGY_BASE_ACTIVE ?
10424      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10425 }
10426
10427 static void WarnBuggyBase(int x, int y)
10428 {
10429   int i;
10430   static int xy[4][2] =
10431   {
10432     { 0, -1 },
10433     { -1, 0 },
10434     { +1, 0 },
10435     { 0, +1 }
10436   };
10437
10438   for (i = 0; i < NUM_DIRECTIONS; i++)
10439   {
10440     int xx = x + xy[i][0];
10441     int yy = y + xy[i][1];
10442
10443     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10444     {
10445       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10446
10447       break;
10448     }
10449   }
10450 }
10451
10452 static void InitTrap(int x, int y)
10453 {
10454   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10455 }
10456
10457 static void ActivateTrap(int x, int y)
10458 {
10459   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10460 }
10461
10462 static void ChangeActiveTrap(int x, int y)
10463 {
10464   int graphic = IMG_TRAP_ACTIVE;
10465
10466   /* if new animation frame was drawn, correct crumbled sand border */
10467   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468     TEST_DrawLevelFieldCrumbled(x, y);
10469 }
10470
10471 static int getSpecialActionElement(int element, int number, int base_element)
10472 {
10473   return (element != EL_EMPTY ? element :
10474           number != -1 ? base_element + number - 1 :
10475           EL_EMPTY);
10476 }
10477
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479                                    int value_min, int value_max)
10480 {
10481   int value_new = (operator == CA_MODE_SET      ? operand :
10482                    operator == CA_MODE_ADD      ? value_old + operand :
10483                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10484                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10485                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10486                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10487                    value_old);
10488
10489   return (value_new < value_min ? value_min :
10490           value_new > value_max ? value_max :
10491           value_new);
10492 }
10493
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10495 {
10496   struct ElementInfo *ei = &element_info[element];
10497   struct ElementChangeInfo *change = &ei->change_page[page];
10498   int target_element = change->target_element;
10499   int action_type = change->action_type;
10500   int action_mode = change->action_mode;
10501   int action_arg = change->action_arg;
10502   int action_element = change->action_element;
10503   int i;
10504
10505   if (!change->has_action)
10506     return;
10507
10508   /* ---------- determine action paramater values -------------------------- */
10509
10510   int level_time_value =
10511     (level.time > 0 ? TimeLeft :
10512      TimePlayed);
10513
10514   int action_arg_element_raw =
10515     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10516      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10518      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10519      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10521      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10522      EL_EMPTY);
10523   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10524
10525 #if 0
10526   if (action_arg_element_raw == EL_GROUP_START)
10527     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10528 #endif
10529
10530   int action_arg_direction =
10531     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534      change->actual_trigger_side :
10535      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10537      MV_NONE);
10538
10539   int action_arg_number_min =
10540     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10541      CA_ARG_MIN);
10542
10543   int action_arg_number_max =
10544     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545      action_type == CA_SET_LEVEL_GEMS ? 999 :
10546      action_type == CA_SET_LEVEL_TIME ? 9999 :
10547      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548      action_type == CA_SET_CE_VALUE ? 9999 :
10549      action_type == CA_SET_CE_SCORE ? 9999 :
10550      CA_ARG_MAX);
10551
10552   int action_arg_number_reset =
10553     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555      action_type == CA_SET_LEVEL_TIME ? level.time :
10556      action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10559 #else
10560      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10561 #endif
10562      action_type == CA_SET_CE_SCORE ? 0 :
10563      0);
10564
10565   int action_arg_number =
10566     (action_arg <= CA_ARG_MAX ? action_arg :
10567      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10575 #else
10576      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10577 #endif
10578      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10590      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10592      -1);
10593
10594   int action_arg_number_old =
10595     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10600      0);
10601
10602   int action_arg_number_new =
10603     getModifiedActionNumber(action_arg_number_old,
10604                             action_mode, action_arg_number,
10605                             action_arg_number_min, action_arg_number_max);
10606
10607 #if 1
10608   int trigger_player_bits =
10609     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610      change->actual_trigger_player_bits : change->trigger_player);
10611 #else
10612   int trigger_player_bits =
10613     (change->actual_trigger_player >= EL_PLAYER_1 &&
10614      change->actual_trigger_player <= EL_PLAYER_4 ?
10615      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10616      PLAYER_BITS_ANY);
10617 #endif
10618
10619   int action_arg_player_bits =
10620     (action_arg >= CA_ARG_PLAYER_1 &&
10621      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10624      PLAYER_BITS_ANY);
10625
10626   /* ---------- execute action  -------------------------------------------- */
10627
10628   switch (action_type)
10629   {
10630     case CA_NO_ACTION:
10631     {
10632       return;
10633     }
10634
10635     /* ---------- level actions  ------------------------------------------- */
10636
10637     case CA_RESTART_LEVEL:
10638     {
10639       game.restart_level = TRUE;
10640
10641       break;
10642     }
10643
10644     case CA_SHOW_ENVELOPE:
10645     {
10646       int element = getSpecialActionElement(action_arg_element,
10647                                             action_arg_number, EL_ENVELOPE_1);
10648
10649       if (IS_ENVELOPE(element))
10650         local_player->show_envelope = element;
10651
10652       break;
10653     }
10654
10655     case CA_SET_LEVEL_TIME:
10656     {
10657       if (level.time > 0)       /* only modify limited time value */
10658       {
10659         TimeLeft = action_arg_number_new;
10660
10661 #if 1
10662         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10663
10664         DisplayGameControlValues();
10665 #else
10666         DrawGameValue_Time(TimeLeft);
10667 #endif
10668
10669         if (!TimeLeft && setup.time_limit)
10670           for (i = 0; i < MAX_PLAYERS; i++)
10671             KillPlayer(&stored_player[i]);
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_LEVEL_SCORE:
10678     {
10679       local_player->score = action_arg_number_new;
10680
10681 #if 1
10682       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10683
10684       DisplayGameControlValues();
10685 #else
10686       DrawGameValue_Score(local_player->score);
10687 #endif
10688
10689       break;
10690     }
10691
10692     case CA_SET_LEVEL_GEMS:
10693     {
10694       local_player->gems_still_needed = action_arg_number_new;
10695
10696 #if 1
10697       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10698
10699       DisplayGameControlValues();
10700 #else
10701       DrawGameValue_Emeralds(local_player->gems_still_needed);
10702 #endif
10703
10704       break;
10705     }
10706
10707 #if !USE_PLAYER_GRAVITY
10708     case CA_SET_LEVEL_GRAVITY:
10709     {
10710       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10711                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10712                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10713                       game.gravity);
10714       break;
10715     }
10716 #endif
10717
10718     case CA_SET_LEVEL_WIND:
10719     {
10720       game.wind_direction = action_arg_direction;
10721
10722       break;
10723     }
10724
10725     case CA_SET_LEVEL_RANDOM_SEED:
10726     {
10727 #if 1
10728       /* ensure that setting a new random seed while playing is predictable */
10729       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10730 #else
10731       InitRND(action_arg_number_new);
10732 #endif
10733
10734 #if 0
10735       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10736 #endif
10737
10738 #if 0
10739       {
10740         int i;
10741
10742         printf("::: ");
10743         for (i = 0; i < 9; i++)
10744           printf("%d, ", RND(2));
10745         printf("\n");
10746       }
10747 #endif
10748
10749       break;
10750     }
10751
10752     /* ---------- player actions  ------------------------------------------ */
10753
10754     case CA_MOVE_PLAYER:
10755     {
10756       /* automatically move to the next field in specified direction */
10757       for (i = 0; i < MAX_PLAYERS; i++)
10758         if (trigger_player_bits & (1 << i))
10759           stored_player[i].programmed_action = action_arg_direction;
10760
10761       break;
10762     }
10763
10764     case CA_EXIT_PLAYER:
10765     {
10766       for (i = 0; i < MAX_PLAYERS; i++)
10767         if (action_arg_player_bits & (1 << i))
10768           PlayerWins(&stored_player[i]);
10769
10770       break;
10771     }
10772
10773     case CA_KILL_PLAYER:
10774     {
10775       for (i = 0; i < MAX_PLAYERS; i++)
10776         if (action_arg_player_bits & (1 << i))
10777           KillPlayer(&stored_player[i]);
10778
10779       break;
10780     }
10781
10782     case CA_SET_PLAYER_KEYS:
10783     {
10784       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785       int element = getSpecialActionElement(action_arg_element,
10786                                             action_arg_number, EL_KEY_1);
10787
10788       if (IS_KEY(element))
10789       {
10790         for (i = 0; i < MAX_PLAYERS; i++)
10791         {
10792           if (trigger_player_bits & (1 << i))
10793           {
10794             stored_player[i].key[KEY_NR(element)] = key_state;
10795
10796             DrawGameDoorValues();
10797           }
10798         }
10799       }
10800
10801       break;
10802     }
10803
10804     case CA_SET_PLAYER_SPEED:
10805     {
10806 #if 0
10807       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10808 #endif
10809
10810       for (i = 0; i < MAX_PLAYERS; i++)
10811       {
10812         if (trigger_player_bits & (1 << i))
10813         {
10814           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10815
10816           if (action_arg == CA_ARG_SPEED_FASTER &&
10817               stored_player[i].cannot_move)
10818           {
10819             action_arg_number = STEPSIZE_VERY_SLOW;
10820           }
10821           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822                    action_arg == CA_ARG_SPEED_FASTER)
10823           {
10824             action_arg_number = 2;
10825             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10826                            CA_MODE_MULTIPLY);
10827           }
10828           else if (action_arg == CA_ARG_NUMBER_RESET)
10829           {
10830             action_arg_number = level.initial_player_stepsize[i];
10831           }
10832
10833           move_stepsize =
10834             getModifiedActionNumber(move_stepsize,
10835                                     action_mode,
10836                                     action_arg_number,
10837                                     action_arg_number_min,
10838                                     action_arg_number_max);
10839
10840           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10841         }
10842       }
10843
10844       break;
10845     }
10846
10847     case CA_SET_PLAYER_SHIELD:
10848     {
10849       for (i = 0; i < MAX_PLAYERS; i++)
10850       {
10851         if (trigger_player_bits & (1 << i))
10852         {
10853           if (action_arg == CA_ARG_SHIELD_OFF)
10854           {
10855             stored_player[i].shield_normal_time_left = 0;
10856             stored_player[i].shield_deadly_time_left = 0;
10857           }
10858           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10859           {
10860             stored_player[i].shield_normal_time_left = 999999;
10861           }
10862           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10863           {
10864             stored_player[i].shield_normal_time_left = 999999;
10865             stored_player[i].shield_deadly_time_left = 999999;
10866           }
10867         }
10868       }
10869
10870       break;
10871     }
10872
10873 #if USE_PLAYER_GRAVITY
10874     case CA_SET_PLAYER_GRAVITY:
10875     {
10876       for (i = 0; i < MAX_PLAYERS; i++)
10877       {
10878         if (trigger_player_bits & (1 << i))
10879         {
10880           stored_player[i].gravity =
10881             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10882              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10883              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884              stored_player[i].gravity);
10885         }
10886       }
10887
10888       break;
10889     }
10890 #endif
10891
10892     case CA_SET_PLAYER_ARTWORK:
10893     {
10894       for (i = 0; i < MAX_PLAYERS; i++)
10895       {
10896         if (trigger_player_bits & (1 << i))
10897         {
10898           int artwork_element = action_arg_element;
10899
10900           if (action_arg == CA_ARG_ELEMENT_RESET)
10901             artwork_element =
10902               (level.use_artwork_element[i] ? level.artwork_element[i] :
10903                stored_player[i].element_nr);
10904
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906           if (stored_player[i].artwork_element != artwork_element)
10907             stored_player[i].Frame = 0;
10908 #endif
10909
10910           stored_player[i].artwork_element = artwork_element;
10911
10912           SetPlayerWaiting(&stored_player[i], FALSE);
10913
10914           /* set number of special actions for bored and sleeping animation */
10915           stored_player[i].num_special_action_bored =
10916             get_num_special_action(artwork_element,
10917                                    ACTION_BORING_1, ACTION_BORING_LAST);
10918           stored_player[i].num_special_action_sleeping =
10919             get_num_special_action(artwork_element,
10920                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10921         }
10922       }
10923
10924       break;
10925     }
10926
10927     case CA_SET_PLAYER_INVENTORY:
10928     {
10929       for (i = 0; i < MAX_PLAYERS; i++)
10930       {
10931         struct PlayerInfo *player = &stored_player[i];
10932         int j, k;
10933
10934         if (trigger_player_bits & (1 << i))
10935         {
10936           int inventory_element = action_arg_element;
10937
10938           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940               action_arg == CA_ARG_ELEMENT_ACTION)
10941           {
10942             int element = inventory_element;
10943             int collect_count = element_info[element].collect_count_initial;
10944
10945             if (!IS_CUSTOM_ELEMENT(element))
10946               collect_count = 1;
10947
10948             if (collect_count == 0)
10949               player->inventory_infinite_element = element;
10950             else
10951               for (k = 0; k < collect_count; k++)
10952                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953                   player->inventory_element[player->inventory_size++] =
10954                     element;
10955           }
10956           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10959           {
10960             if (player->inventory_infinite_element != EL_UNDEFINED &&
10961                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962                                      action_arg_element_raw))
10963               player->inventory_infinite_element = EL_UNDEFINED;
10964
10965             for (k = 0, j = 0; j < player->inventory_size; j++)
10966             {
10967               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968                                         action_arg_element_raw))
10969                 player->inventory_element[k++] = player->inventory_element[j];
10970             }
10971
10972             player->inventory_size = k;
10973           }
10974           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10975           {
10976             if (player->inventory_size > 0)
10977             {
10978               for (j = 0; j < player->inventory_size - 1; j++)
10979                 player->inventory_element[j] = player->inventory_element[j + 1];
10980
10981               player->inventory_size--;
10982             }
10983           }
10984           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10985           {
10986             if (player->inventory_size > 0)
10987               player->inventory_size--;
10988           }
10989           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10990           {
10991             player->inventory_infinite_element = EL_UNDEFINED;
10992             player->inventory_size = 0;
10993           }
10994           else if (action_arg == CA_ARG_INVENTORY_RESET)
10995           {
10996             player->inventory_infinite_element = EL_UNDEFINED;
10997             player->inventory_size = 0;
10998
10999             if (level.use_initial_inventory[i])
11000             {
11001               for (j = 0; j < level.initial_inventory_size[i]; j++)
11002               {
11003                 int element = level.initial_inventory_content[i][j];
11004                 int collect_count = element_info[element].collect_count_initial;
11005
11006                 if (!IS_CUSTOM_ELEMENT(element))
11007                   collect_count = 1;
11008
11009                 if (collect_count == 0)
11010                   player->inventory_infinite_element = element;
11011                 else
11012                   for (k = 0; k < collect_count; k++)
11013                     if (player->inventory_size < MAX_INVENTORY_SIZE)
11014                       player->inventory_element[player->inventory_size++] =
11015                         element;
11016               }
11017             }
11018           }
11019         }
11020       }
11021
11022       break;
11023     }
11024
11025     /* ---------- CE actions  ---------------------------------------------- */
11026
11027     case CA_SET_CE_VALUE:
11028     {
11029 #if USE_NEW_CUSTOM_VALUE
11030       int last_ce_value = CustomValue[x][y];
11031
11032       CustomValue[x][y] = action_arg_number_new;
11033
11034       if (CustomValue[x][y] != last_ce_value)
11035       {
11036         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11038
11039         if (CustomValue[x][y] == 0)
11040         {
11041           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11043         }
11044       }
11045 #endif
11046
11047       break;
11048     }
11049
11050     case CA_SET_CE_SCORE:
11051     {
11052 #if USE_NEW_CUSTOM_VALUE
11053       int last_ce_score = ei->collect_score;
11054
11055       ei->collect_score = action_arg_number_new;
11056
11057       if (ei->collect_score != last_ce_score)
11058       {
11059         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11061
11062         if (ei->collect_score == 0)
11063         {
11064           int xx, yy;
11065
11066           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11068
11069           /*
11070             This is a very special case that seems to be a mixture between
11071             CheckElementChange() and CheckTriggeredElementChange(): while
11072             the first one only affects single elements that are triggered
11073             directly, the second one affects multiple elements in the playfield
11074             that are triggered indirectly by another element. This is a third
11075             case: Changing the CE score always affects multiple identical CEs,
11076             so every affected CE must be checked, not only the single CE for
11077             which the CE score was changed in the first place (as every instance
11078             of that CE shares the same CE score, and therefore also can change)!
11079           */
11080           SCAN_PLAYFIELD(xx, yy)
11081           {
11082             if (Feld[xx][yy] == element)
11083               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084                                  CE_SCORE_GETS_ZERO);
11085           }
11086         }
11087       }
11088 #endif
11089
11090       break;
11091     }
11092
11093     case CA_SET_CE_ARTWORK:
11094     {
11095       int artwork_element = action_arg_element;
11096       boolean reset_frame = FALSE;
11097       int xx, yy;
11098
11099       if (action_arg == CA_ARG_ELEMENT_RESET)
11100         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11101                            element);
11102
11103       if (ei->gfx_element != artwork_element)
11104         reset_frame = TRUE;
11105
11106       ei->gfx_element = artwork_element;
11107
11108       SCAN_PLAYFIELD(xx, yy)
11109       {
11110         if (Feld[xx][yy] == element)
11111         {
11112           if (reset_frame)
11113           {
11114             ResetGfxAnimation(xx, yy);
11115             ResetRandomAnimationValue(xx, yy);
11116           }
11117
11118           TEST_DrawLevelField(xx, yy);
11119         }
11120       }
11121
11122       break;
11123     }
11124
11125     /* ---------- engine actions  ------------------------------------------ */
11126
11127     case CA_SET_ENGINE_SCAN_MODE:
11128     {
11129       InitPlayfieldScanMode(action_arg);
11130
11131       break;
11132     }
11133
11134     default:
11135       break;
11136   }
11137 }
11138
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11140 {
11141   int old_element = Feld[x][y];
11142   int new_element = GetElementFromGroupElement(element);
11143   int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145   int last_ce_value = CustomValue[x][y];
11146 #endif
11147   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149   boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151                                      /* this breaks SnakeBite when a snake is
11152                                         halfway through a door that closes */
11153                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11154                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11155 #endif
11156                                      IS_WALKABLE(old_element));
11157
11158 #if 0
11159   /* check if element under the player changes from accessible to unaccessible
11160      (needed for special case of dropping element which then changes) */
11161   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11163   {
11164     Bang(x, y);
11165
11166     return;
11167   }
11168 #endif
11169
11170   if (!add_player_onto_element)
11171   {
11172     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173       RemoveMovingField(x, y);
11174     else
11175       RemoveField(x, y);
11176
11177     Feld[x][y] = new_element;
11178
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180     ResetGfxAnimation(x, y);
11181     ResetRandomAnimationValue(x, y);
11182 #endif
11183
11184     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185       MovDir[x][y] = previous_move_direction;
11186
11187 #if USE_NEW_CUSTOM_VALUE
11188     if (element_info[new_element].use_last_ce_value)
11189       CustomValue[x][y] = last_ce_value;
11190 #endif
11191
11192     InitField_WithBug1(x, y, FALSE);
11193
11194     new_element = Feld[x][y];   /* element may have changed */
11195
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197     ResetGfxAnimation(x, y);
11198     ResetRandomAnimationValue(x, y);
11199 #endif
11200
11201     TEST_DrawLevelField(x, y);
11202
11203     if (GFX_CRUMBLED(new_element))
11204       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11205   }
11206
11207 #if 1
11208   /* check if element under the player changes from accessible to unaccessible
11209      (needed for special case of dropping element which then changes) */
11210   /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11214   {
11215     Bang(x, y);
11216
11217     return;
11218   }
11219 #else
11220   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11222   {
11223     Bang(x, y);
11224
11225     return;
11226   }
11227 #endif
11228 #endif
11229
11230   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231   if (new_element_is_player)
11232     RelocatePlayer(x, y, new_element);
11233
11234   if (is_change)
11235     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11236
11237   TestIfBadThingTouchesPlayer(x, y);
11238   TestIfPlayerTouchesCustomElement(x, y);
11239   TestIfElementTouchesCustomElement(x, y);
11240 }
11241
11242 static void CreateField(int x, int y, int element)
11243 {
11244   CreateFieldExt(x, y, element, FALSE);
11245 }
11246
11247 static void CreateElementFromChange(int x, int y, int element)
11248 {
11249   element = GET_VALID_RUNTIME_ELEMENT(element);
11250
11251 #if USE_STOP_CHANGED_ELEMENTS
11252   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11253   {
11254     int old_element = Feld[x][y];
11255
11256     /* prevent changed element from moving in same engine frame
11257        unless both old and new element can either fall or move */
11258     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11260       Stop[x][y] = TRUE;
11261   }
11262 #endif
11263
11264   CreateFieldExt(x, y, element, TRUE);
11265 }
11266
11267 static boolean ChangeElement(int x, int y, int element, int page)
11268 {
11269   struct ElementInfo *ei = &element_info[element];
11270   struct ElementChangeInfo *change = &ei->change_page[page];
11271   int ce_value = CustomValue[x][y];
11272   int ce_score = ei->collect_score;
11273   int target_element;
11274   int old_element = Feld[x][y];
11275
11276   /* always use default change event to prevent running into a loop */
11277   if (ChangeEvent[x][y] == -1)
11278     ChangeEvent[x][y] = CE_DELAY;
11279
11280   if (ChangeEvent[x][y] == CE_DELAY)
11281   {
11282     /* reset actual trigger element, trigger player and action element */
11283     change->actual_trigger_element = EL_EMPTY;
11284     change->actual_trigger_player = EL_EMPTY;
11285     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286     change->actual_trigger_side = CH_SIDE_NONE;
11287     change->actual_trigger_ce_value = 0;
11288     change->actual_trigger_ce_score = 0;
11289   }
11290
11291   /* do not change elements more than a specified maximum number of changes */
11292   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11293     return FALSE;
11294
11295   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11296
11297   if (change->explode)
11298   {
11299     Bang(x, y);
11300
11301     return TRUE;
11302   }
11303
11304   if (change->use_target_content)
11305   {
11306     boolean complete_replace = TRUE;
11307     boolean can_replace[3][3];
11308     int xx, yy;
11309
11310     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11311     {
11312       boolean is_empty;
11313       boolean is_walkable;
11314       boolean is_diggable;
11315       boolean is_collectible;
11316       boolean is_removable;
11317       boolean is_destructible;
11318       int ex = x + xx - 1;
11319       int ey = y + yy - 1;
11320       int content_element = change->target_content.e[xx][yy];
11321       int e;
11322
11323       can_replace[xx][yy] = TRUE;
11324
11325       if (ex == x && ey == y)   /* do not check changing element itself */
11326         continue;
11327
11328       if (content_element == EL_EMPTY_SPACE)
11329       {
11330         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11331
11332         continue;
11333       }
11334
11335       if (!IN_LEV_FIELD(ex, ey))
11336       {
11337         can_replace[xx][yy] = FALSE;
11338         complete_replace = FALSE;
11339
11340         continue;
11341       }
11342
11343       e = Feld[ex][ey];
11344
11345       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346         e = MovingOrBlocked2Element(ex, ey);
11347
11348       is_empty = (IS_FREE(ex, ey) ||
11349                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11350
11351       is_walkable     = (is_empty || IS_WALKABLE(e));
11352       is_diggable     = (is_empty || IS_DIGGABLE(e));
11353       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11354       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355       is_removable    = (is_diggable || is_collectible);
11356
11357       can_replace[xx][yy] =
11358         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11359           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11360           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11361           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11362           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11363           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11365
11366       if (!can_replace[xx][yy])
11367         complete_replace = FALSE;
11368     }
11369
11370     if (!change->only_if_complete || complete_replace)
11371     {
11372       boolean something_has_changed = FALSE;
11373
11374       if (change->only_if_complete && change->use_random_replace &&
11375           RND(100) < change->random_percentage)
11376         return FALSE;
11377
11378       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11379       {
11380         int ex = x + xx - 1;
11381         int ey = y + yy - 1;
11382         int content_element;
11383
11384         if (can_replace[xx][yy] && (!change->use_random_replace ||
11385                                     RND(100) < change->random_percentage))
11386         {
11387           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388             RemoveMovingField(ex, ey);
11389
11390           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11391
11392           content_element = change->target_content.e[xx][yy];
11393           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394                                               ce_value, ce_score);
11395
11396           CreateElementFromChange(ex, ey, target_element);
11397
11398           something_has_changed = TRUE;
11399
11400           /* for symmetry reasons, freeze newly created border elements */
11401           if (ex != x || ey != y)
11402             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11403         }
11404       }
11405
11406       if (something_has_changed)
11407       {
11408         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11410       }
11411     }
11412   }
11413   else
11414   {
11415     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416                                         ce_value, ce_score);
11417
11418     if (element == EL_DIAGONAL_GROWING ||
11419         element == EL_DIAGONAL_SHRINKING)
11420     {
11421       target_element = Store[x][y];
11422
11423       Store[x][y] = EL_EMPTY;
11424     }
11425
11426     CreateElementFromChange(x, y, target_element);
11427
11428     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11430   }
11431
11432   /* this uses direct change before indirect change */
11433   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11434
11435   return TRUE;
11436 }
11437
11438 #if USE_NEW_DELAYED_ACTION
11439
11440 static void HandleElementChange(int x, int y, int page)
11441 {
11442   int element = MovingOrBlocked2Element(x, y);
11443   struct ElementInfo *ei = &element_info[element];
11444   struct ElementChangeInfo *change = &ei->change_page[page];
11445   boolean handle_action_before_change = FALSE;
11446
11447 #ifdef DEBUG
11448   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11450   {
11451     printf("\n\n");
11452     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453            x, y, element, element_info[element].token_name);
11454     printf("HandleElementChange(): This should never happen!\n");
11455     printf("\n\n");
11456   }
11457 #endif
11458
11459   /* this can happen with classic bombs on walkable, changing elements */
11460   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11461   {
11462 #if 0
11463     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11464       ChangeDelay[x][y] = 0;
11465 #endif
11466
11467     return;
11468   }
11469
11470   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11471   {
11472     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11473
11474     if (change->can_change)
11475     {
11476 #if 1
11477       /* !!! not clear why graphic animation should be reset at all here !!! */
11478       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480       /* when a custom element is about to change (for example by change delay),
11481          do not reset graphic animation when the custom element is moving */
11482       if (!IS_MOVING(x, y))
11483 #endif
11484       {
11485         ResetGfxAnimation(x, y);
11486         ResetRandomAnimationValue(x, y);
11487       }
11488 #endif
11489
11490       if (change->pre_change_function)
11491         change->pre_change_function(x, y);
11492     }
11493   }
11494
11495   ChangeDelay[x][y]--;
11496
11497   if (ChangeDelay[x][y] != 0)           /* continue element change */
11498   {
11499     if (change->can_change)
11500     {
11501       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11502
11503       if (IS_ANIMATED(graphic))
11504         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505
11506       if (change->change_function)
11507         change->change_function(x, y);
11508     }
11509   }
11510   else                                  /* finish element change */
11511   {
11512     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11513     {
11514       page = ChangePage[x][y];
11515       ChangePage[x][y] = -1;
11516
11517       change = &ei->change_page[page];
11518     }
11519
11520     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11521     {
11522       ChangeDelay[x][y] = 1;            /* try change after next move step */
11523       ChangePage[x][y] = page;          /* remember page to use for change */
11524
11525       return;
11526     }
11527
11528 #if 1
11529     /* special case: set new level random seed before changing element */
11530     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531       handle_action_before_change = TRUE;
11532
11533     if (change->has_action && handle_action_before_change)
11534       ExecuteCustomElementAction(x, y, element, page);
11535 #endif
11536
11537     if (change->can_change)
11538     {
11539       if (ChangeElement(x, y, element, page))
11540       {
11541         if (change->post_change_function)
11542           change->post_change_function(x, y);
11543       }
11544     }
11545
11546     if (change->has_action && !handle_action_before_change)
11547       ExecuteCustomElementAction(x, y, element, page);
11548   }
11549 }
11550
11551 #else
11552
11553 static void HandleElementChange(int x, int y, int page)
11554 {
11555   int element = MovingOrBlocked2Element(x, y);
11556   struct ElementInfo *ei = &element_info[element];
11557   struct ElementChangeInfo *change = &ei->change_page[page];
11558
11559 #ifdef DEBUG
11560   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11561   {
11562     printf("\n\n");
11563     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564            x, y, element, element_info[element].token_name);
11565     printf("HandleElementChange(): This should never happen!\n");
11566     printf("\n\n");
11567   }
11568 #endif
11569
11570   /* this can happen with classic bombs on walkable, changing elements */
11571   if (!CAN_CHANGE(element))
11572   {
11573 #if 0
11574     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11575       ChangeDelay[x][y] = 0;
11576 #endif
11577
11578     return;
11579   }
11580
11581   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11582   {
11583     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11584
11585     ResetGfxAnimation(x, y);
11586     ResetRandomAnimationValue(x, y);
11587
11588     if (change->pre_change_function)
11589       change->pre_change_function(x, y);
11590   }
11591
11592   ChangeDelay[x][y]--;
11593
11594   if (ChangeDelay[x][y] != 0)           /* continue element change */
11595   {
11596     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11597
11598     if (IS_ANIMATED(graphic))
11599       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11600
11601     if (change->change_function)
11602       change->change_function(x, y);
11603   }
11604   else                                  /* finish element change */
11605   {
11606     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11607     {
11608       page = ChangePage[x][y];
11609       ChangePage[x][y] = -1;
11610
11611       change = &ei->change_page[page];
11612     }
11613
11614     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11615     {
11616       ChangeDelay[x][y] = 1;            /* try change after next move step */
11617       ChangePage[x][y] = page;          /* remember page to use for change */
11618
11619       return;
11620     }
11621
11622     if (ChangeElement(x, y, element, page))
11623     {
11624       if (change->post_change_function)
11625         change->post_change_function(x, y);
11626     }
11627   }
11628 }
11629
11630 #endif
11631
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633                                               int trigger_element,
11634                                               int trigger_event,
11635                                               int trigger_player,
11636                                               int trigger_side,
11637                                               int trigger_page)
11638 {
11639   boolean change_done_any = FALSE;
11640   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11641   int i;
11642
11643   if (!(trigger_events[trigger_element][trigger_event]))
11644     return FALSE;
11645
11646 #if 0
11647   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648          trigger_event, recursion_loop_depth, recursion_loop_detected,
11649          recursion_loop_element, EL_NAME(recursion_loop_element));
11650 #endif
11651
11652   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11653
11654   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11655   {
11656     int element = EL_CUSTOM_START + i;
11657     boolean change_done = FALSE;
11658     int p;
11659
11660     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11662       continue;
11663
11664     for (p = 0; p < element_info[element].num_change_pages; p++)
11665     {
11666       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11667
11668       if (change->can_change_or_has_action &&
11669           change->has_event[trigger_event] &&
11670           change->trigger_side & trigger_side &&
11671           change->trigger_player & trigger_player &&
11672           change->trigger_page & trigger_page_bits &&
11673           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11674       {
11675         change->actual_trigger_element = trigger_element;
11676         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677         change->actual_trigger_player_bits = trigger_player;
11678         change->actual_trigger_side = trigger_side;
11679         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11681
11682 #if 0
11683         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684                element, EL_NAME(element), p);
11685 #endif
11686
11687         if ((change->can_change && !change_done) || change->has_action)
11688         {
11689           int x, y;
11690
11691           SCAN_PLAYFIELD(x, y)
11692           {
11693             if (Feld[x][y] == element)
11694             {
11695               if (change->can_change && !change_done)
11696               {
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698                 /* if element already changed in this frame, not only prevent
11699                    another element change (checked in ChangeElement()), but
11700                    also prevent additional element actions for this element */
11701
11702                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703                     !level.use_action_after_change_bug)
11704                   continue;
11705 #endif
11706
11707 #if 0
11708                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709                        element, EL_NAME(element), p);
11710 #endif
11711
11712                 ChangeDelay[x][y] = 1;
11713                 ChangeEvent[x][y] = trigger_event;
11714
11715                 HandleElementChange(x, y, p);
11716               }
11717 #if USE_NEW_DELAYED_ACTION
11718               else if (change->has_action)
11719               {
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721                 /* if element already changed in this frame, not only prevent
11722                    another element change (checked in ChangeElement()), but
11723                    also prevent additional element actions for this element */
11724
11725                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726                     !level.use_action_after_change_bug)
11727                   continue;
11728 #endif
11729
11730
11731 #if 0
11732                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733                        element, EL_NAME(element), p);
11734 #endif
11735
11736                 ExecuteCustomElementAction(x, y, element, p);
11737                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11738               }
11739 #else
11740               if (change->has_action)
11741               {
11742                 ExecuteCustomElementAction(x, y, element, p);
11743                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11744               }
11745 #endif
11746             }
11747           }
11748
11749           if (change->can_change)
11750           {
11751             change_done = TRUE;
11752             change_done_any = TRUE;
11753
11754 #if 0
11755             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756                    element, EL_NAME(element), p);
11757 #endif
11758
11759           }
11760         }
11761       }
11762     }
11763   }
11764
11765   RECURSION_LOOP_DETECTION_END();
11766
11767   return change_done_any;
11768 }
11769
11770 static boolean CheckElementChangeExt(int x, int y,
11771                                      int element,
11772                                      int trigger_element,
11773                                      int trigger_event,
11774                                      int trigger_player,
11775                                      int trigger_side)
11776 {
11777   boolean change_done = FALSE;
11778   int p;
11779
11780   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11782     return FALSE;
11783
11784   if (Feld[x][y] == EL_BLOCKED)
11785   {
11786     Blocked2Moving(x, y, &x, &y);
11787     element = Feld[x][y];
11788   }
11789
11790 #if 0
11791   /* check if element has already changed */
11792   if (Feld[x][y] != element)
11793     return FALSE;
11794 #else
11795   /* check if element has already changed or is about to change after moving */
11796   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797        Feld[x][y] != element) ||
11798
11799       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801         ChangePage[x][y] != -1)))
11802     return FALSE;
11803 #endif
11804
11805 #if 0
11806   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807          trigger_event, recursion_loop_depth, recursion_loop_detected,
11808          recursion_loop_element, EL_NAME(recursion_loop_element));
11809 #endif
11810
11811   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11812
11813 #if 0
11814   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11815 #endif
11816
11817   for (p = 0; p < element_info[element].num_change_pages; p++)
11818   {
11819     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11820
11821     /* check trigger element for all events where the element that is checked
11822        for changing interacts with a directly adjacent element -- this is
11823        different to element changes that affect other elements to change on the
11824        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825     boolean check_trigger_element =
11826       (trigger_event == CE_TOUCHING_X ||
11827        trigger_event == CE_HITTING_X ||
11828        trigger_event == CE_HIT_BY_X ||
11829 #if 1
11830        /* this one was forgotten until 3.2.3 */
11831        trigger_event == CE_DIGGING_X);
11832 #endif
11833
11834     if (change->can_change_or_has_action &&
11835         change->has_event[trigger_event] &&
11836         change->trigger_side & trigger_side &&
11837         change->trigger_player & trigger_player &&
11838         (!check_trigger_element ||
11839          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11840     {
11841       change->actual_trigger_element = trigger_element;
11842       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843       change->actual_trigger_player_bits = trigger_player;
11844       change->actual_trigger_side = trigger_side;
11845       change->actual_trigger_ce_value = CustomValue[x][y];
11846       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11847
11848       /* special case: trigger element not at (x,y) position for some events */
11849       if (check_trigger_element)
11850       {
11851         static struct
11852         {
11853           int dx, dy;
11854         } move_xy[] =
11855           {
11856             {  0,  0 },
11857             { -1,  0 },
11858             { +1,  0 },
11859             {  0,  0 },
11860             {  0, -1 },
11861             {  0,  0 }, { 0, 0 }, { 0, 0 },
11862             {  0, +1 }
11863           };
11864
11865         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11867
11868         change->actual_trigger_ce_value = CustomValue[xx][yy];
11869         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11870       }
11871
11872       if (change->can_change && !change_done)
11873       {
11874         ChangeDelay[x][y] = 1;
11875         ChangeEvent[x][y] = trigger_event;
11876
11877         HandleElementChange(x, y, p);
11878
11879         change_done = TRUE;
11880       }
11881 #if USE_NEW_DELAYED_ACTION
11882       else if (change->has_action)
11883       {
11884         ExecuteCustomElementAction(x, y, element, p);
11885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11886       }
11887 #else
11888       if (change->has_action)
11889       {
11890         ExecuteCustomElementAction(x, y, element, p);
11891         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11892       }
11893 #endif
11894     }
11895   }
11896
11897   RECURSION_LOOP_DETECTION_END();
11898
11899   return change_done;
11900 }
11901
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11903 {
11904   int jx = player->jx, jy = player->jy;
11905   int sound_element = player->artwork_element;
11906   int last_action = player->last_action_waiting;
11907   int action = player->action_waiting;
11908
11909   if (player->is_waiting)
11910   {
11911     if (action != last_action)
11912       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11913     else
11914       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11915   }
11916   else
11917   {
11918     if (action != last_action)
11919       StopSound(element_info[sound_element].sound[last_action]);
11920
11921     if (last_action == ACTION_SLEEPING)
11922       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11923   }
11924 }
11925
11926 static void PlayAllPlayersSound()
11927 {
11928   int i;
11929
11930   for (i = 0; i < MAX_PLAYERS; i++)
11931     if (stored_player[i].active)
11932       PlayPlayerSound(&stored_player[i]);
11933 }
11934
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11936 {
11937   boolean last_waiting = player->is_waiting;
11938   int move_dir = player->MovDir;
11939
11940   player->dir_waiting = move_dir;
11941   player->last_action_waiting = player->action_waiting;
11942
11943   if (is_waiting)
11944   {
11945     if (!last_waiting)          /* not waiting -> waiting */
11946     {
11947       player->is_waiting = TRUE;
11948
11949       player->frame_counter_bored =
11950         FrameCounter +
11951         game.player_boring_delay_fixed +
11952         GetSimpleRandom(game.player_boring_delay_random);
11953       player->frame_counter_sleeping =
11954         FrameCounter +
11955         game.player_sleeping_delay_fixed +
11956         GetSimpleRandom(game.player_sleeping_delay_random);
11957
11958       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11959     }
11960
11961     if (game.player_sleeping_delay_fixed +
11962         game.player_sleeping_delay_random > 0 &&
11963         player->anim_delay_counter == 0 &&
11964         player->post_delay_counter == 0 &&
11965         FrameCounter >= player->frame_counter_sleeping)
11966       player->is_sleeping = TRUE;
11967     else if (game.player_boring_delay_fixed +
11968              game.player_boring_delay_random > 0 &&
11969              FrameCounter >= player->frame_counter_bored)
11970       player->is_bored = TRUE;
11971
11972     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973                               player->is_bored ? ACTION_BORING :
11974                               ACTION_WAITING);
11975
11976     if (player->is_sleeping && player->use_murphy)
11977     {
11978       /* special case for sleeping Murphy when leaning against non-free tile */
11979
11980       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982            !IS_MOVING(player->jx - 1, player->jy)))
11983         move_dir = MV_LEFT;
11984       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986                 !IS_MOVING(player->jx + 1, player->jy)))
11987         move_dir = MV_RIGHT;
11988       else
11989         player->is_sleeping = FALSE;
11990
11991       player->dir_waiting = move_dir;
11992     }
11993
11994     if (player->is_sleeping)
11995     {
11996       if (player->num_special_action_sleeping > 0)
11997       {
11998         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11999         {
12000           int last_special_action = player->special_action_sleeping;
12001           int num_special_action = player->num_special_action_sleeping;
12002           int special_action =
12003             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006              last_special_action + 1 : ACTION_SLEEPING);
12007           int special_graphic =
12008             el_act_dir2img(player->artwork_element, special_action, move_dir);
12009
12010           player->anim_delay_counter =
12011             graphic_info[special_graphic].anim_delay_fixed +
12012             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013           player->post_delay_counter =
12014             graphic_info[special_graphic].post_delay_fixed +
12015             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12016
12017           player->special_action_sleeping = special_action;
12018         }
12019
12020         if (player->anim_delay_counter > 0)
12021         {
12022           player->action_waiting = player->special_action_sleeping;
12023           player->anim_delay_counter--;
12024         }
12025         else if (player->post_delay_counter > 0)
12026         {
12027           player->post_delay_counter--;
12028         }
12029       }
12030     }
12031     else if (player->is_bored)
12032     {
12033       if (player->num_special_action_bored > 0)
12034       {
12035         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12036         {
12037           int special_action =
12038             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039           int special_graphic =
12040             el_act_dir2img(player->artwork_element, special_action, move_dir);
12041
12042           player->anim_delay_counter =
12043             graphic_info[special_graphic].anim_delay_fixed +
12044             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045           player->post_delay_counter =
12046             graphic_info[special_graphic].post_delay_fixed +
12047             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12048
12049           player->special_action_bored = special_action;
12050         }
12051
12052         if (player->anim_delay_counter > 0)
12053         {
12054           player->action_waiting = player->special_action_bored;
12055           player->anim_delay_counter--;
12056         }
12057         else if (player->post_delay_counter > 0)
12058         {
12059           player->post_delay_counter--;
12060         }
12061       }
12062     }
12063   }
12064   else if (last_waiting)        /* waiting -> not waiting */
12065   {
12066     player->is_waiting = FALSE;
12067     player->is_bored = FALSE;
12068     player->is_sleeping = FALSE;
12069
12070     player->frame_counter_bored = -1;
12071     player->frame_counter_sleeping = -1;
12072
12073     player->anim_delay_counter = 0;
12074     player->post_delay_counter = 0;
12075
12076     player->dir_waiting = player->MovDir;
12077     player->action_waiting = ACTION_DEFAULT;
12078
12079     player->special_action_bored = ACTION_DEFAULT;
12080     player->special_action_sleeping = ACTION_DEFAULT;
12081   }
12082 }
12083
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12085 {
12086   if (tape.single_step && tape.recording && !tape.pausing)
12087   {
12088     /* as it is called "single step mode", just return to pause mode when the
12089        player stopped moving after one tile (or never starts moving at all) */
12090     if (!player->is_moving && !player->is_pushing)
12091     {
12092       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093       SnapField(player, 0, 0);                  /* stop snapping */
12094     }
12095   }
12096 }
12097
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12099 {
12100   int left      = player_action & JOY_LEFT;
12101   int right     = player_action & JOY_RIGHT;
12102   int up        = player_action & JOY_UP;
12103   int down      = player_action & JOY_DOWN;
12104   int button1   = player_action & JOY_BUTTON_1;
12105   int button2   = player_action & JOY_BUTTON_2;
12106   int dx        = (left ? -1 : right ? 1 : 0);
12107   int dy        = (up   ? -1 : down  ? 1 : 0);
12108
12109   if (!player->active || tape.pausing)
12110     return 0;
12111
12112   if (player_action)
12113   {
12114     if (button1)
12115       SnapField(player, dx, dy);
12116     else
12117     {
12118       if (button2)
12119         DropElement(player);
12120
12121       MovePlayer(player, dx, dy);
12122     }
12123
12124     CheckSingleStepMode(player);
12125
12126     SetPlayerWaiting(player, FALSE);
12127
12128     return player_action;
12129   }
12130   else
12131   {
12132     /* no actions for this player (no input at player's configured device) */
12133
12134     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12135     SnapField(player, 0, 0);
12136     CheckGravityMovementWhenNotMoving(player);
12137
12138     if (player->MovPos == 0)
12139       SetPlayerWaiting(player, TRUE);
12140
12141     if (player->MovPos == 0)    /* needed for tape.playing */
12142       player->is_moving = FALSE;
12143
12144     player->is_dropping = FALSE;
12145     player->is_dropping_pressed = FALSE;
12146     player->drop_pressed_delay = 0;
12147
12148     CheckSingleStepMode(player);
12149
12150     return 0;
12151   }
12152 }
12153
12154 static void CheckLevelTime()
12155 {
12156   int i;
12157
12158   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12159   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12160   {
12161     if (level.native_em_level->lev->home == 0)  /* all players at home */
12162     {
12163       PlayerWins(local_player);
12164
12165       AllPlayersGone = TRUE;
12166
12167       level.native_em_level->lev->home = -1;
12168     }
12169
12170     if (level.native_em_level->ply[0]->alive == 0 &&
12171         level.native_em_level->ply[1]->alive == 0 &&
12172         level.native_em_level->ply[2]->alive == 0 &&
12173         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12174       AllPlayersGone = TRUE;
12175   }
12176   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12177   {
12178     if (game_sp.LevelSolved &&
12179         !game_sp.GameOver)                              /* game won */
12180     {
12181       PlayerWins(local_player);
12182
12183       game_sp.GameOver = TRUE;
12184
12185       AllPlayersGone = TRUE;
12186     }
12187
12188     if (game_sp.GameOver)                               /* game lost */
12189       AllPlayersGone = TRUE;
12190   }
12191
12192   if (TimeFrames >= FRAMES_PER_SECOND)
12193   {
12194     TimeFrames = 0;
12195     TapeTime++;
12196
12197     for (i = 0; i < MAX_PLAYERS; i++)
12198     {
12199       struct PlayerInfo *player = &stored_player[i];
12200
12201       if (SHIELD_ON(player))
12202       {
12203         player->shield_normal_time_left--;
12204
12205         if (player->shield_deadly_time_left > 0)
12206           player->shield_deadly_time_left--;
12207       }
12208     }
12209
12210     if (!local_player->LevelSolved && !level.use_step_counter)
12211     {
12212       TimePlayed++;
12213
12214       if (TimeLeft > 0)
12215       {
12216         TimeLeft--;
12217
12218         if (TimeLeft <= 10 && setup.time_limit)
12219           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12220
12221 #if 1
12222         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12223            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12224
12225         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12226
12227         /* (already called by UpdateAndDisplayGameControlValues() below) */
12228         // DisplayGameControlValues();
12229 #else
12230         DrawGameValue_Time(TimeLeft);
12231 #endif
12232
12233         if (!TimeLeft && setup.time_limit)
12234         {
12235           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12236             level.native_em_level->lev->killed_out_of_time = TRUE;
12237           else
12238             for (i = 0; i < MAX_PLAYERS; i++)
12239               KillPlayer(&stored_player[i]);
12240         }
12241       }
12242 #if 1
12243       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12244       {
12245         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12246
12247         /* (already called by UpdateAndDisplayGameControlValues() below) */
12248         // DisplayGameControlValues();
12249       }
12250 #else
12251       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12252         DrawGameValue_Time(TimePlayed);
12253 #endif
12254
12255       level.native_em_level->lev->time =
12256         (game.no_time_limit ? TimePlayed : TimeLeft);
12257     }
12258
12259     if (tape.recording || tape.playing)
12260       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12261   }
12262
12263 #if 1
12264   UpdateAndDisplayGameControlValues();
12265 #else
12266   UpdateGameDoorValues();
12267   DrawGameDoorValues();
12268 #endif
12269 }
12270
12271 void AdvanceFrameAndPlayerCounters(int player_nr)
12272 {
12273   int i;
12274
12275   /* advance frame counters (global frame counter and time frame counter) */
12276   FrameCounter++;
12277   TimeFrames++;
12278
12279   /* advance player counters (counters for move delay, move animation etc.) */
12280   for (i = 0; i < MAX_PLAYERS; i++)
12281   {
12282     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12283     int move_delay_value = stored_player[i].move_delay_value;
12284     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12285
12286     if (!advance_player_counters)       /* not all players may be affected */
12287       continue;
12288
12289 #if USE_NEW_PLAYER_ANIM
12290     if (move_frames == 0)       /* less than one move per game frame */
12291     {
12292       int stepsize = TILEX / move_delay_value;
12293       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12294       int count = (stored_player[i].is_moving ?
12295                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12296
12297       if (count % delay == 0)
12298         move_frames = 1;
12299     }
12300 #endif
12301
12302     stored_player[i].Frame += move_frames;
12303
12304     if (stored_player[i].MovPos != 0)
12305       stored_player[i].StepFrame += move_frames;
12306
12307     if (stored_player[i].move_delay > 0)
12308       stored_player[i].move_delay--;
12309
12310     /* due to bugs in previous versions, counter must count up, not down */
12311     if (stored_player[i].push_delay != -1)
12312       stored_player[i].push_delay++;
12313
12314     if (stored_player[i].drop_delay > 0)
12315       stored_player[i].drop_delay--;
12316
12317     if (stored_player[i].is_dropping_pressed)
12318       stored_player[i].drop_pressed_delay++;
12319   }
12320 }
12321
12322 void StartGameActions(boolean init_network_game, boolean record_tape,
12323                       int random_seed)
12324 {
12325   unsigned int new_random_seed = InitRND(random_seed);
12326
12327   if (record_tape)
12328     TapeStartRecording(new_random_seed);
12329
12330 #if defined(NETWORK_AVALIABLE)
12331   if (init_network_game)
12332   {
12333     SendToServer_StartPlaying();
12334
12335     return;
12336   }
12337 #endif
12338
12339   InitGame();
12340 }
12341
12342 void GameActions()
12343 {
12344   static unsigned int game_frame_delay = 0;
12345   unsigned int game_frame_delay_value;
12346   byte *recorded_player_action;
12347   byte summarized_player_action = 0;
12348   byte tape_action[MAX_PLAYERS];
12349   int i;
12350
12351   /* detect endless loops, caused by custom element programming */
12352   if (recursion_loop_detected && recursion_loop_depth == 0)
12353   {
12354     char *message = getStringCat3("Internal Error! Element ",
12355                                   EL_NAME(recursion_loop_element),
12356                                   " caused endless loop! Quit the game?");
12357
12358     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12359           EL_NAME(recursion_loop_element));
12360
12361     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12362
12363     recursion_loop_detected = FALSE;    /* if game should be continued */
12364
12365     free(message);
12366
12367     return;
12368   }
12369
12370   if (game.restart_level)
12371     StartGameActions(options.network, setup.autorecord, level.random_seed);
12372
12373   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12374   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12375   {
12376     if (level.native_em_level->lev->home == 0)  /* all players at home */
12377     {
12378       PlayerWins(local_player);
12379
12380       AllPlayersGone = TRUE;
12381
12382       level.native_em_level->lev->home = -1;
12383     }
12384
12385     if (level.native_em_level->ply[0]->alive == 0 &&
12386         level.native_em_level->ply[1]->alive == 0 &&
12387         level.native_em_level->ply[2]->alive == 0 &&
12388         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12389       AllPlayersGone = TRUE;
12390   }
12391   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12392   {
12393     if (game_sp.LevelSolved &&
12394         !game_sp.GameOver)                              /* game won */
12395     {
12396       PlayerWins(local_player);
12397
12398       game_sp.GameOver = TRUE;
12399
12400       AllPlayersGone = TRUE;
12401     }
12402
12403     if (game_sp.GameOver)                               /* game lost */
12404       AllPlayersGone = TRUE;
12405   }
12406
12407   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12408     GameWon();
12409
12410   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12411     TapeStop();
12412
12413   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12414     return;
12415
12416   game_frame_delay_value =
12417     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12418
12419   if (tape.playing && tape.warp_forward && !tape.pausing)
12420     game_frame_delay_value = 0;
12421
12422   /* ---------- main game synchronization point ---------- */
12423
12424   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12425
12426   if (network_playing && !network_player_action_received)
12427   {
12428     /* try to get network player actions in time */
12429
12430 #if defined(NETWORK_AVALIABLE)
12431     /* last chance to get network player actions without main loop delay */
12432     HandleNetworking();
12433 #endif
12434
12435     /* game was quit by network peer */
12436     if (game_status != GAME_MODE_PLAYING)
12437       return;
12438
12439     if (!network_player_action_received)
12440       return;           /* failed to get network player actions in time */
12441
12442     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12443   }
12444
12445   if (tape.pausing)
12446     return;
12447
12448   /* at this point we know that we really continue executing the game */
12449
12450   network_player_action_received = FALSE;
12451
12452   /* when playing tape, read previously recorded player input from tape data */
12453   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12454
12455 #if 1
12456   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12457   if (tape.pausing)
12458     return;
12459 #endif
12460
12461   if (tape.set_centered_player)
12462   {
12463     game.centered_player_nr_next = tape.centered_player_nr_next;
12464     game.set_centered_player = TRUE;
12465   }
12466
12467   for (i = 0; i < MAX_PLAYERS; i++)
12468   {
12469     summarized_player_action |= stored_player[i].action;
12470
12471 #if 1
12472     if (!network_playing && (game.team_mode || tape.playing))
12473       stored_player[i].effective_action = stored_player[i].action;
12474 #else
12475     if (!network_playing)
12476       stored_player[i].effective_action = stored_player[i].action;
12477 #endif
12478   }
12479
12480 #if defined(NETWORK_AVALIABLE)
12481   if (network_playing)
12482     SendToServer_MovePlayer(summarized_player_action);
12483 #endif
12484
12485   if (!options.network && !game.team_mode)
12486     local_player->effective_action = summarized_player_action;
12487
12488   if (tape.recording &&
12489       setup.team_mode &&
12490       setup.input_on_focus &&
12491       game.centered_player_nr != -1)
12492   {
12493     for (i = 0; i < MAX_PLAYERS; i++)
12494       stored_player[i].effective_action =
12495         (i == game.centered_player_nr ? summarized_player_action : 0);
12496   }
12497
12498   if (recorded_player_action != NULL)
12499     for (i = 0; i < MAX_PLAYERS; i++)
12500       stored_player[i].effective_action = recorded_player_action[i];
12501
12502   for (i = 0; i < MAX_PLAYERS; i++)
12503   {
12504     tape_action[i] = stored_player[i].effective_action;
12505
12506 #if 1
12507     /* (this may happen in the RND game engine if a player was not present on
12508        the playfield on level start, but appeared later from a custom element */
12509     if (tape.recording &&
12510         setup.team_mode &&
12511         tape_action[i] &&
12512         !tape.player_participates[i])
12513       tape.player_participates[i] = TRUE;
12514 #else
12515     /* (this can only happen in the R'n'D game engine) */
12516     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12517       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12518 #endif
12519   }
12520
12521   /* only record actions from input devices, but not programmed actions */
12522   if (tape.recording)
12523     TapeRecordAction(tape_action);
12524
12525 #if USE_NEW_PLAYER_ASSIGNMENTS
12526 #if 1
12527   if (game.team_mode)
12528 #endif
12529   {
12530     byte mapped_action[MAX_PLAYERS];
12531
12532 #if DEBUG_PLAYER_ACTIONS
12533     printf(":::");
12534     for (i = 0; i < MAX_PLAYERS; i++)
12535       printf(" %d, ", stored_player[i].effective_action);
12536 #endif
12537
12538     for (i = 0; i < MAX_PLAYERS; i++)
12539       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12540
12541     for (i = 0; i < MAX_PLAYERS; i++)
12542       stored_player[i].effective_action = mapped_action[i];
12543
12544 #if DEBUG_PLAYER_ACTIONS
12545     printf(" =>");
12546     for (i = 0; i < MAX_PLAYERS; i++)
12547       printf(" %d, ", stored_player[i].effective_action);
12548     printf("\n");
12549 #endif
12550   }
12551 #if DEBUG_PLAYER_ACTIONS
12552   else
12553   {
12554     printf(":::");
12555     for (i = 0; i < MAX_PLAYERS; i++)
12556       printf(" %d, ", stored_player[i].effective_action);
12557     printf("\n");
12558   }
12559 #endif
12560 #endif
12561
12562 #if 0
12563   printf("::: summarized_player_action == %d\n",
12564          local_player->effective_action);
12565 #endif
12566
12567
12568
12569
12570 #if 0
12571 #if DEBUG_INIT_PLAYER
12572     if (options.debug)
12573     {
12574       printf("Player status (final):\n");
12575
12576       for (i = 0; i < MAX_PLAYERS; i++)
12577       {
12578         struct PlayerInfo *player = &stored_player[i];
12579
12580         printf("- player %d: present == %d, connected == %d, active == %d",
12581                i + 1,
12582                player->present,
12583                player->connected,
12584                player->active);
12585
12586         if (local_player == player)
12587           printf(" (local player)");
12588
12589         printf("\n");
12590       }
12591     }
12592 #endif
12593 #endif
12594
12595
12596
12597   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12598   {
12599     GameActions_EM_Main();
12600   }
12601   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12602   {
12603     GameActions_SP_Main();
12604   }
12605   else
12606   {
12607     GameActions_RND();
12608   }
12609 }
12610
12611 void GameActions_EM_Main()
12612 {
12613   byte effective_action[MAX_PLAYERS];
12614   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12615   int i;
12616
12617   for (i = 0; i < MAX_PLAYERS; i++)
12618     effective_action[i] = stored_player[i].effective_action;
12619
12620   GameActions_EM(effective_action, warp_mode);
12621
12622   CheckLevelTime();
12623
12624   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12625 }
12626
12627 void GameActions_SP_Main()
12628 {
12629   byte effective_action[MAX_PLAYERS];
12630   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12631   int i;
12632
12633   for (i = 0; i < MAX_PLAYERS; i++)
12634     effective_action[i] = stored_player[i].effective_action;
12635
12636   GameActions_SP(effective_action, warp_mode);
12637
12638   CheckLevelTime();
12639
12640   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12641 }
12642
12643 void GameActions_RND()
12644 {
12645   int magic_wall_x = 0, magic_wall_y = 0;
12646   int i, x, y, element, graphic;
12647
12648   InitPlayfieldScanModeVars();
12649
12650 #if USE_ONE_MORE_CHANGE_PER_FRAME
12651   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12652   {
12653     SCAN_PLAYFIELD(x, y)
12654     {
12655       ChangeCount[x][y] = 0;
12656       ChangeEvent[x][y] = -1;
12657     }
12658   }
12659 #endif
12660
12661   if (game.set_centered_player)
12662   {
12663     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12664
12665     /* switching to "all players" only possible if all players fit to screen */
12666     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12667     {
12668       game.centered_player_nr_next = game.centered_player_nr;
12669       game.set_centered_player = FALSE;
12670     }
12671
12672     /* do not switch focus to non-existing (or non-active) player */
12673     if (game.centered_player_nr_next >= 0 &&
12674         !stored_player[game.centered_player_nr_next].active)
12675     {
12676       game.centered_player_nr_next = game.centered_player_nr;
12677       game.set_centered_player = FALSE;
12678     }
12679   }
12680
12681   if (game.set_centered_player &&
12682       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12683   {
12684     int sx, sy;
12685
12686     if (game.centered_player_nr_next == -1)
12687     {
12688       setScreenCenteredToAllPlayers(&sx, &sy);
12689     }
12690     else
12691     {
12692       sx = stored_player[game.centered_player_nr_next].jx;
12693       sy = stored_player[game.centered_player_nr_next].jy;
12694     }
12695
12696     game.centered_player_nr = game.centered_player_nr_next;
12697     game.set_centered_player = FALSE;
12698
12699     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12700     DrawGameDoorValues();
12701   }
12702
12703   for (i = 0; i < MAX_PLAYERS; i++)
12704   {
12705     int actual_player_action = stored_player[i].effective_action;
12706
12707 #if 1
12708     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12709        - rnd_equinox_tetrachloride 048
12710        - rnd_equinox_tetrachloride_ii 096
12711        - rnd_emanuel_schmieg 002
12712        - doctor_sloan_ww 001, 020
12713     */
12714     if (stored_player[i].MovPos == 0)
12715       CheckGravityMovement(&stored_player[i]);
12716 #endif
12717
12718     /* overwrite programmed action with tape action */
12719     if (stored_player[i].programmed_action)
12720       actual_player_action = stored_player[i].programmed_action;
12721
12722     PlayerActions(&stored_player[i], actual_player_action);
12723
12724     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12725   }
12726
12727   ScrollScreen(NULL, SCROLL_GO_ON);
12728
12729   /* for backwards compatibility, the following code emulates a fixed bug that
12730      occured when pushing elements (causing elements that just made their last
12731      pushing step to already (if possible) make their first falling step in the
12732      same game frame, which is bad); this code is also needed to use the famous
12733      "spring push bug" which is used in older levels and might be wanted to be
12734      used also in newer levels, but in this case the buggy pushing code is only
12735      affecting the "spring" element and no other elements */
12736
12737   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12738   {
12739     for (i = 0; i < MAX_PLAYERS; i++)
12740     {
12741       struct PlayerInfo *player = &stored_player[i];
12742       int x = player->jx;
12743       int y = player->jy;
12744
12745       if (player->active && player->is_pushing && player->is_moving &&
12746           IS_MOVING(x, y) &&
12747           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12748            Feld[x][y] == EL_SPRING))
12749       {
12750         ContinueMoving(x, y);
12751
12752         /* continue moving after pushing (this is actually a bug) */
12753         if (!IS_MOVING(x, y))
12754           Stop[x][y] = FALSE;
12755       }
12756     }
12757   }
12758
12759 #if 0
12760   debug_print_timestamp(0, "start main loop profiling");
12761 #endif
12762
12763   SCAN_PLAYFIELD(x, y)
12764   {
12765     ChangeCount[x][y] = 0;
12766     ChangeEvent[x][y] = -1;
12767
12768     /* this must be handled before main playfield loop */
12769     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12770     {
12771       MovDelay[x][y]--;
12772       if (MovDelay[x][y] <= 0)
12773         RemoveField(x, y);
12774     }
12775
12776 #if USE_NEW_SNAP_DELAY
12777     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12778     {
12779       MovDelay[x][y]--;
12780       if (MovDelay[x][y] <= 0)
12781       {
12782         RemoveField(x, y);
12783         TEST_DrawLevelField(x, y);
12784
12785         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12786       }
12787     }
12788 #endif
12789
12790 #if DEBUG
12791     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12792     {
12793       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12794       printf("GameActions(): This should never happen!\n");
12795
12796       ChangePage[x][y] = -1;
12797     }
12798 #endif
12799
12800     Stop[x][y] = FALSE;
12801     if (WasJustMoving[x][y] > 0)
12802       WasJustMoving[x][y]--;
12803     if (WasJustFalling[x][y] > 0)
12804       WasJustFalling[x][y]--;
12805     if (CheckCollision[x][y] > 0)
12806       CheckCollision[x][y]--;
12807     if (CheckImpact[x][y] > 0)
12808       CheckImpact[x][y]--;
12809
12810     GfxFrame[x][y]++;
12811
12812     /* reset finished pushing action (not done in ContinueMoving() to allow
12813        continuous pushing animation for elements with zero push delay) */
12814     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12815     {
12816       ResetGfxAnimation(x, y);
12817       TEST_DrawLevelField(x, y);
12818     }
12819
12820 #if DEBUG
12821     if (IS_BLOCKED(x, y))
12822     {
12823       int oldx, oldy;
12824
12825       Blocked2Moving(x, y, &oldx, &oldy);
12826       if (!IS_MOVING(oldx, oldy))
12827       {
12828         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12829         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12830         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12831         printf("GameActions(): This should never happen!\n");
12832       }
12833     }
12834 #endif
12835   }
12836
12837 #if 0
12838   debug_print_timestamp(0, "- time for pre-main loop:");
12839 #endif
12840
12841 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12842   SCAN_PLAYFIELD(x, y)
12843   {
12844     element = Feld[x][y];
12845     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12846
12847 #if 1
12848     {
12849 #if 1
12850       int element2 = element;
12851       int graphic2 = graphic;
12852 #else
12853       int element2 = Feld[x][y];
12854       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12855 #endif
12856       int last_gfx_frame = GfxFrame[x][y];
12857
12858       if (graphic_info[graphic2].anim_global_sync)
12859         GfxFrame[x][y] = FrameCounter;
12860       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12861         GfxFrame[x][y] = CustomValue[x][y];
12862       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12863         GfxFrame[x][y] = element_info[element2].collect_score;
12864       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12865         GfxFrame[x][y] = ChangeDelay[x][y];
12866
12867       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12868         DrawLevelGraphicAnimation(x, y, graphic2);
12869     }
12870 #else
12871     ResetGfxFrame(x, y, TRUE);
12872 #endif
12873
12874 #if 1
12875     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12876         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12877       ResetRandomAnimationValue(x, y);
12878 #endif
12879
12880 #if 1
12881     SetRandomAnimationValue(x, y);
12882 #endif
12883
12884 #if 1
12885     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12886 #endif
12887   }
12888 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12889
12890 #if 0
12891   debug_print_timestamp(0, "- time for TEST loop:     -->");
12892 #endif
12893
12894   SCAN_PLAYFIELD(x, y)
12895   {
12896     element = Feld[x][y];
12897     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12898
12899     ResetGfxFrame(x, y, TRUE);
12900
12901     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12902         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12903       ResetRandomAnimationValue(x, y);
12904
12905     SetRandomAnimationValue(x, y);
12906
12907     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12908
12909     if (IS_INACTIVE(element))
12910     {
12911       if (IS_ANIMATED(graphic))
12912         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12913
12914       continue;
12915     }
12916
12917     /* this may take place after moving, so 'element' may have changed */
12918     if (IS_CHANGING(x, y) &&
12919         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12920     {
12921       int page = element_info[element].event_page_nr[CE_DELAY];
12922
12923 #if 1
12924       HandleElementChange(x, y, page);
12925 #else
12926       if (CAN_CHANGE(element))
12927         HandleElementChange(x, y, page);
12928
12929       if (HAS_ACTION(element))
12930         ExecuteCustomElementAction(x, y, element, page);
12931 #endif
12932
12933       element = Feld[x][y];
12934       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12935     }
12936
12937 #if 0   // ---------------------------------------------------------------------
12938
12939     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12940     {
12941       StartMoving(x, y);
12942
12943       element = Feld[x][y];
12944       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12945
12946       if (IS_ANIMATED(graphic) &&
12947           !IS_MOVING(x, y) &&
12948           !Stop[x][y])
12949         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12950
12951       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12952         TEST_DrawTwinkleOnField(x, y);
12953     }
12954     else if (IS_MOVING(x, y))
12955       ContinueMoving(x, y);
12956     else
12957     {
12958       switch (element)
12959       {
12960         case EL_ACID:
12961         case EL_EXIT_OPEN:
12962         case EL_EM_EXIT_OPEN:
12963         case EL_SP_EXIT_OPEN:
12964         case EL_STEEL_EXIT_OPEN:
12965         case EL_EM_STEEL_EXIT_OPEN:
12966         case EL_SP_TERMINAL:
12967         case EL_SP_TERMINAL_ACTIVE:
12968         case EL_EXTRA_TIME:
12969         case EL_SHIELD_NORMAL:
12970         case EL_SHIELD_DEADLY:
12971           if (IS_ANIMATED(graphic))
12972             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12973           break;
12974
12975         case EL_DYNAMITE_ACTIVE:
12976         case EL_EM_DYNAMITE_ACTIVE:
12977         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12978         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12979         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12980         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12981         case EL_SP_DISK_RED_ACTIVE:
12982           CheckDynamite(x, y);
12983           break;
12984
12985         case EL_AMOEBA_GROWING:
12986           AmoebeWaechst(x, y);
12987           break;
12988
12989         case EL_AMOEBA_SHRINKING:
12990           AmoebaDisappearing(x, y);
12991           break;
12992
12993 #if !USE_NEW_AMOEBA_CODE
12994         case EL_AMOEBA_WET:
12995         case EL_AMOEBA_DRY:
12996         case EL_AMOEBA_FULL:
12997         case EL_BD_AMOEBA:
12998         case EL_EMC_DRIPPER:
12999           AmoebeAbleger(x, y);
13000           break;
13001 #endif
13002
13003         case EL_GAME_OF_LIFE:
13004         case EL_BIOMAZE:
13005           Life(x, y);
13006           break;
13007
13008         case EL_EXIT_CLOSED:
13009           CheckExit(x, y);
13010           break;
13011
13012         case EL_EM_EXIT_CLOSED:
13013           CheckExitEM(x, y);
13014           break;
13015
13016         case EL_STEEL_EXIT_CLOSED:
13017           CheckExitSteel(x, y);
13018           break;
13019
13020         case EL_EM_STEEL_EXIT_CLOSED:
13021           CheckExitSteelEM(x, y);
13022           break;
13023
13024         case EL_SP_EXIT_CLOSED:
13025           CheckExitSP(x, y);
13026           break;
13027
13028         case EL_EXPANDABLE_WALL_GROWING:
13029         case EL_EXPANDABLE_STEELWALL_GROWING:
13030           MauerWaechst(x, y);
13031           break;
13032
13033         case EL_EXPANDABLE_WALL:
13034         case EL_EXPANDABLE_WALL_HORIZONTAL:
13035         case EL_EXPANDABLE_WALL_VERTICAL:
13036         case EL_EXPANDABLE_WALL_ANY:
13037         case EL_BD_EXPANDABLE_WALL:
13038           MauerAbleger(x, y);
13039           break;
13040
13041         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13042         case EL_EXPANDABLE_STEELWALL_VERTICAL:
13043         case EL_EXPANDABLE_STEELWALL_ANY:
13044           MauerAblegerStahl(x, y);
13045           break;
13046
13047         case EL_FLAMES:
13048           CheckForDragon(x, y);
13049           break;
13050
13051         case EL_EXPLOSION:
13052           break;
13053
13054         case EL_ELEMENT_SNAPPING:
13055         case EL_DIAGONAL_SHRINKING:
13056         case EL_DIAGONAL_GROWING:
13057         {
13058           graphic =
13059             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13060
13061           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13062           break;
13063         }
13064
13065         default:
13066           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13067             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13068           break;
13069       }
13070     }
13071
13072 #else   // ---------------------------------------------------------------------
13073
13074     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13075     {
13076       StartMoving(x, y);
13077
13078       element = Feld[x][y];
13079       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13080
13081       if (IS_ANIMATED(graphic) &&
13082           !IS_MOVING(x, y) &&
13083           !Stop[x][y])
13084         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13085
13086       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13087         TEST_DrawTwinkleOnField(x, y);
13088     }
13089     else if ((element == EL_ACID ||
13090               element == EL_EXIT_OPEN ||
13091               element == EL_EM_EXIT_OPEN ||
13092               element == EL_SP_EXIT_OPEN ||
13093               element == EL_STEEL_EXIT_OPEN ||
13094               element == EL_EM_STEEL_EXIT_OPEN ||
13095               element == EL_SP_TERMINAL ||
13096               element == EL_SP_TERMINAL_ACTIVE ||
13097               element == EL_EXTRA_TIME ||
13098               element == EL_SHIELD_NORMAL ||
13099               element == EL_SHIELD_DEADLY) &&
13100              IS_ANIMATED(graphic))
13101       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13102     else if (IS_MOVING(x, y))
13103       ContinueMoving(x, y);
13104     else if (IS_ACTIVE_BOMB(element))
13105       CheckDynamite(x, y);
13106     else if (element == EL_AMOEBA_GROWING)
13107       AmoebeWaechst(x, y);
13108     else if (element == EL_AMOEBA_SHRINKING)
13109       AmoebaDisappearing(x, y);
13110
13111 #if !USE_NEW_AMOEBA_CODE
13112     else if (IS_AMOEBALIVE(element))
13113       AmoebeAbleger(x, y);
13114 #endif
13115
13116     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13117       Life(x, y);
13118     else if (element == EL_EXIT_CLOSED)
13119       CheckExit(x, y);
13120     else if (element == EL_EM_EXIT_CLOSED)
13121       CheckExitEM(x, y);
13122     else if (element == EL_STEEL_EXIT_CLOSED)
13123       CheckExitSteel(x, y);
13124     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13125       CheckExitSteelEM(x, y);
13126     else if (element == EL_SP_EXIT_CLOSED)
13127       CheckExitSP(x, y);
13128     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13129              element == EL_EXPANDABLE_STEELWALL_GROWING)
13130       MauerWaechst(x, y);
13131     else if (element == EL_EXPANDABLE_WALL ||
13132              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13133              element == EL_EXPANDABLE_WALL_VERTICAL ||
13134              element == EL_EXPANDABLE_WALL_ANY ||
13135              element == EL_BD_EXPANDABLE_WALL)
13136       MauerAbleger(x, y);
13137     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13138              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13139              element == EL_EXPANDABLE_STEELWALL_ANY)
13140       MauerAblegerStahl(x, y);
13141     else if (element == EL_FLAMES)
13142       CheckForDragon(x, y);
13143     else if (element == EL_EXPLOSION)
13144       ; /* drawing of correct explosion animation is handled separately */
13145     else if (element == EL_ELEMENT_SNAPPING ||
13146              element == EL_DIAGONAL_SHRINKING ||
13147              element == EL_DIAGONAL_GROWING)
13148     {
13149       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13150
13151       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13152     }
13153     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13154       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13155
13156 #endif  // ---------------------------------------------------------------------
13157
13158     if (IS_BELT_ACTIVE(element))
13159       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13160
13161     if (game.magic_wall_active)
13162     {
13163       int jx = local_player->jx, jy = local_player->jy;
13164
13165       /* play the element sound at the position nearest to the player */
13166       if ((element == EL_MAGIC_WALL_FULL ||
13167            element == EL_MAGIC_WALL_ACTIVE ||
13168            element == EL_MAGIC_WALL_EMPTYING ||
13169            element == EL_BD_MAGIC_WALL_FULL ||
13170            element == EL_BD_MAGIC_WALL_ACTIVE ||
13171            element == EL_BD_MAGIC_WALL_EMPTYING ||
13172            element == EL_DC_MAGIC_WALL_FULL ||
13173            element == EL_DC_MAGIC_WALL_ACTIVE ||
13174            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13175           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13176       {
13177         magic_wall_x = x;
13178         magic_wall_y = y;
13179       }
13180     }
13181   }
13182
13183 #if 0
13184   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13185 #endif
13186
13187 #if USE_NEW_AMOEBA_CODE
13188   /* new experimental amoeba growth stuff */
13189   if (!(FrameCounter % 8))
13190   {
13191     static unsigned int random = 1684108901;
13192
13193     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13194     {
13195       x = RND(lev_fieldx);
13196       y = RND(lev_fieldy);
13197       element = Feld[x][y];
13198
13199       if (!IS_PLAYER(x,y) &&
13200           (element == EL_EMPTY ||
13201            CAN_GROW_INTO(element) ||
13202            element == EL_QUICKSAND_EMPTY ||
13203            element == EL_QUICKSAND_FAST_EMPTY ||
13204            element == EL_ACID_SPLASH_LEFT ||
13205            element == EL_ACID_SPLASH_RIGHT))
13206       {
13207         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13208             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13209             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13210             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13211           Feld[x][y] = EL_AMOEBA_DROP;
13212       }
13213
13214       random = random * 129 + 1;
13215     }
13216   }
13217 #endif
13218
13219 #if 0
13220   if (game.explosions_delayed)
13221 #endif
13222   {
13223     game.explosions_delayed = FALSE;
13224
13225     SCAN_PLAYFIELD(x, y)
13226     {
13227       element = Feld[x][y];
13228
13229       if (ExplodeField[x][y])
13230         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13231       else if (element == EL_EXPLOSION)
13232         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13233
13234       ExplodeField[x][y] = EX_TYPE_NONE;
13235     }
13236
13237     game.explosions_delayed = TRUE;
13238   }
13239
13240   if (game.magic_wall_active)
13241   {
13242     if (!(game.magic_wall_time_left % 4))
13243     {
13244       int element = Feld[magic_wall_x][magic_wall_y];
13245
13246       if (element == EL_BD_MAGIC_WALL_FULL ||
13247           element == EL_BD_MAGIC_WALL_ACTIVE ||
13248           element == EL_BD_MAGIC_WALL_EMPTYING)
13249         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13250       else if (element == EL_DC_MAGIC_WALL_FULL ||
13251                element == EL_DC_MAGIC_WALL_ACTIVE ||
13252                element == EL_DC_MAGIC_WALL_EMPTYING)
13253         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13254       else
13255         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13256     }
13257
13258     if (game.magic_wall_time_left > 0)
13259     {
13260       game.magic_wall_time_left--;
13261
13262       if (!game.magic_wall_time_left)
13263       {
13264         SCAN_PLAYFIELD(x, y)
13265         {
13266           element = Feld[x][y];
13267
13268           if (element == EL_MAGIC_WALL_ACTIVE ||
13269               element == EL_MAGIC_WALL_FULL)
13270           {
13271             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13272             TEST_DrawLevelField(x, y);
13273           }
13274           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13275                    element == EL_BD_MAGIC_WALL_FULL)
13276           {
13277             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13278             TEST_DrawLevelField(x, y);
13279           }
13280           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13281                    element == EL_DC_MAGIC_WALL_FULL)
13282           {
13283             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13284             TEST_DrawLevelField(x, y);
13285           }
13286         }
13287
13288         game.magic_wall_active = FALSE;
13289       }
13290     }
13291   }
13292
13293   if (game.light_time_left > 0)
13294   {
13295     game.light_time_left--;
13296
13297     if (game.light_time_left == 0)
13298       RedrawAllLightSwitchesAndInvisibleElements();
13299   }
13300
13301   if (game.timegate_time_left > 0)
13302   {
13303     game.timegate_time_left--;
13304
13305     if (game.timegate_time_left == 0)
13306       CloseAllOpenTimegates();
13307   }
13308
13309   if (game.lenses_time_left > 0)
13310   {
13311     game.lenses_time_left--;
13312
13313     if (game.lenses_time_left == 0)
13314       RedrawAllInvisibleElementsForLenses();
13315   }
13316
13317   if (game.magnify_time_left > 0)
13318   {
13319     game.magnify_time_left--;
13320
13321     if (game.magnify_time_left == 0)
13322       RedrawAllInvisibleElementsForMagnifier();
13323   }
13324
13325   for (i = 0; i < MAX_PLAYERS; i++)
13326   {
13327     struct PlayerInfo *player = &stored_player[i];
13328
13329     if (SHIELD_ON(player))
13330     {
13331       if (player->shield_deadly_time_left)
13332         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13333       else if (player->shield_normal_time_left)
13334         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13335     }
13336   }
13337
13338 #if USE_DELAYED_GFX_REDRAW
13339   SCAN_PLAYFIELD(x, y)
13340   {
13341 #if 1
13342     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13343 #else
13344     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13345         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13346 #endif
13347     {
13348       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13349          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13350
13351       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13352         DrawLevelField(x, y);
13353
13354       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13355         DrawLevelFieldCrumbled(x, y);
13356
13357       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13358         DrawLevelFieldCrumbledNeighbours(x, y);
13359
13360       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13361         DrawTwinkleOnField(x, y);
13362     }
13363
13364     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13365   }
13366 #endif
13367
13368   CheckLevelTime();
13369
13370   DrawAllPlayers();
13371   PlayAllPlayersSound();
13372
13373   if (options.debug)                    /* calculate frames per second */
13374   {
13375     static unsigned int fps_counter = 0;
13376     static int fps_frames = 0;
13377     unsigned int fps_delay_ms = Counter() - fps_counter;
13378
13379     fps_frames++;
13380
13381     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13382     {
13383       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13384
13385       fps_frames = 0;
13386       fps_counter = Counter();
13387     }
13388
13389     redraw_mask |= REDRAW_FPS;
13390   }
13391
13392   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13393
13394   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13395   {
13396     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13397
13398     local_player->show_envelope = 0;
13399   }
13400
13401 #if 0
13402   debug_print_timestamp(0, "stop main loop profiling ");
13403   printf("----------------------------------------------------------\n");
13404 #endif
13405
13406   /* use random number generator in every frame to make it less predictable */
13407   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13408     RND(1);
13409 }
13410
13411 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13412 {
13413   int min_x = x, min_y = y, max_x = x, max_y = y;
13414   int i;
13415
13416   for (i = 0; i < MAX_PLAYERS; i++)
13417   {
13418     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13419
13420     if (!stored_player[i].active || &stored_player[i] == player)
13421       continue;
13422
13423     min_x = MIN(min_x, jx);
13424     min_y = MIN(min_y, jy);
13425     max_x = MAX(max_x, jx);
13426     max_y = MAX(max_y, jy);
13427   }
13428
13429   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13430 }
13431
13432 static boolean AllPlayersInVisibleScreen()
13433 {
13434   int i;
13435
13436   for (i = 0; i < MAX_PLAYERS; i++)
13437   {
13438     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13439
13440     if (!stored_player[i].active)
13441       continue;
13442
13443     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13444       return FALSE;
13445   }
13446
13447   return TRUE;
13448 }
13449
13450 void ScrollLevel(int dx, int dy)
13451 {
13452 #if 0
13453   /* (directly solved in BlitBitmap() now) */
13454   static Bitmap *bitmap_db_field2 = NULL;
13455   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13456   int x, y;
13457 #else
13458   int x, y;
13459 #endif
13460
13461 #if 0
13462   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13463   /* only horizontal XOR vertical scroll direction allowed */
13464   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13465     return;
13466 #endif
13467
13468 #if 0
13469   /* (directly solved in BlitBitmap() now) */
13470   if (bitmap_db_field2 == NULL)
13471     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13472
13473   /* needed when blitting directly to same bitmap -- should not be needed with
13474      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13475   BlitBitmap(drawto_field, bitmap_db_field2,
13476              FX + TILEX * (dx == -1) - softscroll_offset,
13477              FY + TILEY * (dy == -1) - softscroll_offset,
13478              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13479              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13480              FX + TILEX * (dx == 1) - softscroll_offset,
13481              FY + TILEY * (dy == 1) - softscroll_offset);
13482   BlitBitmap(bitmap_db_field2, drawto_field,
13483              FX + TILEX * (dx == 1) - softscroll_offset,
13484              FY + TILEY * (dy == 1) - softscroll_offset,
13485              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13486              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13487              FX + TILEX * (dx == 1) - softscroll_offset,
13488              FY + TILEY * (dy == 1) - softscroll_offset);
13489
13490 #else
13491
13492 #if 0
13493   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13494   int xsize = (BX2 - BX1 + 1);
13495   int ysize = (BY2 - BY1 + 1);
13496   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13497   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13498   int step  = (start < end ? +1 : -1);
13499
13500   for (i = start; i != end; i += step)
13501   {
13502     BlitBitmap(drawto_field, drawto_field,
13503                FX + TILEX * (dx != 0 ? i + step : 0),
13504                FY + TILEY * (dy != 0 ? i + step : 0),
13505                TILEX * (dx != 0 ? 1 : xsize),
13506                TILEY * (dy != 0 ? 1 : ysize),
13507                FX + TILEX * (dx != 0 ? i : 0),
13508                FY + TILEY * (dy != 0 ? i : 0));
13509   }
13510
13511 #else
13512
13513 #if NEW_TILESIZE
13514 #if NEW_SCROLL
13515   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13516 #else
13517   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13518 #endif
13519 #else
13520 #if NEW_SCROLL
13521   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13522 #else
13523   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13524 #endif
13525 #endif
13526
13527 #if NEW_TILESIZE
13528   BlitBitmap(drawto_field, drawto_field,
13529              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13530              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13531              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13532              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13533              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13534              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13535 #else
13536   BlitBitmap(drawto_field, drawto_field,
13537              FX + TILEX * (dx == -1) - softscroll_offset,
13538              FY + TILEY * (dy == -1) - softscroll_offset,
13539              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13540              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13541              FX + TILEX * (dx == 1) - softscroll_offset,
13542              FY + TILEY * (dy == 1) - softscroll_offset);
13543 #endif
13544
13545 #endif
13546 #endif
13547
13548   if (dx != 0)
13549   {
13550     x = (dx == 1 ? BX1 : BX2);
13551     for (y = BY1; y <= BY2; y++)
13552       DrawScreenField(x, y);
13553   }
13554
13555   if (dy != 0)
13556   {
13557     y = (dy == 1 ? BY1 : BY2);
13558     for (x = BX1; x <= BX2; x++)
13559       DrawScreenField(x, y);
13560   }
13561
13562   redraw_mask |= REDRAW_FIELD;
13563 }
13564
13565 static boolean canFallDown(struct PlayerInfo *player)
13566 {
13567   int jx = player->jx, jy = player->jy;
13568
13569   return (IN_LEV_FIELD(jx, jy + 1) &&
13570           (IS_FREE(jx, jy + 1) ||
13571            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13572           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13573           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13574 }
13575
13576 static boolean canPassField(int x, int y, int move_dir)
13577 {
13578   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13579   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13580   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13581   int nextx = x + dx;
13582   int nexty = y + dy;
13583   int element = Feld[x][y];
13584
13585   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13586           !CAN_MOVE(element) &&
13587           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13588           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13589           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13590 }
13591
13592 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13593 {
13594   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13595   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13596   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13597   int newx = x + dx;
13598   int newy = y + dy;
13599
13600   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13601           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13602           (IS_DIGGABLE(Feld[newx][newy]) ||
13603            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13604            canPassField(newx, newy, move_dir)));
13605 }
13606
13607 static void CheckGravityMovement(struct PlayerInfo *player)
13608 {
13609 #if USE_PLAYER_GRAVITY
13610   if (player->gravity && !player->programmed_action)
13611 #else
13612   if (game.gravity && !player->programmed_action)
13613 #endif
13614   {
13615     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13616     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13617     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13618     int jx = player->jx, jy = player->jy;
13619     boolean player_is_moving_to_valid_field =
13620       (!player_is_snapping &&
13621        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13622         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13623     boolean player_can_fall_down = canFallDown(player);
13624
13625     if (player_can_fall_down &&
13626         !player_is_moving_to_valid_field)
13627       player->programmed_action = MV_DOWN;
13628   }
13629 }
13630
13631 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13632 {
13633   return CheckGravityMovement(player);
13634
13635 #if USE_PLAYER_GRAVITY
13636   if (player->gravity && !player->programmed_action)
13637 #else
13638   if (game.gravity && !player->programmed_action)
13639 #endif
13640   {
13641     int jx = player->jx, jy = player->jy;
13642     boolean field_under_player_is_free =
13643       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13644     boolean player_is_standing_on_valid_field =
13645       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13646        (IS_WALKABLE(Feld[jx][jy]) &&
13647         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13648
13649     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13650       player->programmed_action = MV_DOWN;
13651   }
13652 }
13653
13654 /*
13655   MovePlayerOneStep()
13656   -----------------------------------------------------------------------------
13657   dx, dy:               direction (non-diagonal) to try to move the player to
13658   real_dx, real_dy:     direction as read from input device (can be diagonal)
13659 */
13660
13661 boolean MovePlayerOneStep(struct PlayerInfo *player,
13662                           int dx, int dy, int real_dx, int real_dy)
13663 {
13664   int jx = player->jx, jy = player->jy;
13665   int new_jx = jx + dx, new_jy = jy + dy;
13666 #if !USE_FIXED_DONT_RUN_INTO
13667   int element;
13668 #endif
13669   int can_move;
13670   boolean player_can_move = !player->cannot_move;
13671
13672   if (!player->active || (!dx && !dy))
13673     return MP_NO_ACTION;
13674
13675   player->MovDir = (dx < 0 ? MV_LEFT :
13676                     dx > 0 ? MV_RIGHT :
13677                     dy < 0 ? MV_UP :
13678                     dy > 0 ? MV_DOWN :  MV_NONE);
13679
13680   if (!IN_LEV_FIELD(new_jx, new_jy))
13681     return MP_NO_ACTION;
13682
13683   if (!player_can_move)
13684   {
13685     if (player->MovPos == 0)
13686     {
13687       player->is_moving = FALSE;
13688       player->is_digging = FALSE;
13689       player->is_collecting = FALSE;
13690       player->is_snapping = FALSE;
13691       player->is_pushing = FALSE;
13692     }
13693   }
13694
13695 #if 1
13696   if (!options.network && game.centered_player_nr == -1 &&
13697       !AllPlayersInSight(player, new_jx, new_jy))
13698     return MP_NO_ACTION;
13699 #else
13700   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13701     return MP_NO_ACTION;
13702 #endif
13703
13704 #if !USE_FIXED_DONT_RUN_INTO
13705   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13706
13707   /* (moved to DigField()) */
13708   if (player_can_move && DONT_RUN_INTO(element))
13709   {
13710     if (element == EL_ACID && dx == 0 && dy == 1)
13711     {
13712       SplashAcid(new_jx, new_jy);
13713       Feld[jx][jy] = EL_PLAYER_1;
13714       InitMovingField(jx, jy, MV_DOWN);
13715       Store[jx][jy] = EL_ACID;
13716       ContinueMoving(jx, jy);
13717       BuryPlayer(player);
13718     }
13719     else
13720       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13721
13722     return MP_MOVING;
13723   }
13724 #endif
13725
13726   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13727   if (can_move != MP_MOVING)
13728     return can_move;
13729
13730   /* check if DigField() has caused relocation of the player */
13731   if (player->jx != jx || player->jy != jy)
13732     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13733
13734   StorePlayer[jx][jy] = 0;
13735   player->last_jx = jx;
13736   player->last_jy = jy;
13737   player->jx = new_jx;
13738   player->jy = new_jy;
13739   StorePlayer[new_jx][new_jy] = player->element_nr;
13740
13741   if (player->move_delay_value_next != -1)
13742   {
13743     player->move_delay_value = player->move_delay_value_next;
13744     player->move_delay_value_next = -1;
13745   }
13746
13747   player->MovPos =
13748     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13749
13750   player->step_counter++;
13751
13752   PlayerVisit[jx][jy] = FrameCounter;
13753
13754 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13755   player->is_moving = TRUE;
13756 #endif
13757
13758 #if 1
13759   /* should better be called in MovePlayer(), but this breaks some tapes */
13760   ScrollPlayer(player, SCROLL_INIT);
13761 #endif
13762
13763   return MP_MOVING;
13764 }
13765
13766 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13767 {
13768   int jx = player->jx, jy = player->jy;
13769   int old_jx = jx, old_jy = jy;
13770   int moved = MP_NO_ACTION;
13771
13772   if (!player->active)
13773     return FALSE;
13774
13775   if (!dx && !dy)
13776   {
13777     if (player->MovPos == 0)
13778     {
13779       player->is_moving = FALSE;
13780       player->is_digging = FALSE;
13781       player->is_collecting = FALSE;
13782       player->is_snapping = FALSE;
13783       player->is_pushing = FALSE;
13784     }
13785
13786     return FALSE;
13787   }
13788
13789   if (player->move_delay > 0)
13790     return FALSE;
13791
13792   player->move_delay = -1;              /* set to "uninitialized" value */
13793
13794   /* store if player is automatically moved to next field */
13795   player->is_auto_moving = (player->programmed_action != MV_NONE);
13796
13797   /* remove the last programmed player action */
13798   player->programmed_action = 0;
13799
13800   if (player->MovPos)
13801   {
13802     /* should only happen if pre-1.2 tape recordings are played */
13803     /* this is only for backward compatibility */
13804
13805     int original_move_delay_value = player->move_delay_value;
13806
13807 #if DEBUG
13808     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13809            tape.counter);
13810 #endif
13811
13812     /* scroll remaining steps with finest movement resolution */
13813     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13814
13815     while (player->MovPos)
13816     {
13817       ScrollPlayer(player, SCROLL_GO_ON);
13818       ScrollScreen(NULL, SCROLL_GO_ON);
13819
13820       AdvanceFrameAndPlayerCounters(player->index_nr);
13821
13822       DrawAllPlayers();
13823       BackToFront();
13824     }
13825
13826     player->move_delay_value = original_move_delay_value;
13827   }
13828
13829   player->is_active = FALSE;
13830
13831   if (player->last_move_dir & MV_HORIZONTAL)
13832   {
13833     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13834       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13835   }
13836   else
13837   {
13838     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13839       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13840   }
13841
13842 #if USE_FIXED_BORDER_RUNNING_GFX
13843   if (!moved && !player->is_active)
13844   {
13845     player->is_moving = FALSE;
13846     player->is_digging = FALSE;
13847     player->is_collecting = FALSE;
13848     player->is_snapping = FALSE;
13849     player->is_pushing = FALSE;
13850   }
13851 #endif
13852
13853   jx = player->jx;
13854   jy = player->jy;
13855
13856 #if 1
13857   if (moved & MP_MOVING && !ScreenMovPos &&
13858       (player->index_nr == game.centered_player_nr ||
13859        game.centered_player_nr == -1))
13860 #else
13861   if (moved & MP_MOVING && !ScreenMovPos &&
13862       (player == local_player || !options.network))
13863 #endif
13864   {
13865     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13866     int offset = game.scroll_delay_value;
13867
13868     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13869     {
13870       /* actual player has left the screen -- scroll in that direction */
13871       if (jx != old_jx)         /* player has moved horizontally */
13872         scroll_x += (jx - old_jx);
13873       else                      /* player has moved vertically */
13874         scroll_y += (jy - old_jy);
13875     }
13876     else
13877     {
13878       if (jx != old_jx)         /* player has moved horizontally */
13879       {
13880         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13881             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13882           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13883
13884         /* don't scroll over playfield boundaries */
13885         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13886           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13887
13888         /* don't scroll more than one field at a time */
13889         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13890
13891         /* don't scroll against the player's moving direction */
13892         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13893             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13894           scroll_x = old_scroll_x;
13895       }
13896       else                      /* player has moved vertically */
13897       {
13898         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13899             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13900           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13901
13902         /* don't scroll over playfield boundaries */
13903         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13904           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13905
13906         /* don't scroll more than one field at a time */
13907         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13908
13909         /* don't scroll against the player's moving direction */
13910         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13911             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13912           scroll_y = old_scroll_y;
13913       }
13914     }
13915
13916     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13917     {
13918 #if 1
13919       if (!options.network && game.centered_player_nr == -1 &&
13920           !AllPlayersInVisibleScreen())
13921       {
13922         scroll_x = old_scroll_x;
13923         scroll_y = old_scroll_y;
13924       }
13925       else
13926 #else
13927       if (!options.network && !AllPlayersInVisibleScreen())
13928       {
13929         scroll_x = old_scroll_x;
13930         scroll_y = old_scroll_y;
13931       }
13932       else
13933 #endif
13934       {
13935         ScrollScreen(player, SCROLL_INIT);
13936         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13937       }
13938     }
13939   }
13940
13941   player->StepFrame = 0;
13942
13943   if (moved & MP_MOVING)
13944   {
13945     if (old_jx != jx && old_jy == jy)
13946       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13947     else if (old_jx == jx && old_jy != jy)
13948       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13949
13950     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13951
13952     player->last_move_dir = player->MovDir;
13953     player->is_moving = TRUE;
13954     player->is_snapping = FALSE;
13955     player->is_switching = FALSE;
13956     player->is_dropping = FALSE;
13957     player->is_dropping_pressed = FALSE;
13958     player->drop_pressed_delay = 0;
13959
13960 #if 0
13961     /* should better be called here than above, but this breaks some tapes */
13962     ScrollPlayer(player, SCROLL_INIT);
13963 #endif
13964   }
13965   else
13966   {
13967     CheckGravityMovementWhenNotMoving(player);
13968
13969     player->is_moving = FALSE;
13970
13971     /* at this point, the player is allowed to move, but cannot move right now
13972        (e.g. because of something blocking the way) -- ensure that the player
13973        is also allowed to move in the next frame (in old versions before 3.1.1,
13974        the player was forced to wait again for eight frames before next try) */
13975
13976     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13977       player->move_delay = 0;   /* allow direct movement in the next frame */
13978   }
13979
13980   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13981     player->move_delay = player->move_delay_value;
13982
13983   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13984   {
13985     TestIfPlayerTouchesBadThing(jx, jy);
13986     TestIfPlayerTouchesCustomElement(jx, jy);
13987   }
13988
13989   if (!player->active)
13990     RemovePlayer(player);
13991
13992   return moved;
13993 }
13994
13995 void ScrollPlayer(struct PlayerInfo *player, int mode)
13996 {
13997   int jx = player->jx, jy = player->jy;
13998   int last_jx = player->last_jx, last_jy = player->last_jy;
13999   int move_stepsize = TILEX / player->move_delay_value;
14000
14001 #if USE_NEW_PLAYER_SPEED
14002   if (!player->active)
14003     return;
14004
14005   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
14006     return;
14007 #else
14008   if (!player->active || player->MovPos == 0)
14009     return;
14010 #endif
14011
14012   if (mode == SCROLL_INIT)
14013   {
14014     player->actual_frame_counter = FrameCounter;
14015     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14016
14017     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14018         Feld[last_jx][last_jy] == EL_EMPTY)
14019     {
14020       int last_field_block_delay = 0;   /* start with no blocking at all */
14021       int block_delay_adjustment = player->block_delay_adjustment;
14022
14023       /* if player blocks last field, add delay for exactly one move */
14024       if (player->block_last_field)
14025       {
14026         last_field_block_delay += player->move_delay_value;
14027
14028         /* when blocking enabled, prevent moving up despite gravity */
14029 #if USE_PLAYER_GRAVITY
14030         if (player->gravity && player->MovDir == MV_UP)
14031           block_delay_adjustment = -1;
14032 #else
14033         if (game.gravity && player->MovDir == MV_UP)
14034           block_delay_adjustment = -1;
14035 #endif
14036       }
14037
14038       /* add block delay adjustment (also possible when not blocking) */
14039       last_field_block_delay += block_delay_adjustment;
14040
14041       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14042       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14043     }
14044
14045 #if USE_NEW_PLAYER_SPEED
14046     if (player->MovPos != 0)    /* player has not yet reached destination */
14047       return;
14048 #else
14049     return;
14050 #endif
14051   }
14052   else if (!FrameReached(&player->actual_frame_counter, 1))
14053     return;
14054
14055 #if USE_NEW_PLAYER_SPEED
14056   if (player->MovPos != 0)
14057   {
14058     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14059     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14060
14061     /* before DrawPlayer() to draw correct player graphic for this case */
14062     if (player->MovPos == 0)
14063       CheckGravityMovement(player);
14064   }
14065 #else
14066   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14067   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14068
14069   /* before DrawPlayer() to draw correct player graphic for this case */
14070   if (player->MovPos == 0)
14071     CheckGravityMovement(player);
14072 #endif
14073
14074   if (player->MovPos == 0)      /* player reached destination field */
14075   {
14076     if (player->move_delay_reset_counter > 0)
14077     {
14078       player->move_delay_reset_counter--;
14079
14080       if (player->move_delay_reset_counter == 0)
14081       {
14082         /* continue with normal speed after quickly moving through gate */
14083         HALVE_PLAYER_SPEED(player);
14084
14085         /* be able to make the next move without delay */
14086         player->move_delay = 0;
14087       }
14088     }
14089
14090     player->last_jx = jx;
14091     player->last_jy = jy;
14092
14093     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14094         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14095 #if 1
14096         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14097 #endif
14098         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14099         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14100 #if 1
14101         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14102 #endif
14103         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14104         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14105     {
14106       DrawPlayer(player);       /* needed here only to cleanup last field */
14107       RemovePlayer(player);
14108
14109       if (local_player->friends_still_needed == 0 ||
14110           IS_SP_ELEMENT(Feld[jx][jy]))
14111         PlayerWins(player);
14112     }
14113
14114     /* this breaks one level: "machine", level 000 */
14115     {
14116       int move_direction = player->MovDir;
14117       int enter_side = MV_DIR_OPPOSITE(move_direction);
14118       int leave_side = move_direction;
14119       int old_jx = last_jx;
14120       int old_jy = last_jy;
14121       int old_element = Feld[old_jx][old_jy];
14122       int new_element = Feld[jx][jy];
14123
14124       if (IS_CUSTOM_ELEMENT(old_element))
14125         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14126                                    CE_LEFT_BY_PLAYER,
14127                                    player->index_bit, leave_side);
14128
14129       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14130                                           CE_PLAYER_LEAVES_X,
14131                                           player->index_bit, leave_side);
14132
14133       if (IS_CUSTOM_ELEMENT(new_element))
14134         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14135                                    player->index_bit, enter_side);
14136
14137       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14138                                           CE_PLAYER_ENTERS_X,
14139                                           player->index_bit, enter_side);
14140
14141 #if USE_FIX_CE_ACTION_WITH_PLAYER
14142       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14143                                         CE_MOVE_OF_X, move_direction);
14144 #else
14145       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14146                                         CE_MOVE_OF_X, move_direction);
14147 #endif
14148     }
14149
14150     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14151     {
14152       TestIfPlayerTouchesBadThing(jx, jy);
14153       TestIfPlayerTouchesCustomElement(jx, jy);
14154
14155       /* needed because pushed element has not yet reached its destination,
14156          so it would trigger a change event at its previous field location */
14157       if (!player->is_pushing)
14158         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14159
14160       if (!player->active)
14161         RemovePlayer(player);
14162     }
14163
14164     if (!local_player->LevelSolved && level.use_step_counter)
14165     {
14166       int i;
14167
14168       TimePlayed++;
14169
14170       if (TimeLeft > 0)
14171       {
14172         TimeLeft--;
14173
14174         if (TimeLeft <= 10 && setup.time_limit)
14175           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14176
14177 #if 1
14178         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14179
14180         DisplayGameControlValues();
14181 #else
14182         DrawGameValue_Time(TimeLeft);
14183 #endif
14184
14185         if (!TimeLeft && setup.time_limit)
14186           for (i = 0; i < MAX_PLAYERS; i++)
14187             KillPlayer(&stored_player[i]);
14188       }
14189 #if 1
14190       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14191       {
14192         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14193
14194         DisplayGameControlValues();
14195       }
14196 #else
14197       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14198         DrawGameValue_Time(TimePlayed);
14199 #endif
14200     }
14201
14202     if (tape.single_step && tape.recording && !tape.pausing &&
14203         !player->programmed_action)
14204       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14205   }
14206 }
14207
14208 void ScrollScreen(struct PlayerInfo *player, int mode)
14209 {
14210   static unsigned int screen_frame_counter = 0;
14211
14212   if (mode == SCROLL_INIT)
14213   {
14214     /* set scrolling step size according to actual player's moving speed */
14215     ScrollStepSize = TILEX / player->move_delay_value;
14216
14217     screen_frame_counter = FrameCounter;
14218     ScreenMovDir = player->MovDir;
14219     ScreenMovPos = player->MovPos;
14220     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14221     return;
14222   }
14223   else if (!FrameReached(&screen_frame_counter, 1))
14224     return;
14225
14226   if (ScreenMovPos)
14227   {
14228     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14229     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14230     redraw_mask |= REDRAW_FIELD;
14231   }
14232   else
14233     ScreenMovDir = MV_NONE;
14234 }
14235
14236 void TestIfPlayerTouchesCustomElement(int x, int y)
14237 {
14238   static int xy[4][2] =
14239   {
14240     { 0, -1 },
14241     { -1, 0 },
14242     { +1, 0 },
14243     { 0, +1 }
14244   };
14245   static int trigger_sides[4][2] =
14246   {
14247     /* center side       border side */
14248     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14249     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14250     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14251     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14252   };
14253   static int touch_dir[4] =
14254   {
14255     MV_LEFT | MV_RIGHT,
14256     MV_UP   | MV_DOWN,
14257     MV_UP   | MV_DOWN,
14258     MV_LEFT | MV_RIGHT
14259   };
14260   int center_element = Feld[x][y];      /* should always be non-moving! */
14261   int i;
14262
14263   for (i = 0; i < NUM_DIRECTIONS; i++)
14264   {
14265     int xx = x + xy[i][0];
14266     int yy = y + xy[i][1];
14267     int center_side = trigger_sides[i][0];
14268     int border_side = trigger_sides[i][1];
14269     int border_element;
14270
14271     if (!IN_LEV_FIELD(xx, yy))
14272       continue;
14273
14274     if (IS_PLAYER(x, y))                /* player found at center element */
14275     {
14276       struct PlayerInfo *player = PLAYERINFO(x, y);
14277
14278       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14279         border_element = Feld[xx][yy];          /* may be moving! */
14280       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14281         border_element = Feld[xx][yy];
14282       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14283         border_element = MovingOrBlocked2Element(xx, yy);
14284       else
14285         continue;               /* center and border element do not touch */
14286
14287       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14288                                  player->index_bit, border_side);
14289       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14290                                           CE_PLAYER_TOUCHES_X,
14291                                           player->index_bit, border_side);
14292
14293 #if USE_FIX_CE_ACTION_WITH_PLAYER
14294       {
14295         /* use player element that is initially defined in the level playfield,
14296            not the player element that corresponds to the runtime player number
14297            (example: a level that contains EL_PLAYER_3 as the only player would
14298            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14299         int player_element = PLAYERINFO(x, y)->initial_element;
14300
14301         CheckElementChangeBySide(xx, yy, border_element, player_element,
14302                                  CE_TOUCHING_X, border_side);
14303       }
14304 #endif
14305     }
14306     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14307     {
14308       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14309
14310       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14311       {
14312         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14313           continue;             /* center and border element do not touch */
14314       }
14315
14316       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14317                                  player->index_bit, center_side);
14318       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14319                                           CE_PLAYER_TOUCHES_X,
14320                                           player->index_bit, center_side);
14321
14322 #if USE_FIX_CE_ACTION_WITH_PLAYER
14323       {
14324         /* use player element that is initially defined in the level playfield,
14325            not the player element that corresponds to the runtime player number
14326            (example: a level that contains EL_PLAYER_3 as the only player would
14327            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14328         int player_element = PLAYERINFO(xx, yy)->initial_element;
14329
14330         CheckElementChangeBySide(x, y, center_element, player_element,
14331                                  CE_TOUCHING_X, center_side);
14332       }
14333 #endif
14334
14335       break;
14336     }
14337   }
14338 }
14339
14340 #if USE_ELEMENT_TOUCHING_BUGFIX
14341
14342 void TestIfElementTouchesCustomElement(int x, int y)
14343 {
14344   static int xy[4][2] =
14345   {
14346     { 0, -1 },
14347     { -1, 0 },
14348     { +1, 0 },
14349     { 0, +1 }
14350   };
14351   static int trigger_sides[4][2] =
14352   {
14353     /* center side      border side */
14354     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14355     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14356     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14357     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14358   };
14359   static int touch_dir[4] =
14360   {
14361     MV_LEFT | MV_RIGHT,
14362     MV_UP   | MV_DOWN,
14363     MV_UP   | MV_DOWN,
14364     MV_LEFT | MV_RIGHT
14365   };
14366   boolean change_center_element = FALSE;
14367   int center_element = Feld[x][y];      /* should always be non-moving! */
14368   int border_element_old[NUM_DIRECTIONS];
14369   int i;
14370
14371   for (i = 0; i < NUM_DIRECTIONS; i++)
14372   {
14373     int xx = x + xy[i][0];
14374     int yy = y + xy[i][1];
14375     int border_element;
14376
14377     border_element_old[i] = -1;
14378
14379     if (!IN_LEV_FIELD(xx, yy))
14380       continue;
14381
14382     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14383       border_element = Feld[xx][yy];    /* may be moving! */
14384     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14385       border_element = Feld[xx][yy];
14386     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14387       border_element = MovingOrBlocked2Element(xx, yy);
14388     else
14389       continue;                 /* center and border element do not touch */
14390
14391     border_element_old[i] = border_element;
14392   }
14393
14394   for (i = 0; i < NUM_DIRECTIONS; i++)
14395   {
14396     int xx = x + xy[i][0];
14397     int yy = y + xy[i][1];
14398     int center_side = trigger_sides[i][0];
14399     int border_element = border_element_old[i];
14400
14401     if (border_element == -1)
14402       continue;
14403
14404     /* check for change of border element */
14405     CheckElementChangeBySide(xx, yy, border_element, center_element,
14406                              CE_TOUCHING_X, center_side);
14407
14408     /* (center element cannot be player, so we dont have to check this here) */
14409   }
14410
14411   for (i = 0; i < NUM_DIRECTIONS; i++)
14412   {
14413     int xx = x + xy[i][0];
14414     int yy = y + xy[i][1];
14415     int border_side = trigger_sides[i][1];
14416     int border_element = border_element_old[i];
14417
14418     if (border_element == -1)
14419       continue;
14420
14421     /* check for change of center element (but change it only once) */
14422     if (!change_center_element)
14423       change_center_element =
14424         CheckElementChangeBySide(x, y, center_element, border_element,
14425                                  CE_TOUCHING_X, border_side);
14426
14427 #if USE_FIX_CE_ACTION_WITH_PLAYER
14428     if (IS_PLAYER(xx, yy))
14429     {
14430       /* use player element that is initially defined in the level playfield,
14431          not the player element that corresponds to the runtime player number
14432          (example: a level that contains EL_PLAYER_3 as the only player would
14433          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14434       int player_element = PLAYERINFO(xx, yy)->initial_element;
14435
14436       CheckElementChangeBySide(x, y, center_element, player_element,
14437                                CE_TOUCHING_X, border_side);
14438     }
14439 #endif
14440   }
14441 }
14442
14443 #else
14444
14445 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14446 {
14447   static int xy[4][2] =
14448   {
14449     { 0, -1 },
14450     { -1, 0 },
14451     { +1, 0 },
14452     { 0, +1 }
14453   };
14454   static int trigger_sides[4][2] =
14455   {
14456     /* center side      border side */
14457     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14458     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14459     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14460     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14461   };
14462   static int touch_dir[4] =
14463   {
14464     MV_LEFT | MV_RIGHT,
14465     MV_UP   | MV_DOWN,
14466     MV_UP   | MV_DOWN,
14467     MV_LEFT | MV_RIGHT
14468   };
14469   boolean change_center_element = FALSE;
14470   int center_element = Feld[x][y];      /* should always be non-moving! */
14471   int i;
14472
14473   for (i = 0; i < NUM_DIRECTIONS; i++)
14474   {
14475     int xx = x + xy[i][0];
14476     int yy = y + xy[i][1];
14477     int center_side = trigger_sides[i][0];
14478     int border_side = trigger_sides[i][1];
14479     int border_element;
14480
14481     if (!IN_LEV_FIELD(xx, yy))
14482       continue;
14483
14484     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14485       border_element = Feld[xx][yy];    /* may be moving! */
14486     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14487       border_element = Feld[xx][yy];
14488     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14489       border_element = MovingOrBlocked2Element(xx, yy);
14490     else
14491       continue;                 /* center and border element do not touch */
14492
14493     /* check for change of center element (but change it only once) */
14494     if (!change_center_element)
14495       change_center_element =
14496         CheckElementChangeBySide(x, y, center_element, border_element,
14497                                  CE_TOUCHING_X, border_side);
14498
14499     /* check for change of border element */
14500     CheckElementChangeBySide(xx, yy, border_element, center_element,
14501                              CE_TOUCHING_X, center_side);
14502   }
14503 }
14504
14505 #endif
14506
14507 void TestIfElementHitsCustomElement(int x, int y, int direction)
14508 {
14509   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14510   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14511   int hitx = x + dx, hity = y + dy;
14512   int hitting_element = Feld[x][y];
14513   int touched_element;
14514
14515   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14516     return;
14517
14518   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14519                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14520
14521   if (IN_LEV_FIELD(hitx, hity))
14522   {
14523     int opposite_direction = MV_DIR_OPPOSITE(direction);
14524     int hitting_side = direction;
14525     int touched_side = opposite_direction;
14526     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14527                           MovDir[hitx][hity] != direction ||
14528                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14529
14530     object_hit = TRUE;
14531
14532     if (object_hit)
14533     {
14534       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14535                                CE_HITTING_X, touched_side);
14536
14537       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14538                                CE_HIT_BY_X, hitting_side);
14539
14540       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14541                                CE_HIT_BY_SOMETHING, opposite_direction);
14542
14543 #if USE_FIX_CE_ACTION_WITH_PLAYER
14544       if (IS_PLAYER(hitx, hity))
14545       {
14546         /* use player element that is initially defined in the level playfield,
14547            not the player element that corresponds to the runtime player number
14548            (example: a level that contains EL_PLAYER_3 as the only player would
14549            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14550         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14551
14552         CheckElementChangeBySide(x, y, hitting_element, player_element,
14553                                  CE_HITTING_X, touched_side);
14554       }
14555 #endif
14556     }
14557   }
14558
14559   /* "hitting something" is also true when hitting the playfield border */
14560   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14561                            CE_HITTING_SOMETHING, direction);
14562 }
14563
14564 #if 0
14565 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14566 {
14567   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14568   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14569   int hitx = x + dx, hity = y + dy;
14570   int hitting_element = Feld[x][y];
14571   int touched_element;
14572 #if 0
14573   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14574                         !IS_FREE(hitx, hity) &&
14575                         (!IS_MOVING(hitx, hity) ||
14576                          MovDir[hitx][hity] != direction ||
14577                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14578 #endif
14579
14580   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14581     return;
14582
14583 #if 0
14584   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14585     return;
14586 #endif
14587
14588   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14589                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14590
14591   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14592                            EP_CAN_SMASH_EVERYTHING, direction);
14593
14594   if (IN_LEV_FIELD(hitx, hity))
14595   {
14596     int opposite_direction = MV_DIR_OPPOSITE(direction);
14597     int hitting_side = direction;
14598     int touched_side = opposite_direction;
14599 #if 0
14600     int touched_element = MovingOrBlocked2Element(hitx, hity);
14601 #endif
14602 #if 1
14603     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14604                           MovDir[hitx][hity] != direction ||
14605                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14606
14607     object_hit = TRUE;
14608 #endif
14609
14610     if (object_hit)
14611     {
14612       int i;
14613
14614       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14615                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14616
14617       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14618                                CE_OTHER_IS_SMASHING, touched_side);
14619
14620       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14621                                CE_OTHER_GETS_SMASHED, hitting_side);
14622     }
14623   }
14624 }
14625 #endif
14626
14627 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14628 {
14629   int i, kill_x = -1, kill_y = -1;
14630
14631   int bad_element = -1;
14632   static int test_xy[4][2] =
14633   {
14634     { 0, -1 },
14635     { -1, 0 },
14636     { +1, 0 },
14637     { 0, +1 }
14638   };
14639   static int test_dir[4] =
14640   {
14641     MV_UP,
14642     MV_LEFT,
14643     MV_RIGHT,
14644     MV_DOWN
14645   };
14646
14647   for (i = 0; i < NUM_DIRECTIONS; i++)
14648   {
14649     int test_x, test_y, test_move_dir, test_element;
14650
14651     test_x = good_x + test_xy[i][0];
14652     test_y = good_y + test_xy[i][1];
14653
14654     if (!IN_LEV_FIELD(test_x, test_y))
14655       continue;
14656
14657     test_move_dir =
14658       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14659
14660     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14661
14662     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14663        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14664     */
14665     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14666         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14667     {
14668       kill_x = test_x;
14669       kill_y = test_y;
14670       bad_element = test_element;
14671
14672       break;
14673     }
14674   }
14675
14676   if (kill_x != -1 || kill_y != -1)
14677   {
14678     if (IS_PLAYER(good_x, good_y))
14679     {
14680       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14681
14682       if (player->shield_deadly_time_left > 0 &&
14683           !IS_INDESTRUCTIBLE(bad_element))
14684         Bang(kill_x, kill_y);
14685       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14686         KillPlayer(player);
14687     }
14688     else
14689       Bang(good_x, good_y);
14690   }
14691 }
14692
14693 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14694 {
14695   int i, kill_x = -1, kill_y = -1;
14696   int bad_element = Feld[bad_x][bad_y];
14697   static int test_xy[4][2] =
14698   {
14699     { 0, -1 },
14700     { -1, 0 },
14701     { +1, 0 },
14702     { 0, +1 }
14703   };
14704   static int touch_dir[4] =
14705   {
14706     MV_LEFT | MV_RIGHT,
14707     MV_UP   | MV_DOWN,
14708     MV_UP   | MV_DOWN,
14709     MV_LEFT | MV_RIGHT
14710   };
14711   static int test_dir[4] =
14712   {
14713     MV_UP,
14714     MV_LEFT,
14715     MV_RIGHT,
14716     MV_DOWN
14717   };
14718
14719   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14720     return;
14721
14722   for (i = 0; i < NUM_DIRECTIONS; i++)
14723   {
14724     int test_x, test_y, test_move_dir, test_element;
14725
14726     test_x = bad_x + test_xy[i][0];
14727     test_y = bad_y + test_xy[i][1];
14728
14729     if (!IN_LEV_FIELD(test_x, test_y))
14730       continue;
14731
14732     test_move_dir =
14733       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14734
14735     test_element = Feld[test_x][test_y];
14736
14737     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14738        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14739     */
14740     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14741         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14742     {
14743       /* good thing is player or penguin that does not move away */
14744       if (IS_PLAYER(test_x, test_y))
14745       {
14746         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14747
14748         if (bad_element == EL_ROBOT && player->is_moving)
14749           continue;     /* robot does not kill player if he is moving */
14750
14751         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14752         {
14753           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14754             continue;           /* center and border element do not touch */
14755         }
14756
14757         kill_x = test_x;
14758         kill_y = test_y;
14759
14760         break;
14761       }
14762       else if (test_element == EL_PENGUIN)
14763       {
14764         kill_x = test_x;
14765         kill_y = test_y;
14766
14767         break;
14768       }
14769     }
14770   }
14771
14772   if (kill_x != -1 || kill_y != -1)
14773   {
14774     if (IS_PLAYER(kill_x, kill_y))
14775     {
14776       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14777
14778       if (player->shield_deadly_time_left > 0 &&
14779           !IS_INDESTRUCTIBLE(bad_element))
14780         Bang(bad_x, bad_y);
14781       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14782         KillPlayer(player);
14783     }
14784     else
14785       Bang(kill_x, kill_y);
14786   }
14787 }
14788
14789 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14790 {
14791   int bad_element = Feld[bad_x][bad_y];
14792   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14793   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14794   int test_x = bad_x + dx, test_y = bad_y + dy;
14795   int test_move_dir, test_element;
14796   int kill_x = -1, kill_y = -1;
14797
14798   if (!IN_LEV_FIELD(test_x, test_y))
14799     return;
14800
14801   test_move_dir =
14802     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14803
14804   test_element = Feld[test_x][test_y];
14805
14806   if (test_move_dir != bad_move_dir)
14807   {
14808     /* good thing can be player or penguin that does not move away */
14809     if (IS_PLAYER(test_x, test_y))
14810     {
14811       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14812
14813       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14814          player as being hit when he is moving towards the bad thing, because
14815          the "get hit by" condition would be lost after the player stops) */
14816       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14817         return;         /* player moves away from bad thing */
14818
14819       kill_x = test_x;
14820       kill_y = test_y;
14821     }
14822     else if (test_element == EL_PENGUIN)
14823     {
14824       kill_x = test_x;
14825       kill_y = test_y;
14826     }
14827   }
14828
14829   if (kill_x != -1 || kill_y != -1)
14830   {
14831     if (IS_PLAYER(kill_x, kill_y))
14832     {
14833       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14834
14835       if (player->shield_deadly_time_left > 0 &&
14836           !IS_INDESTRUCTIBLE(bad_element))
14837         Bang(bad_x, bad_y);
14838       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14839         KillPlayer(player);
14840     }
14841     else
14842       Bang(kill_x, kill_y);
14843   }
14844 }
14845
14846 void TestIfPlayerTouchesBadThing(int x, int y)
14847 {
14848   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14849 }
14850
14851 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14852 {
14853   TestIfGoodThingHitsBadThing(x, y, move_dir);
14854 }
14855
14856 void TestIfBadThingTouchesPlayer(int x, int y)
14857 {
14858   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14859 }
14860
14861 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14862 {
14863   TestIfBadThingHitsGoodThing(x, y, move_dir);
14864 }
14865
14866 void TestIfFriendTouchesBadThing(int x, int y)
14867 {
14868   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14869 }
14870
14871 void TestIfBadThingTouchesFriend(int x, int y)
14872 {
14873   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14874 }
14875
14876 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14877 {
14878   int i, kill_x = bad_x, kill_y = bad_y;
14879   static int xy[4][2] =
14880   {
14881     { 0, -1 },
14882     { -1, 0 },
14883     { +1, 0 },
14884     { 0, +1 }
14885   };
14886
14887   for (i = 0; i < NUM_DIRECTIONS; i++)
14888   {
14889     int x, y, element;
14890
14891     x = bad_x + xy[i][0];
14892     y = bad_y + xy[i][1];
14893     if (!IN_LEV_FIELD(x, y))
14894       continue;
14895
14896     element = Feld[x][y];
14897     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14898         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14899     {
14900       kill_x = x;
14901       kill_y = y;
14902       break;
14903     }
14904   }
14905
14906   if (kill_x != bad_x || kill_y != bad_y)
14907     Bang(bad_x, bad_y);
14908 }
14909
14910 void KillPlayer(struct PlayerInfo *player)
14911 {
14912   int jx = player->jx, jy = player->jy;
14913
14914   if (!player->active)
14915     return;
14916
14917 #if 0
14918   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14919          player->killed, player->active, player->reanimated);
14920 #endif
14921
14922   /* the following code was introduced to prevent an infinite loop when calling
14923      -> Bang()
14924      -> CheckTriggeredElementChangeExt()
14925      -> ExecuteCustomElementAction()
14926      -> KillPlayer()
14927      -> (infinitely repeating the above sequence of function calls)
14928      which occurs when killing the player while having a CE with the setting
14929      "kill player X when explosion of <player X>"; the solution using a new
14930      field "player->killed" was chosen for backwards compatibility, although
14931      clever use of the fields "player->active" etc. would probably also work */
14932 #if 1
14933   if (player->killed)
14934     return;
14935 #endif
14936
14937   player->killed = TRUE;
14938
14939   /* remove accessible field at the player's position */
14940   Feld[jx][jy] = EL_EMPTY;
14941
14942   /* deactivate shield (else Bang()/Explode() would not work right) */
14943   player->shield_normal_time_left = 0;
14944   player->shield_deadly_time_left = 0;
14945
14946 #if 0
14947   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14948          player->killed, player->active, player->reanimated);
14949 #endif
14950
14951   Bang(jx, jy);
14952
14953 #if 0
14954   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14955          player->killed, player->active, player->reanimated);
14956 #endif
14957
14958 #if USE_PLAYER_REANIMATION
14959 #if 1
14960   if (player->reanimated)       /* killed player may have been reanimated */
14961     player->killed = player->reanimated = FALSE;
14962   else
14963     BuryPlayer(player);
14964 #else
14965   if (player->killed)           /* player may have been reanimated */
14966     BuryPlayer(player);
14967 #endif
14968 #else
14969   BuryPlayer(player);
14970 #endif
14971 }
14972
14973 static void KillPlayerUnlessEnemyProtected(int x, int y)
14974 {
14975   if (!PLAYER_ENEMY_PROTECTED(x, y))
14976     KillPlayer(PLAYERINFO(x, y));
14977 }
14978
14979 static void KillPlayerUnlessExplosionProtected(int x, int y)
14980 {
14981   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14982     KillPlayer(PLAYERINFO(x, y));
14983 }
14984
14985 void BuryPlayer(struct PlayerInfo *player)
14986 {
14987   int jx = player->jx, jy = player->jy;
14988
14989   if (!player->active)
14990     return;
14991
14992   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14993   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14994
14995   player->GameOver = TRUE;
14996   RemovePlayer(player);
14997 }
14998
14999 void RemovePlayer(struct PlayerInfo *player)
15000 {
15001   int jx = player->jx, jy = player->jy;
15002   int i, found = FALSE;
15003
15004   player->present = FALSE;
15005   player->active = FALSE;
15006
15007   if (!ExplodeField[jx][jy])
15008     StorePlayer[jx][jy] = 0;
15009
15010   if (player->is_moving)
15011     TEST_DrawLevelField(player->last_jx, player->last_jy);
15012
15013   for (i = 0; i < MAX_PLAYERS; i++)
15014     if (stored_player[i].active)
15015       found = TRUE;
15016
15017   if (!found)
15018     AllPlayersGone = TRUE;
15019
15020   ExitX = ZX = jx;
15021   ExitY = ZY = jy;
15022 }
15023
15024 #if USE_NEW_SNAP_DELAY
15025 static void setFieldForSnapping(int x, int y, int element, int direction)
15026 {
15027   struct ElementInfo *ei = &element_info[element];
15028   int direction_bit = MV_DIR_TO_BIT(direction);
15029   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15030   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15031                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15032
15033   Feld[x][y] = EL_ELEMENT_SNAPPING;
15034   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15035
15036   ResetGfxAnimation(x, y);
15037
15038   GfxElement[x][y] = element;
15039   GfxAction[x][y] = action;
15040   GfxDir[x][y] = direction;
15041   GfxFrame[x][y] = -1;
15042 }
15043 #endif
15044
15045 /*
15046   =============================================================================
15047   checkDiagonalPushing()
15048   -----------------------------------------------------------------------------
15049   check if diagonal input device direction results in pushing of object
15050   (by checking if the alternative direction is walkable, diggable, ...)
15051   =============================================================================
15052 */
15053
15054 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15055                                     int x, int y, int real_dx, int real_dy)
15056 {
15057   int jx, jy, dx, dy, xx, yy;
15058
15059   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
15060     return TRUE;
15061
15062   /* diagonal direction: check alternative direction */
15063   jx = player->jx;
15064   jy = player->jy;
15065   dx = x - jx;
15066   dy = y - jy;
15067   xx = jx + (dx == 0 ? real_dx : 0);
15068   yy = jy + (dy == 0 ? real_dy : 0);
15069
15070   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15071 }
15072
15073 /*
15074   =============================================================================
15075   DigField()
15076   -----------------------------------------------------------------------------
15077   x, y:                 field next to player (non-diagonal) to try to dig to
15078   real_dx, real_dy:     direction as read from input device (can be diagonal)
15079   =============================================================================
15080 */
15081
15082 static int DigField(struct PlayerInfo *player,
15083                     int oldx, int oldy, int x, int y,
15084                     int real_dx, int real_dy, int mode)
15085 {
15086   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15087   boolean player_was_pushing = player->is_pushing;
15088   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15089   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15090   int jx = oldx, jy = oldy;
15091   int dx = x - jx, dy = y - jy;
15092   int nextx = x + dx, nexty = y + dy;
15093   int move_direction = (dx == -1 ? MV_LEFT  :
15094                         dx == +1 ? MV_RIGHT :
15095                         dy == -1 ? MV_UP    :
15096                         dy == +1 ? MV_DOWN  : MV_NONE);
15097   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15098   int dig_side = MV_DIR_OPPOSITE(move_direction);
15099   int old_element = Feld[jx][jy];
15100 #if USE_FIXED_DONT_RUN_INTO
15101   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15102 #else
15103   int element;
15104 #endif
15105   int collect_count;
15106
15107   if (is_player)                /* function can also be called by EL_PENGUIN */
15108   {
15109     if (player->MovPos == 0)
15110     {
15111       player->is_digging = FALSE;
15112       player->is_collecting = FALSE;
15113     }
15114
15115     if (player->MovPos == 0)    /* last pushing move finished */
15116       player->is_pushing = FALSE;
15117
15118     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15119     {
15120       player->is_switching = FALSE;
15121       player->push_delay = -1;
15122
15123       return MP_NO_ACTION;
15124     }
15125   }
15126
15127 #if !USE_FIXED_DONT_RUN_INTO
15128   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15129     return MP_NO_ACTION;
15130 #endif
15131
15132   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15133     old_element = Back[jx][jy];
15134
15135   /* in case of element dropped at player position, check background */
15136   else if (Back[jx][jy] != EL_EMPTY &&
15137            game.engine_version >= VERSION_IDENT(2,2,0,0))
15138     old_element = Back[jx][jy];
15139
15140   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15141     return MP_NO_ACTION;        /* field has no opening in this direction */
15142
15143   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15144     return MP_NO_ACTION;        /* field has no opening in this direction */
15145
15146 #if USE_FIXED_DONT_RUN_INTO
15147   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15148   {
15149     SplashAcid(x, y);
15150
15151     Feld[jx][jy] = player->artwork_element;
15152     InitMovingField(jx, jy, MV_DOWN);
15153     Store[jx][jy] = EL_ACID;
15154     ContinueMoving(jx, jy);
15155     BuryPlayer(player);
15156
15157     return MP_DONT_RUN_INTO;
15158   }
15159 #endif
15160
15161 #if USE_FIXED_DONT_RUN_INTO
15162   if (player_can_move && DONT_RUN_INTO(element))
15163   {
15164     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15165
15166     return MP_DONT_RUN_INTO;
15167   }
15168 #endif
15169
15170 #if USE_FIXED_DONT_RUN_INTO
15171   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15172     return MP_NO_ACTION;
15173 #endif
15174
15175 #if !USE_FIXED_DONT_RUN_INTO
15176   element = Feld[x][y];
15177 #endif
15178
15179   collect_count = element_info[element].collect_count_initial;
15180
15181   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15182     return MP_NO_ACTION;
15183
15184   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15185     player_can_move = player_can_move_or_snap;
15186
15187   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15188       game.engine_version >= VERSION_IDENT(2,2,0,0))
15189   {
15190     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15191                                player->index_bit, dig_side);
15192     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193                                         player->index_bit, dig_side);
15194
15195     if (element == EL_DC_LANDMINE)
15196       Bang(x, y);
15197
15198     if (Feld[x][y] != element)          /* field changed by snapping */
15199       return MP_ACTION;
15200
15201     return MP_NO_ACTION;
15202   }
15203
15204 #if USE_PLAYER_GRAVITY
15205   if (player->gravity && is_player && !player->is_auto_moving &&
15206       canFallDown(player) && move_direction != MV_DOWN &&
15207       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15208     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15209 #else
15210   if (game.gravity && is_player && !player->is_auto_moving &&
15211       canFallDown(player) && move_direction != MV_DOWN &&
15212       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15213     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15214 #endif
15215
15216   if (player_can_move &&
15217       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15218   {
15219     int sound_element = SND_ELEMENT(element);
15220     int sound_action = ACTION_WALKING;
15221
15222     if (IS_RND_GATE(element))
15223     {
15224       if (!player->key[RND_GATE_NR(element)])
15225         return MP_NO_ACTION;
15226     }
15227     else if (IS_RND_GATE_GRAY(element))
15228     {
15229       if (!player->key[RND_GATE_GRAY_NR(element)])
15230         return MP_NO_ACTION;
15231     }
15232     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15233     {
15234       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15235         return MP_NO_ACTION;
15236     }
15237     else if (element == EL_EXIT_OPEN ||
15238              element == EL_EM_EXIT_OPEN ||
15239 #if 1
15240              element == EL_EM_EXIT_OPENING ||
15241 #endif
15242              element == EL_STEEL_EXIT_OPEN ||
15243              element == EL_EM_STEEL_EXIT_OPEN ||
15244 #if 1
15245              element == EL_EM_STEEL_EXIT_OPENING ||
15246 #endif
15247              element == EL_SP_EXIT_OPEN ||
15248              element == EL_SP_EXIT_OPENING)
15249     {
15250       sound_action = ACTION_PASSING;    /* player is passing exit */
15251     }
15252     else if (element == EL_EMPTY)
15253     {
15254       sound_action = ACTION_MOVING;             /* nothing to walk on */
15255     }
15256
15257     /* play sound from background or player, whatever is available */
15258     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15259       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15260     else
15261       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15262   }
15263   else if (player_can_move &&
15264            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15265   {
15266     if (!ACCESS_FROM(element, opposite_direction))
15267       return MP_NO_ACTION;      /* field not accessible from this direction */
15268
15269     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15270       return MP_NO_ACTION;
15271
15272     if (IS_EM_GATE(element))
15273     {
15274       if (!player->key[EM_GATE_NR(element)])
15275         return MP_NO_ACTION;
15276     }
15277     else if (IS_EM_GATE_GRAY(element))
15278     {
15279       if (!player->key[EM_GATE_GRAY_NR(element)])
15280         return MP_NO_ACTION;
15281     }
15282     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15283     {
15284       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15285         return MP_NO_ACTION;
15286     }
15287     else if (IS_EMC_GATE(element))
15288     {
15289       if (!player->key[EMC_GATE_NR(element)])
15290         return MP_NO_ACTION;
15291     }
15292     else if (IS_EMC_GATE_GRAY(element))
15293     {
15294       if (!player->key[EMC_GATE_GRAY_NR(element)])
15295         return MP_NO_ACTION;
15296     }
15297     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15298     {
15299       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15300         return MP_NO_ACTION;
15301     }
15302     else if (element == EL_DC_GATE_WHITE ||
15303              element == EL_DC_GATE_WHITE_GRAY ||
15304              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15305     {
15306       if (player->num_white_keys == 0)
15307         return MP_NO_ACTION;
15308
15309       player->num_white_keys--;
15310     }
15311     else if (IS_SP_PORT(element))
15312     {
15313       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15314           element == EL_SP_GRAVITY_PORT_RIGHT ||
15315           element == EL_SP_GRAVITY_PORT_UP ||
15316           element == EL_SP_GRAVITY_PORT_DOWN)
15317 #if USE_PLAYER_GRAVITY
15318         player->gravity = !player->gravity;
15319 #else
15320         game.gravity = !game.gravity;
15321 #endif
15322       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15323                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15324                element == EL_SP_GRAVITY_ON_PORT_UP ||
15325                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15326 #if USE_PLAYER_GRAVITY
15327         player->gravity = TRUE;
15328 #else
15329         game.gravity = TRUE;
15330 #endif
15331       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15332                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15333                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15334                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15335 #if USE_PLAYER_GRAVITY
15336         player->gravity = FALSE;
15337 #else
15338         game.gravity = FALSE;
15339 #endif
15340     }
15341
15342     /* automatically move to the next field with double speed */
15343     player->programmed_action = move_direction;
15344
15345     if (player->move_delay_reset_counter == 0)
15346     {
15347       player->move_delay_reset_counter = 2;     /* two double speed steps */
15348
15349       DOUBLE_PLAYER_SPEED(player);
15350     }
15351
15352     PlayLevelSoundAction(x, y, ACTION_PASSING);
15353   }
15354   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15355   {
15356     RemoveField(x, y);
15357
15358     if (mode != DF_SNAP)
15359     {
15360       GfxElement[x][y] = GFX_ELEMENT(element);
15361       player->is_digging = TRUE;
15362     }
15363
15364     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15365
15366     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15367                                         player->index_bit, dig_side);
15368
15369     if (mode == DF_SNAP)
15370     {
15371 #if USE_NEW_SNAP_DELAY
15372       if (level.block_snap_field)
15373         setFieldForSnapping(x, y, element, move_direction);
15374       else
15375         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15376 #else
15377       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15378 #endif
15379
15380       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15381                                           player->index_bit, dig_side);
15382     }
15383   }
15384   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15385   {
15386     RemoveField(x, y);
15387
15388     if (is_player && mode != DF_SNAP)
15389     {
15390       GfxElement[x][y] = element;
15391       player->is_collecting = TRUE;
15392     }
15393
15394     if (element == EL_SPEED_PILL)
15395     {
15396       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15397     }
15398     else if (element == EL_EXTRA_TIME && level.time > 0)
15399     {
15400       TimeLeft += level.extra_time;
15401
15402 #if 1
15403       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15404
15405       DisplayGameControlValues();
15406 #else
15407       DrawGameValue_Time(TimeLeft);
15408 #endif
15409     }
15410     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15411     {
15412       player->shield_normal_time_left += level.shield_normal_time;
15413       if (element == EL_SHIELD_DEADLY)
15414         player->shield_deadly_time_left += level.shield_deadly_time;
15415     }
15416     else if (element == EL_DYNAMITE ||
15417              element == EL_EM_DYNAMITE ||
15418              element == EL_SP_DISK_RED)
15419     {
15420       if (player->inventory_size < MAX_INVENTORY_SIZE)
15421         player->inventory_element[player->inventory_size++] = element;
15422
15423       DrawGameDoorValues();
15424     }
15425     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15426     {
15427       player->dynabomb_count++;
15428       player->dynabombs_left++;
15429     }
15430     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15431     {
15432       player->dynabomb_size++;
15433     }
15434     else if (element == EL_DYNABOMB_INCREASE_POWER)
15435     {
15436       player->dynabomb_xl = TRUE;
15437     }
15438     else if (IS_KEY(element))
15439     {
15440       player->key[KEY_NR(element)] = TRUE;
15441
15442       DrawGameDoorValues();
15443     }
15444     else if (element == EL_DC_KEY_WHITE)
15445     {
15446       player->num_white_keys++;
15447
15448       /* display white keys? */
15449       /* DrawGameDoorValues(); */
15450     }
15451     else if (IS_ENVELOPE(element))
15452     {
15453       player->show_envelope = element;
15454     }
15455     else if (element == EL_EMC_LENSES)
15456     {
15457       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15458
15459       RedrawAllInvisibleElementsForLenses();
15460     }
15461     else if (element == EL_EMC_MAGNIFIER)
15462     {
15463       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15464
15465       RedrawAllInvisibleElementsForMagnifier();
15466     }
15467     else if (IS_DROPPABLE(element) ||
15468              IS_THROWABLE(element))     /* can be collected and dropped */
15469     {
15470       int i;
15471
15472       if (collect_count == 0)
15473         player->inventory_infinite_element = element;
15474       else
15475         for (i = 0; i < collect_count; i++)
15476           if (player->inventory_size < MAX_INVENTORY_SIZE)
15477             player->inventory_element[player->inventory_size++] = element;
15478
15479       DrawGameDoorValues();
15480     }
15481     else if (collect_count > 0)
15482     {
15483       local_player->gems_still_needed -= collect_count;
15484       if (local_player->gems_still_needed < 0)
15485         local_player->gems_still_needed = 0;
15486
15487 #if 1
15488       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15489
15490       DisplayGameControlValues();
15491 #else
15492       DrawGameValue_Emeralds(local_player->gems_still_needed);
15493 #endif
15494     }
15495
15496     RaiseScoreElement(element);
15497     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15498
15499     if (is_player)
15500       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15501                                           player->index_bit, dig_side);
15502
15503     if (mode == DF_SNAP)
15504     {
15505 #if USE_NEW_SNAP_DELAY
15506       if (level.block_snap_field)
15507         setFieldForSnapping(x, y, element, move_direction);
15508       else
15509         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15510 #else
15511       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15512 #endif
15513
15514       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15515                                           player->index_bit, dig_side);
15516     }
15517   }
15518   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15519   {
15520     if (mode == DF_SNAP && element != EL_BD_ROCK)
15521       return MP_NO_ACTION;
15522
15523     if (CAN_FALL(element) && dy)
15524       return MP_NO_ACTION;
15525
15526     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15527         !(element == EL_SPRING && level.use_spring_bug))
15528       return MP_NO_ACTION;
15529
15530     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15531         ((move_direction & MV_VERTICAL &&
15532           ((element_info[element].move_pattern & MV_LEFT &&
15533             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15534            (element_info[element].move_pattern & MV_RIGHT &&
15535             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15536          (move_direction & MV_HORIZONTAL &&
15537           ((element_info[element].move_pattern & MV_UP &&
15538             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15539            (element_info[element].move_pattern & MV_DOWN &&
15540             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15541       return MP_NO_ACTION;
15542
15543     /* do not push elements already moving away faster than player */
15544     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15545         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15546       return MP_NO_ACTION;
15547
15548     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15549     {
15550       if (player->push_delay_value == -1 || !player_was_pushing)
15551         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15552     }
15553     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15554     {
15555       if (player->push_delay_value == -1)
15556         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15557     }
15558     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15559     {
15560       if (!player->is_pushing)
15561         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15562     }
15563
15564     player->is_pushing = TRUE;
15565     player->is_active = TRUE;
15566
15567     if (!(IN_LEV_FIELD(nextx, nexty) &&
15568           (IS_FREE(nextx, nexty) ||
15569            (IS_SB_ELEMENT(element) &&
15570             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15571            (IS_CUSTOM_ELEMENT(element) &&
15572             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15573       return MP_NO_ACTION;
15574
15575     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15576       return MP_NO_ACTION;
15577
15578     if (player->push_delay == -1)       /* new pushing; restart delay */
15579       player->push_delay = 0;
15580
15581     if (player->push_delay < player->push_delay_value &&
15582         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15583         element != EL_SPRING && element != EL_BALLOON)
15584     {
15585       /* make sure that there is no move delay before next try to push */
15586       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15587         player->move_delay = 0;
15588
15589       return MP_NO_ACTION;
15590     }
15591
15592     if (IS_CUSTOM_ELEMENT(element) &&
15593         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15594     {
15595       if (!DigFieldByCE(nextx, nexty, element))
15596         return MP_NO_ACTION;
15597     }
15598
15599     if (IS_SB_ELEMENT(element))
15600     {
15601       if (element == EL_SOKOBAN_FIELD_FULL)
15602       {
15603         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15604         local_player->sokobanfields_still_needed++;
15605       }
15606
15607       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15608       {
15609         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15610         local_player->sokobanfields_still_needed--;
15611       }
15612
15613       Feld[x][y] = EL_SOKOBAN_OBJECT;
15614
15615       if (Back[x][y] == Back[nextx][nexty])
15616         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15617       else if (Back[x][y] != 0)
15618         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15619                                     ACTION_EMPTYING);
15620       else
15621         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15622                                     ACTION_FILLING);
15623
15624 #if 1
15625       if (local_player->sokobanfields_still_needed == 0 &&
15626           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15627 #else
15628       if (local_player->sokobanfields_still_needed == 0 &&
15629           game.emulation == EMU_SOKOBAN)
15630 #endif
15631       {
15632         PlayerWins(player);
15633
15634         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15635       }
15636     }
15637     else
15638       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15639
15640     InitMovingField(x, y, move_direction);
15641     GfxAction[x][y] = ACTION_PUSHING;
15642
15643     if (mode == DF_SNAP)
15644       ContinueMoving(x, y);
15645     else
15646       MovPos[x][y] = (dx != 0 ? dx : dy);
15647
15648     Pushed[x][y] = TRUE;
15649     Pushed[nextx][nexty] = TRUE;
15650
15651     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15652       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15653     else
15654       player->push_delay_value = -1;    /* get new value later */
15655
15656     /* check for element change _after_ element has been pushed */
15657     if (game.use_change_when_pushing_bug)
15658     {
15659       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15660                                  player->index_bit, dig_side);
15661       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15662                                           player->index_bit, dig_side);
15663     }
15664   }
15665   else if (IS_SWITCHABLE(element))
15666   {
15667     if (PLAYER_SWITCHING(player, x, y))
15668     {
15669       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15670                                           player->index_bit, dig_side);
15671
15672       return MP_ACTION;
15673     }
15674
15675     player->is_switching = TRUE;
15676     player->switch_x = x;
15677     player->switch_y = y;
15678
15679     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15680
15681     if (element == EL_ROBOT_WHEEL)
15682     {
15683       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15684       ZX = x;
15685       ZY = y;
15686
15687       game.robot_wheel_active = TRUE;
15688
15689       TEST_DrawLevelField(x, y);
15690     }
15691     else if (element == EL_SP_TERMINAL)
15692     {
15693       int xx, yy;
15694
15695       SCAN_PLAYFIELD(xx, yy)
15696       {
15697         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15698           Bang(xx, yy);
15699         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15700           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15701       }
15702     }
15703     else if (IS_BELT_SWITCH(element))
15704     {
15705       ToggleBeltSwitch(x, y);
15706     }
15707     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15708              element == EL_SWITCHGATE_SWITCH_DOWN ||
15709              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15710              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15711     {
15712       ToggleSwitchgateSwitch(x, y);
15713     }
15714     else if (element == EL_LIGHT_SWITCH ||
15715              element == EL_LIGHT_SWITCH_ACTIVE)
15716     {
15717       ToggleLightSwitch(x, y);
15718     }
15719     else if (element == EL_TIMEGATE_SWITCH ||
15720              element == EL_DC_TIMEGATE_SWITCH)
15721     {
15722       ActivateTimegateSwitch(x, y);
15723     }
15724     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15725              element == EL_BALLOON_SWITCH_RIGHT ||
15726              element == EL_BALLOON_SWITCH_UP    ||
15727              element == EL_BALLOON_SWITCH_DOWN  ||
15728              element == EL_BALLOON_SWITCH_NONE  ||
15729              element == EL_BALLOON_SWITCH_ANY)
15730     {
15731       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15732                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15733                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15734                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15735                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15736                              move_direction);
15737     }
15738     else if (element == EL_LAMP)
15739     {
15740       Feld[x][y] = EL_LAMP_ACTIVE;
15741       local_player->lights_still_needed--;
15742
15743       ResetGfxAnimation(x, y);
15744       TEST_DrawLevelField(x, y);
15745     }
15746     else if (element == EL_TIME_ORB_FULL)
15747     {
15748       Feld[x][y] = EL_TIME_ORB_EMPTY;
15749
15750       if (level.time > 0 || level.use_time_orb_bug)
15751       {
15752         TimeLeft += level.time_orb_time;
15753         game.no_time_limit = FALSE;
15754
15755 #if 1
15756         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15757
15758         DisplayGameControlValues();
15759 #else
15760         DrawGameValue_Time(TimeLeft);
15761 #endif
15762       }
15763
15764       ResetGfxAnimation(x, y);
15765       TEST_DrawLevelField(x, y);
15766     }
15767     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15768              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15769     {
15770       int xx, yy;
15771
15772       game.ball_state = !game.ball_state;
15773
15774       SCAN_PLAYFIELD(xx, yy)
15775       {
15776         int e = Feld[xx][yy];
15777
15778         if (game.ball_state)
15779         {
15780           if (e == EL_EMC_MAGIC_BALL)
15781             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15782           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15783             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15784         }
15785         else
15786         {
15787           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15788             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15789           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15790             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15791         }
15792       }
15793     }
15794
15795     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15796                                         player->index_bit, dig_side);
15797
15798     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15799                                         player->index_bit, dig_side);
15800
15801     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15802                                         player->index_bit, dig_side);
15803
15804     return MP_ACTION;
15805   }
15806   else
15807   {
15808     if (!PLAYER_SWITCHING(player, x, y))
15809     {
15810       player->is_switching = TRUE;
15811       player->switch_x = x;
15812       player->switch_y = y;
15813
15814       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15815                                  player->index_bit, dig_side);
15816       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15817                                           player->index_bit, dig_side);
15818
15819       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15820                                  player->index_bit, dig_side);
15821       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15822                                           player->index_bit, dig_side);
15823     }
15824
15825     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15826                                player->index_bit, dig_side);
15827     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15828                                         player->index_bit, dig_side);
15829
15830     return MP_NO_ACTION;
15831   }
15832
15833   player->push_delay = -1;
15834
15835   if (is_player)                /* function can also be called by EL_PENGUIN */
15836   {
15837     if (Feld[x][y] != element)          /* really digged/collected something */
15838     {
15839       player->is_collecting = !player->is_digging;
15840       player->is_active = TRUE;
15841     }
15842   }
15843
15844   return MP_MOVING;
15845 }
15846
15847 static boolean DigFieldByCE(int x, int y, int digging_element)
15848 {
15849   int element = Feld[x][y];
15850
15851   if (!IS_FREE(x, y))
15852   {
15853     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15854                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15855                   ACTION_BREAKING);
15856
15857     /* no element can dig solid indestructible elements */
15858     if (IS_INDESTRUCTIBLE(element) &&
15859         !IS_DIGGABLE(element) &&
15860         !IS_COLLECTIBLE(element))
15861       return FALSE;
15862
15863     if (AmoebaNr[x][y] &&
15864         (element == EL_AMOEBA_FULL ||
15865          element == EL_BD_AMOEBA ||
15866          element == EL_AMOEBA_GROWING))
15867     {
15868       AmoebaCnt[AmoebaNr[x][y]]--;
15869       AmoebaCnt2[AmoebaNr[x][y]]--;
15870     }
15871
15872     if (IS_MOVING(x, y))
15873       RemoveMovingField(x, y);
15874     else
15875     {
15876       RemoveField(x, y);
15877       TEST_DrawLevelField(x, y);
15878     }
15879
15880     /* if digged element was about to explode, prevent the explosion */
15881     ExplodeField[x][y] = EX_TYPE_NONE;
15882
15883     PlayLevelSoundAction(x, y, action);
15884   }
15885
15886   Store[x][y] = EL_EMPTY;
15887
15888 #if 1
15889   /* this makes it possible to leave the removed element again */
15890   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15891     Store[x][y] = element;
15892 #else
15893   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15894   {
15895     int move_leave_element = element_info[digging_element].move_leave_element;
15896
15897     /* this makes it possible to leave the removed element again */
15898     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15899                    element : move_leave_element);
15900   }
15901 #endif
15902
15903   return TRUE;
15904 }
15905
15906 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15907 {
15908   int jx = player->jx, jy = player->jy;
15909   int x = jx + dx, y = jy + dy;
15910   int snap_direction = (dx == -1 ? MV_LEFT  :
15911                         dx == +1 ? MV_RIGHT :
15912                         dy == -1 ? MV_UP    :
15913                         dy == +1 ? MV_DOWN  : MV_NONE);
15914   boolean can_continue_snapping = (level.continuous_snapping &&
15915                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15916
15917   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15918     return FALSE;
15919
15920   if (!player->active || !IN_LEV_FIELD(x, y))
15921     return FALSE;
15922
15923   if (dx && dy)
15924     return FALSE;
15925
15926   if (!dx && !dy)
15927   {
15928     if (player->MovPos == 0)
15929       player->is_pushing = FALSE;
15930
15931     player->is_snapping = FALSE;
15932
15933     if (player->MovPos == 0)
15934     {
15935       player->is_moving = FALSE;
15936       player->is_digging = FALSE;
15937       player->is_collecting = FALSE;
15938     }
15939
15940     return FALSE;
15941   }
15942
15943 #if USE_NEW_CONTINUOUS_SNAPPING
15944   /* prevent snapping with already pressed snap key when not allowed */
15945   if (player->is_snapping && !can_continue_snapping)
15946     return FALSE;
15947 #else
15948   if (player->is_snapping)
15949     return FALSE;
15950 #endif
15951
15952   player->MovDir = snap_direction;
15953
15954   if (player->MovPos == 0)
15955   {
15956     player->is_moving = FALSE;
15957     player->is_digging = FALSE;
15958     player->is_collecting = FALSE;
15959   }
15960
15961   player->is_dropping = FALSE;
15962   player->is_dropping_pressed = FALSE;
15963   player->drop_pressed_delay = 0;
15964
15965   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15966     return FALSE;
15967
15968   player->is_snapping = TRUE;
15969   player->is_active = TRUE;
15970
15971   if (player->MovPos == 0)
15972   {
15973     player->is_moving = FALSE;
15974     player->is_digging = FALSE;
15975     player->is_collecting = FALSE;
15976   }
15977
15978   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15979     TEST_DrawLevelField(player->last_jx, player->last_jy);
15980
15981   TEST_DrawLevelField(x, y);
15982
15983   return TRUE;
15984 }
15985
15986 static boolean DropElement(struct PlayerInfo *player)
15987 {
15988   int old_element, new_element;
15989   int dropx = player->jx, dropy = player->jy;
15990   int drop_direction = player->MovDir;
15991   int drop_side = drop_direction;
15992 #if 1
15993   int drop_element = get_next_dropped_element(player);
15994 #else
15995   int drop_element = (player->inventory_size > 0 ?
15996                       player->inventory_element[player->inventory_size - 1] :
15997                       player->inventory_infinite_element != EL_UNDEFINED ?
15998                       player->inventory_infinite_element :
15999                       player->dynabombs_left > 0 ?
16000                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
16001                       EL_UNDEFINED);
16002 #endif
16003
16004   player->is_dropping_pressed = TRUE;
16005
16006   /* do not drop an element on top of another element; when holding drop key
16007      pressed without moving, dropped element must move away before the next
16008      element can be dropped (this is especially important if the next element
16009      is dynamite, which can be placed on background for historical reasons) */
16010   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
16011     return MP_ACTION;
16012
16013   if (IS_THROWABLE(drop_element))
16014   {
16015     dropx += GET_DX_FROM_DIR(drop_direction);
16016     dropy += GET_DY_FROM_DIR(drop_direction);
16017
16018     if (!IN_LEV_FIELD(dropx, dropy))
16019       return FALSE;
16020   }
16021
16022   old_element = Feld[dropx][dropy];     /* old element at dropping position */
16023   new_element = drop_element;           /* default: no change when dropping */
16024
16025   /* check if player is active, not moving and ready to drop */
16026   if (!player->active || player->MovPos || player->drop_delay > 0)
16027     return FALSE;
16028
16029   /* check if player has anything that can be dropped */
16030   if (new_element == EL_UNDEFINED)
16031     return FALSE;
16032
16033   /* check if drop key was pressed long enough for EM style dynamite */
16034   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16035     return FALSE;
16036
16037   /* check if anything can be dropped at the current position */
16038   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16039     return FALSE;
16040
16041   /* collected custom elements can only be dropped on empty fields */
16042   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16043     return FALSE;
16044
16045   if (old_element != EL_EMPTY)
16046     Back[dropx][dropy] = old_element;   /* store old element on this field */
16047
16048   ResetGfxAnimation(dropx, dropy);
16049   ResetRandomAnimationValue(dropx, dropy);
16050
16051   if (player->inventory_size > 0 ||
16052       player->inventory_infinite_element != EL_UNDEFINED)
16053   {
16054     if (player->inventory_size > 0)
16055     {
16056       player->inventory_size--;
16057
16058       DrawGameDoorValues();
16059
16060       if (new_element == EL_DYNAMITE)
16061         new_element = EL_DYNAMITE_ACTIVE;
16062       else if (new_element == EL_EM_DYNAMITE)
16063         new_element = EL_EM_DYNAMITE_ACTIVE;
16064       else if (new_element == EL_SP_DISK_RED)
16065         new_element = EL_SP_DISK_RED_ACTIVE;
16066     }
16067
16068     Feld[dropx][dropy] = new_element;
16069
16070     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16071       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16072                           el2img(Feld[dropx][dropy]), 0);
16073
16074     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16075
16076     /* needed if previous element just changed to "empty" in the last frame */
16077     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16078
16079     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16080                                player->index_bit, drop_side);
16081     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16082                                         CE_PLAYER_DROPS_X,
16083                                         player->index_bit, drop_side);
16084
16085     TestIfElementTouchesCustomElement(dropx, dropy);
16086   }
16087   else          /* player is dropping a dyna bomb */
16088   {
16089     player->dynabombs_left--;
16090
16091     Feld[dropx][dropy] = new_element;
16092
16093     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16094       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16095                           el2img(Feld[dropx][dropy]), 0);
16096
16097     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16098   }
16099
16100   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16101     InitField_WithBug1(dropx, dropy, FALSE);
16102
16103   new_element = Feld[dropx][dropy];     /* element might have changed */
16104
16105   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16106       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16107   {
16108 #if 0
16109     int move_direction;
16110     int nextx, nexty;
16111 #endif
16112
16113     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16114       MovDir[dropx][dropy] = drop_direction;
16115
16116 #if 0
16117     move_direction = MovDir[dropx][dropy];
16118     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16119     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16120 #endif
16121
16122     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16123
16124 #if USE_FIX_IMPACT_COLLISION
16125     /* do not cause impact style collision by dropping elements that can fall */
16126     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16127 #else
16128     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16129 #endif
16130   }
16131
16132   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16133   player->is_dropping = TRUE;
16134
16135   player->drop_pressed_delay = 0;
16136   player->is_dropping_pressed = FALSE;
16137
16138   player->drop_x = dropx;
16139   player->drop_y = dropy;
16140
16141   return TRUE;
16142 }
16143
16144 /* ------------------------------------------------------------------------- */
16145 /* game sound playing functions                                              */
16146 /* ------------------------------------------------------------------------- */
16147
16148 static int *loop_sound_frame = NULL;
16149 static int *loop_sound_volume = NULL;
16150
16151 void InitPlayLevelSound()
16152 {
16153   int num_sounds = getSoundListSize();
16154
16155   checked_free(loop_sound_frame);
16156   checked_free(loop_sound_volume);
16157
16158   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16159   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16160 }
16161
16162 static void PlayLevelSound(int x, int y, int nr)
16163 {
16164   int sx = SCREENX(x), sy = SCREENY(y);
16165   int volume, stereo_position;
16166   int max_distance = 8;
16167   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16168
16169   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16170       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16171     return;
16172
16173   if (!IN_LEV_FIELD(x, y) ||
16174       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16175       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16176     return;
16177
16178   volume = SOUND_MAX_VOLUME;
16179
16180   if (!IN_SCR_FIELD(sx, sy))
16181   {
16182     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16183     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16184
16185     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16186   }
16187
16188   stereo_position = (SOUND_MAX_LEFT +
16189                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16190                      (SCR_FIELDX + 2 * max_distance));
16191
16192   if (IS_LOOP_SOUND(nr))
16193   {
16194     /* This assures that quieter loop sounds do not overwrite louder ones,
16195        while restarting sound volume comparison with each new game frame. */
16196
16197     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16198       return;
16199
16200     loop_sound_volume[nr] = volume;
16201     loop_sound_frame[nr] = FrameCounter;
16202   }
16203
16204   PlaySoundExt(nr, volume, stereo_position, type);
16205 }
16206
16207 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16208 {
16209   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16210                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16211                  y < LEVELY(BY1) ? LEVELY(BY1) :
16212                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16213                  sound_action);
16214 }
16215
16216 static void PlayLevelSoundAction(int x, int y, int action)
16217 {
16218   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16219 }
16220
16221 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16222 {
16223   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16224
16225   if (sound_effect != SND_UNDEFINED)
16226     PlayLevelSound(x, y, sound_effect);
16227 }
16228
16229 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16230                                               int action)
16231 {
16232   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16233
16234   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16235     PlayLevelSound(x, y, sound_effect);
16236 }
16237
16238 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16239 {
16240   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16241
16242   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16243     PlayLevelSound(x, y, sound_effect);
16244 }
16245
16246 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16247 {
16248   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16249
16250   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16251     StopSound(sound_effect);
16252 }
16253
16254 static void PlayLevelMusic()
16255 {
16256   if (levelset.music[level_nr] != MUS_UNDEFINED)
16257     PlayMusic(levelset.music[level_nr]);        /* from config file */
16258   else
16259     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16260 }
16261
16262 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16263 {
16264   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16265   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16266   int x = xx - 1 - offset;
16267   int y = yy - 1 - offset;
16268
16269   switch (sample)
16270   {
16271     case SAMPLE_blank:
16272       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16273       break;
16274
16275     case SAMPLE_roll:
16276       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16277       break;
16278
16279     case SAMPLE_stone:
16280       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16281       break;
16282
16283     case SAMPLE_nut:
16284       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16285       break;
16286
16287     case SAMPLE_crack:
16288       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16289       break;
16290
16291     case SAMPLE_bug:
16292       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16293       break;
16294
16295     case SAMPLE_tank:
16296       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16297       break;
16298
16299     case SAMPLE_android_clone:
16300       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16301       break;
16302
16303     case SAMPLE_android_move:
16304       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16305       break;
16306
16307     case SAMPLE_spring:
16308       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16309       break;
16310
16311     case SAMPLE_slurp:
16312       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16313       break;
16314
16315     case SAMPLE_eater:
16316       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16317       break;
16318
16319     case SAMPLE_eater_eat:
16320       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16321       break;
16322
16323     case SAMPLE_alien:
16324       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16325       break;
16326
16327     case SAMPLE_collect:
16328       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16329       break;
16330
16331     case SAMPLE_diamond:
16332       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16333       break;
16334
16335     case SAMPLE_squash:
16336       /* !!! CHECK THIS !!! */
16337 #if 1
16338       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16339 #else
16340       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16341 #endif
16342       break;
16343
16344     case SAMPLE_wonderfall:
16345       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16346       break;
16347
16348     case SAMPLE_drip:
16349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16350       break;
16351
16352     case SAMPLE_push:
16353       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16354       break;
16355
16356     case SAMPLE_dirt:
16357       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16358       break;
16359
16360     case SAMPLE_acid:
16361       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16362       break;
16363
16364     case SAMPLE_ball:
16365       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16366       break;
16367
16368     case SAMPLE_grow:
16369       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16370       break;
16371
16372     case SAMPLE_wonder:
16373       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16374       break;
16375
16376     case SAMPLE_door:
16377       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16378       break;
16379
16380     case SAMPLE_exit_open:
16381       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16382       break;
16383
16384     case SAMPLE_exit_leave:
16385       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16386       break;
16387
16388     case SAMPLE_dynamite:
16389       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16390       break;
16391
16392     case SAMPLE_tick:
16393       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16394       break;
16395
16396     case SAMPLE_press:
16397       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16398       break;
16399
16400     case SAMPLE_wheel:
16401       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16402       break;
16403
16404     case SAMPLE_boom:
16405       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16406       break;
16407
16408     case SAMPLE_die:
16409       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16410       break;
16411
16412     case SAMPLE_time:
16413       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16414       break;
16415
16416     default:
16417       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16418       break;
16419   }
16420 }
16421
16422 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16423 {
16424   int element = map_element_SP_to_RND(element_sp);
16425   int action = map_action_SP_to_RND(action_sp);
16426   int offset = (setup.sp_show_border_elements ? 0 : 1);
16427   int x = xx - offset;
16428   int y = yy - offset;
16429
16430 #if 0
16431   printf("::: %d -> %d\n", element_sp, action_sp);
16432 #endif
16433
16434   PlayLevelSoundElementAction(x, y, element, action);
16435 }
16436
16437 void RaiseScore(int value)
16438 {
16439   local_player->score += value;
16440
16441 #if 1
16442   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16443
16444   DisplayGameControlValues();
16445 #else
16446   DrawGameValue_Score(local_player->score);
16447 #endif
16448 }
16449
16450 void RaiseScoreElement(int element)
16451 {
16452   switch (element)
16453   {
16454     case EL_EMERALD:
16455     case EL_BD_DIAMOND:
16456     case EL_EMERALD_YELLOW:
16457     case EL_EMERALD_RED:
16458     case EL_EMERALD_PURPLE:
16459     case EL_SP_INFOTRON:
16460       RaiseScore(level.score[SC_EMERALD]);
16461       break;
16462     case EL_DIAMOND:
16463       RaiseScore(level.score[SC_DIAMOND]);
16464       break;
16465     case EL_CRYSTAL:
16466       RaiseScore(level.score[SC_CRYSTAL]);
16467       break;
16468     case EL_PEARL:
16469       RaiseScore(level.score[SC_PEARL]);
16470       break;
16471     case EL_BUG:
16472     case EL_BD_BUTTERFLY:
16473     case EL_SP_ELECTRON:
16474       RaiseScore(level.score[SC_BUG]);
16475       break;
16476     case EL_SPACESHIP:
16477     case EL_BD_FIREFLY:
16478     case EL_SP_SNIKSNAK:
16479       RaiseScore(level.score[SC_SPACESHIP]);
16480       break;
16481     case EL_YAMYAM:
16482     case EL_DARK_YAMYAM:
16483       RaiseScore(level.score[SC_YAMYAM]);
16484       break;
16485     case EL_ROBOT:
16486       RaiseScore(level.score[SC_ROBOT]);
16487       break;
16488     case EL_PACMAN:
16489       RaiseScore(level.score[SC_PACMAN]);
16490       break;
16491     case EL_NUT:
16492       RaiseScore(level.score[SC_NUT]);
16493       break;
16494     case EL_DYNAMITE:
16495     case EL_EM_DYNAMITE:
16496     case EL_SP_DISK_RED:
16497     case EL_DYNABOMB_INCREASE_NUMBER:
16498     case EL_DYNABOMB_INCREASE_SIZE:
16499     case EL_DYNABOMB_INCREASE_POWER:
16500       RaiseScore(level.score[SC_DYNAMITE]);
16501       break;
16502     case EL_SHIELD_NORMAL:
16503     case EL_SHIELD_DEADLY:
16504       RaiseScore(level.score[SC_SHIELD]);
16505       break;
16506     case EL_EXTRA_TIME:
16507       RaiseScore(level.extra_time_score);
16508       break;
16509     case EL_KEY_1:
16510     case EL_KEY_2:
16511     case EL_KEY_3:
16512     case EL_KEY_4:
16513     case EL_EM_KEY_1:
16514     case EL_EM_KEY_2:
16515     case EL_EM_KEY_3:
16516     case EL_EM_KEY_4:
16517     case EL_EMC_KEY_5:
16518     case EL_EMC_KEY_6:
16519     case EL_EMC_KEY_7:
16520     case EL_EMC_KEY_8:
16521     case EL_DC_KEY_WHITE:
16522       RaiseScore(level.score[SC_KEY]);
16523       break;
16524     default:
16525       RaiseScore(element_info[element].collect_score);
16526       break;
16527   }
16528 }
16529
16530 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16531 {
16532   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16533   {
16534 #if 1
16535     /* closing door required in case of envelope style request dialogs */
16536     if (!skip_request)
16537       CloseDoor(DOOR_CLOSE_1);
16538 #endif
16539
16540 #if defined(NETWORK_AVALIABLE)
16541     if (options.network)
16542       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16543     else
16544 #endif
16545     {
16546       if (quick_quit)
16547       {
16548 #if 1
16549
16550 #if 1
16551         FadeSkipNextFadeIn();
16552 #else
16553         fading = fading_none;
16554 #endif
16555
16556 #else
16557         OpenDoor(DOOR_CLOSE_1);
16558 #endif
16559
16560         game_status = GAME_MODE_MAIN;
16561
16562 #if 1
16563         DrawAndFadeInMainMenu(REDRAW_FIELD);
16564 #else
16565         DrawMainMenu();
16566 #endif
16567       }
16568       else
16569       {
16570 #if 0
16571         FadeOut(REDRAW_FIELD);
16572 #endif
16573
16574         game_status = GAME_MODE_MAIN;
16575
16576         DrawAndFadeInMainMenu(REDRAW_FIELD);
16577       }
16578     }
16579   }
16580   else          /* continue playing the game */
16581   {
16582     if (tape.playing && tape.deactivate_display)
16583       TapeDeactivateDisplayOff(TRUE);
16584
16585     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16586
16587     if (tape.playing && tape.deactivate_display)
16588       TapeDeactivateDisplayOn();
16589   }
16590 }
16591
16592 void RequestQuitGame(boolean ask_if_really_quit)
16593 {
16594   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16595   boolean skip_request = AllPlayersGone || quick_quit;
16596
16597   RequestQuitGameExt(skip_request, quick_quit,
16598                      "Do you really want to quit the game?");
16599 }
16600
16601
16602 /* ------------------------------------------------------------------------- */
16603 /* random generator functions                                                */
16604 /* ------------------------------------------------------------------------- */
16605
16606 unsigned int InitEngineRandom_RND(int seed)
16607 {
16608   game.num_random_calls = 0;
16609
16610 #if 0
16611   unsigned int rnd_seed = InitEngineRandom(seed);
16612
16613   printf("::: START RND: %d\n", rnd_seed);
16614
16615   return rnd_seed;
16616 #else
16617
16618   return InitEngineRandom(seed);
16619
16620 #endif
16621
16622 }
16623
16624 unsigned int RND(int max)
16625 {
16626   if (max > 0)
16627   {
16628     game.num_random_calls++;
16629
16630     return GetEngineRandom(max);
16631   }
16632
16633   return 0;
16634 }
16635
16636
16637 /* ------------------------------------------------------------------------- */
16638 /* game engine snapshot handling functions                                   */
16639 /* ------------------------------------------------------------------------- */
16640
16641 struct EngineSnapshotInfo
16642 {
16643   /* runtime values for custom element collect score */
16644   int collect_score[NUM_CUSTOM_ELEMENTS];
16645
16646   /* runtime values for group element choice position */
16647   int choice_pos[NUM_GROUP_ELEMENTS];
16648
16649   /* runtime values for belt position animations */
16650   int belt_graphic[4][NUM_BELT_PARTS];
16651   int belt_anim_mode[4][NUM_BELT_PARTS];
16652 };
16653
16654 static struct EngineSnapshotInfo engine_snapshot_rnd;
16655 static char *snapshot_level_identifier = NULL;
16656 static int snapshot_level_nr = -1;
16657
16658 static void SaveEngineSnapshotValues_RND()
16659 {
16660   static int belt_base_active_element[4] =
16661   {
16662     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16663     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16664     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16665     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16666   };
16667   int i, j;
16668
16669   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16670   {
16671     int element = EL_CUSTOM_START + i;
16672
16673     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16674   }
16675
16676   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16677   {
16678     int element = EL_GROUP_START + i;
16679
16680     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16681   }
16682
16683   for (i = 0; i < 4; i++)
16684   {
16685     for (j = 0; j < NUM_BELT_PARTS; j++)
16686     {
16687       int element = belt_base_active_element[i] + j;
16688       int graphic = el2img(element);
16689       int anim_mode = graphic_info[graphic].anim_mode;
16690
16691       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16692       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16693     }
16694   }
16695 }
16696
16697 static void LoadEngineSnapshotValues_RND()
16698 {
16699   unsigned int num_random_calls = game.num_random_calls;
16700   int i, j;
16701
16702   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16703   {
16704     int element = EL_CUSTOM_START + i;
16705
16706     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16707   }
16708
16709   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16710   {
16711     int element = EL_GROUP_START + i;
16712
16713     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16714   }
16715
16716   for (i = 0; i < 4; i++)
16717   {
16718     for (j = 0; j < NUM_BELT_PARTS; j++)
16719     {
16720       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16721       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16722
16723       graphic_info[graphic].anim_mode = anim_mode;
16724     }
16725   }
16726
16727   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16728   {
16729     InitRND(tape.random_seed);
16730     for (i = 0; i < num_random_calls; i++)
16731       RND(1);
16732   }
16733
16734   if (game.num_random_calls != num_random_calls)
16735   {
16736     Error(ERR_INFO, "number of random calls out of sync");
16737     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16738     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16739     Error(ERR_EXIT, "this should not happen -- please debug");
16740   }
16741 }
16742
16743 void FreeEngineSnapshot()
16744 {
16745   FreeEngineSnapshotBuffers();
16746
16747   setString(&snapshot_level_identifier, NULL);
16748   snapshot_level_nr = -1;
16749 }
16750
16751 void SaveEngineSnapshot()
16752 {
16753   /* do not save snapshots from editor */
16754   if (level_editor_test_game)
16755     return;
16756
16757   /* free previous snapshot buffers, if needed */
16758   FreeEngineSnapshotBuffers();
16759
16760 #if 1
16761   /* copy some special values to a structure better suited for the snapshot */
16762
16763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16764     SaveEngineSnapshotValues_RND();
16765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16766     SaveEngineSnapshotValues_EM();
16767   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16768     SaveEngineSnapshotValues_SP();
16769
16770   /* save values stored in special snapshot structure */
16771
16772   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16773     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16774   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16775     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16776   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16777     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16778 #else
16779   /* copy some special values to a structure better suited for the snapshot */
16780
16781   SaveEngineSnapshotValues_RND();
16782   SaveEngineSnapshotValues_EM();
16783   SaveEngineSnapshotValues_SP();
16784
16785   /* save values stored in special snapshot structure */
16786
16787   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16788   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16789   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16790 #endif
16791
16792   /* save further RND engine values */
16793
16794   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16795   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16796   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16797
16798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16799   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16800   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16801   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16802
16803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16804   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16805   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16807   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16808
16809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16810   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16812
16813   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16814
16815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16816
16817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16818   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16819
16820   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16822   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16824   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16827   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16838
16839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16841
16842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16845
16846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16848
16849   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16853   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16854
16855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16856   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16857
16858   /* save level identification information */
16859
16860   setString(&snapshot_level_identifier, leveldir_current->identifier);
16861   snapshot_level_nr = level_nr;
16862
16863 #if 0
16864   ListNode *node = engine_snapshot_list_rnd;
16865   int num_bytes = 0;
16866
16867   while (node != NULL)
16868   {
16869     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16870
16871     node = node->next;
16872   }
16873
16874   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16875 #endif
16876 }
16877
16878 void LoadEngineSnapshot()
16879 {
16880   /* restore generically stored snapshot buffers */
16881
16882   LoadEngineSnapshotBuffers();
16883
16884   /* restore special values from snapshot structure */
16885
16886 #if 1
16887   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16888     LoadEngineSnapshotValues_RND();
16889   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16890     LoadEngineSnapshotValues_EM();
16891   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16892     LoadEngineSnapshotValues_SP();
16893 #else
16894   LoadEngineSnapshotValues_RND();
16895   LoadEngineSnapshotValues_EM();
16896   LoadEngineSnapshotValues_SP();
16897 #endif
16898
16899 #if 0
16900   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16901 #endif
16902
16903 #if 0
16904   // needed if tile size was different when saving and loading engine snapshot
16905   if (local_player->present)
16906   {
16907     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
16908                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16909                 local_player->jx - MIDPOSX);
16910
16911     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16912                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16913                 local_player->jy - MIDPOSY);
16914   }
16915 #endif
16916
16917 #if 0
16918   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16919 #endif
16920 }
16921
16922 boolean CheckEngineSnapshot()
16923 {
16924   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16925           snapshot_level_nr == level_nr);
16926 }
16927
16928
16929 /* ---------- new game button stuff ---------------------------------------- */
16930
16931 static struct
16932 {
16933   int graphic;
16934   struct Rect *pos;
16935   int gadget_id;
16936   char *infotext;
16937 } gamebutton_info[NUM_GAME_BUTTONS] =
16938 {
16939   {
16940     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16941     GAME_CTRL_ID_STOP,                  "stop game"
16942   },
16943   {
16944     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16945     GAME_CTRL_ID_PAUSE,                 "pause game"
16946   },
16947   {
16948     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16949     GAME_CTRL_ID_PLAY,                  "play game"
16950   },
16951   {
16952     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16953     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16954   },
16955   {
16956     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16957     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16958   },
16959   {
16960     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16961     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16962   },
16963   {
16964     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16965     GAME_CTRL_ID_SAVE,                  "save game"
16966   },
16967   {
16968     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16969     GAME_CTRL_ID_LOAD,                  "load game"
16970   }
16971 };
16972
16973 void CreateGameButtons()
16974 {
16975   int i;
16976
16977   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16978   {
16979     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16980     struct Rect *pos = gamebutton_info[i].pos;
16981     struct GadgetInfo *gi;
16982     int button_type;
16983     boolean checked;
16984     unsigned int event_mask;
16985     int base_x = (tape.show_game_buttons ? VX : DX);
16986     int base_y = (tape.show_game_buttons ? VY : DY);
16987     int gd_x   = gfx->src_x;
16988     int gd_y   = gfx->src_y;
16989     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16990     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16991     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16992     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16993     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16994     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16995     int id = i;
16996
16997     if (gfx->bitmap == NULL)
16998     {
16999       game_gadget[id] = NULL;
17000
17001       continue;
17002     }
17003
17004     if (id == GAME_CTRL_ID_STOP ||
17005         id == GAME_CTRL_ID_PAUSE ||
17006         id == GAME_CTRL_ID_PLAY ||
17007         id == GAME_CTRL_ID_SAVE ||
17008         id == GAME_CTRL_ID_LOAD)
17009     {
17010       button_type = GD_TYPE_NORMAL_BUTTON;
17011       checked = FALSE;
17012       event_mask = GD_EVENT_RELEASED;
17013     }
17014     else
17015     {
17016       button_type = GD_TYPE_CHECK_BUTTON;
17017       checked =
17018         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17019          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17020          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17021       event_mask = GD_EVENT_PRESSED;
17022     }
17023
17024     gi = CreateGadget(GDI_CUSTOM_ID, id,
17025                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
17026                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17027                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17028                       GDI_WIDTH, gfx->width,
17029                       GDI_HEIGHT, gfx->height,
17030                       GDI_TYPE, button_type,
17031                       GDI_STATE, GD_BUTTON_UNPRESSED,
17032                       GDI_CHECKED, checked,
17033                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17034                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17035                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17036                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17037                       GDI_DIRECT_DRAW, FALSE,
17038                       GDI_EVENT_MASK, event_mask,
17039                       GDI_CALLBACK_ACTION, HandleGameButtons,
17040                       GDI_END);
17041
17042     if (gi == NULL)
17043       Error(ERR_EXIT, "cannot create gadget");
17044
17045     game_gadget[id] = gi;
17046   }
17047 }
17048
17049 void FreeGameButtons()
17050 {
17051   int i;
17052
17053   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17054     FreeGadget(game_gadget[i]);
17055 }
17056
17057 void MapGameButtons()
17058 {
17059   int i;
17060
17061   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17062     MapGadget(game_gadget[i]);
17063 }
17064
17065 void UnmapGameButtons()
17066 {
17067   int i;
17068
17069   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17070     UnmapGadget(game_gadget[i]);
17071 }
17072
17073 void RedrawGameButtons()
17074 {
17075   int i;
17076
17077   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17078     RedrawGadget(game_gadget[i]);
17079
17080   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
17081   redraw_mask &= ~REDRAW_ALL;
17082 }
17083
17084 static void HandleGameButtonsExt(int id)
17085 {
17086   boolean handle_game_buttons =
17087     (game_status == GAME_MODE_PLAYING ||
17088      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17089
17090   if (!handle_game_buttons)
17091     return;
17092
17093   switch (id)
17094   {
17095     case GAME_CTRL_ID_STOP:
17096       if (game_status == GAME_MODE_MAIN)
17097         break;
17098
17099       if (tape.playing)
17100         TapeStop();
17101       else
17102         RequestQuitGame(TRUE);
17103
17104       break;
17105
17106     case GAME_CTRL_ID_PAUSE:
17107       if (options.network && game_status == GAME_MODE_PLAYING)
17108       {
17109 #if defined(NETWORK_AVALIABLE)
17110         if (tape.pausing)
17111           SendToServer_ContinuePlaying();
17112         else
17113           SendToServer_PausePlaying();
17114 #endif
17115       }
17116       else
17117         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17118       break;
17119
17120     case GAME_CTRL_ID_PLAY:
17121       if (game_status == GAME_MODE_MAIN)
17122       {
17123         StartGameActions(options.network, setup.autorecord, level.random_seed);
17124       }
17125       else if (tape.pausing)
17126       {
17127 #if defined(NETWORK_AVALIABLE)
17128         if (options.network)
17129           SendToServer_ContinuePlaying();
17130         else
17131 #endif
17132         {
17133           tape.pausing = FALSE;
17134           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17135         }
17136       }
17137       break;
17138
17139     case SOUND_CTRL_ID_MUSIC:
17140       if (setup.sound_music)
17141       { 
17142         setup.sound_music = FALSE;
17143
17144         FadeMusic();
17145       }
17146       else if (audio.music_available)
17147       { 
17148         setup.sound = setup.sound_music = TRUE;
17149
17150         SetAudioMode(setup.sound);
17151
17152         PlayLevelMusic();
17153       }
17154       break;
17155
17156     case SOUND_CTRL_ID_LOOPS:
17157       if (setup.sound_loops)
17158         setup.sound_loops = FALSE;
17159       else if (audio.loops_available)
17160       {
17161         setup.sound = setup.sound_loops = TRUE;
17162
17163         SetAudioMode(setup.sound);
17164       }
17165       break;
17166
17167     case SOUND_CTRL_ID_SIMPLE:
17168       if (setup.sound_simple)
17169         setup.sound_simple = FALSE;
17170       else if (audio.sound_available)
17171       {
17172         setup.sound = setup.sound_simple = TRUE;
17173
17174         SetAudioMode(setup.sound);
17175       }
17176       break;
17177
17178     case GAME_CTRL_ID_SAVE:
17179       TapeQuickSave();
17180       break;
17181
17182     case GAME_CTRL_ID_LOAD:
17183       TapeQuickLoad();
17184       break;
17185
17186     default:
17187       break;
17188   }
17189 }
17190
17191 static void HandleGameButtons(struct GadgetInfo *gi)
17192 {
17193   HandleGameButtonsExt(gi->custom_id);
17194 }
17195
17196 void HandleSoundButtonKeys(Key key)
17197 {
17198 #if 1
17199   if (key == setup.shortcut.sound_simple)
17200     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17201   else if (key == setup.shortcut.sound_loops)
17202     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17203   else if (key == setup.shortcut.sound_music)
17204     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17205 #else
17206   if (key == setup.shortcut.sound_simple)
17207     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17208   else if (key == setup.shortcut.sound_loops)
17209     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17210   else if (key == setup.shortcut.sound_music)
17211     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17212 #endif
17213 }