2e951c1761a96476b13fa9436cb5d6e0d254a2dc
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF                   (                         1)
34
35 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
36 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
37 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
38 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
39 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
40 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
41 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
44 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
45 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
46 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
51 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
54
55 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
56
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
58
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
60
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
63
64 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
65 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
68
69 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
70
71 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
72
73 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
74
75 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
76
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y)                               \
79         GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
81         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
83         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y)                           \
85         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
86 #else
87 #define TEST_DrawLevelField(x, y)                               \
88              DrawLevelField(x, y)
89 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
90              DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
92              DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y)                           \
94              DrawTwinkleOnField(x, y)
95 #endif
96
97
98 /* for DigField() */
99 #define DF_NO_PUSH              0
100 #define DF_DIG                  1
101 #define DF_SNAP                 2
102
103 /* for MovePlayer() */
104 #define MP_NO_ACTION            0
105 #define MP_MOVING               1
106 #define MP_ACTION               2
107 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
108
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT             0
111 #define SCROLL_GO_ON            1
112
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START          0
115 #define EX_TYPE_NONE            0
116 #define EX_TYPE_NORMAL          (1 << 0)
117 #define EX_TYPE_CENTER          (1 << 1)
118 #define EX_TYPE_BORDER          (1 << 2)
119 #define EX_TYPE_CROSS           (1 << 3)
120 #define EX_TYPE_DYNA            (1 << 4)
121 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
122
123 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
127
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER                 0
130 #define GAME_PANEL_GEMS                         1
131 #define GAME_PANEL_INVENTORY_COUNT              2
132 #define GAME_PANEL_INVENTORY_FIRST_1            3
133 #define GAME_PANEL_INVENTORY_FIRST_2            4
134 #define GAME_PANEL_INVENTORY_FIRST_3            5
135 #define GAME_PANEL_INVENTORY_FIRST_4            6
136 #define GAME_PANEL_INVENTORY_FIRST_5            7
137 #define GAME_PANEL_INVENTORY_FIRST_6            8
138 #define GAME_PANEL_INVENTORY_FIRST_7            9
139 #define GAME_PANEL_INVENTORY_FIRST_8            10
140 #define GAME_PANEL_INVENTORY_LAST_1             11
141 #define GAME_PANEL_INVENTORY_LAST_2             12
142 #define GAME_PANEL_INVENTORY_LAST_3             13
143 #define GAME_PANEL_INVENTORY_LAST_4             14
144 #define GAME_PANEL_INVENTORY_LAST_5             15
145 #define GAME_PANEL_INVENTORY_LAST_6             16
146 #define GAME_PANEL_INVENTORY_LAST_7             17
147 #define GAME_PANEL_INVENTORY_LAST_8             18
148 #define GAME_PANEL_KEY_1                        19
149 #define GAME_PANEL_KEY_2                        20
150 #define GAME_PANEL_KEY_3                        21
151 #define GAME_PANEL_KEY_4                        22
152 #define GAME_PANEL_KEY_5                        23
153 #define GAME_PANEL_KEY_6                        24
154 #define GAME_PANEL_KEY_7                        25
155 #define GAME_PANEL_KEY_8                        26
156 #define GAME_PANEL_KEY_WHITE                    27
157 #define GAME_PANEL_KEY_WHITE_COUNT              28
158 #define GAME_PANEL_SCORE                        29
159 #define GAME_PANEL_HIGHSCORE                    30
160 #define GAME_PANEL_TIME                         31
161 #define GAME_PANEL_TIME_HH                      32
162 #define GAME_PANEL_TIME_MM                      33
163 #define GAME_PANEL_TIME_SS                      34
164 #define GAME_PANEL_FRAME                        35
165 #define GAME_PANEL_SHIELD_NORMAL                36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
167 #define GAME_PANEL_SHIELD_DEADLY                38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
169 #define GAME_PANEL_EXIT                         40
170 #define GAME_PANEL_EMC_MAGIC_BALL               41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
172 #define GAME_PANEL_LIGHT_SWITCH                 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
174 #define GAME_PANEL_TIMEGATE_SWITCH              45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
176 #define GAME_PANEL_SWITCHGATE_SWITCH            47
177 #define GAME_PANEL_EMC_LENSES                   48
178 #define GAME_PANEL_EMC_LENSES_TIME              49
179 #define GAME_PANEL_EMC_MAGNIFIER                50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
181 #define GAME_PANEL_BALLOON_SWITCH               52
182 #define GAME_PANEL_DYNABOMB_NUMBER              53
183 #define GAME_PANEL_DYNABOMB_SIZE                54
184 #define GAME_PANEL_DYNABOMB_POWER               55
185 #define GAME_PANEL_PENGUINS                     56
186 #define GAME_PANEL_SOKOBAN_OBJECTS              57
187 #define GAME_PANEL_SOKOBAN_FIELDS               58
188 #define GAME_PANEL_ROBOT_WHEEL                  59
189 #define GAME_PANEL_CONVEYOR_BELT_1              60
190 #define GAME_PANEL_CONVEYOR_BELT_2              61
191 #define GAME_PANEL_CONVEYOR_BELT_3              62
192 #define GAME_PANEL_CONVEYOR_BELT_4              63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
197 #define GAME_PANEL_MAGIC_WALL                   68
198 #define GAME_PANEL_MAGIC_WALL_TIME              69
199 #define GAME_PANEL_GRAVITY_STATE                70
200 #define GAME_PANEL_GRAPHIC_1                    71
201 #define GAME_PANEL_GRAPHIC_2                    72
202 #define GAME_PANEL_GRAPHIC_3                    73
203 #define GAME_PANEL_GRAPHIC_4                    74
204 #define GAME_PANEL_GRAPHIC_5                    75
205 #define GAME_PANEL_GRAPHIC_6                    76
206 #define GAME_PANEL_GRAPHIC_7                    77
207 #define GAME_PANEL_GRAPHIC_8                    78
208 #define GAME_PANEL_ELEMENT_1                    79
209 #define GAME_PANEL_ELEMENT_2                    80
210 #define GAME_PANEL_ELEMENT_3                    81
211 #define GAME_PANEL_ELEMENT_4                    82
212 #define GAME_PANEL_ELEMENT_5                    83
213 #define GAME_PANEL_ELEMENT_6                    84
214 #define GAME_PANEL_ELEMENT_7                    85
215 #define GAME_PANEL_ELEMENT_8                    86
216 #define GAME_PANEL_ELEMENT_COUNT_1              87
217 #define GAME_PANEL_ELEMENT_COUNT_2              88
218 #define GAME_PANEL_ELEMENT_COUNT_3              89
219 #define GAME_PANEL_ELEMENT_COUNT_4              90
220 #define GAME_PANEL_ELEMENT_COUNT_5              91
221 #define GAME_PANEL_ELEMENT_COUNT_6              92
222 #define GAME_PANEL_ELEMENT_COUNT_7              93
223 #define GAME_PANEL_ELEMENT_COUNT_8              94
224 #define GAME_PANEL_CE_SCORE_1                   95
225 #define GAME_PANEL_CE_SCORE_2                   96
226 #define GAME_PANEL_CE_SCORE_3                   97
227 #define GAME_PANEL_CE_SCORE_4                   98
228 #define GAME_PANEL_CE_SCORE_5                   99
229 #define GAME_PANEL_CE_SCORE_6                   100
230 #define GAME_PANEL_CE_SCORE_7                   101
231 #define GAME_PANEL_CE_SCORE_8                   102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
240 #define GAME_PANEL_PLAYER_NAME                  111
241 #define GAME_PANEL_LEVEL_NAME                   112
242 #define GAME_PANEL_LEVEL_AUTHOR                 113
243
244 #define NUM_GAME_PANEL_CONTROLS                 114
245
246 struct GamePanelOrderInfo
247 {
248   int nr;
249   int sort_priority;
250 };
251
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
253
254 struct GamePanelControlInfo
255 {
256   int nr;
257
258   struct TextPosInfo *pos;
259   int type;
260
261   int value, last_value;
262   int frame, last_frame;
263   int gfx_frame;
264   int gfx_random;
265 };
266
267 static struct GamePanelControlInfo game_panel_controls[] =
268 {
269   {
270     GAME_PANEL_LEVEL_NUMBER,
271     &game.panel.level_number,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_GEMS,
276     &game.panel.gems,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_COUNT,
281     &game.panel.inventory_count,
282     TYPE_INTEGER,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_1,
286     &game.panel.inventory_first[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_2,
291     &game.panel.inventory_first[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_3,
296     &game.panel.inventory_first[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_4,
301     &game.panel.inventory_first[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_5,
306     &game.panel.inventory_first[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_6,
311     &game.panel.inventory_first[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_7,
316     &game.panel.inventory_first[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_FIRST_8,
321     &game.panel.inventory_first[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_1,
326     &game.panel.inventory_last[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_2,
331     &game.panel.inventory_last[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_3,
336     &game.panel.inventory_last[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_4,
341     &game.panel.inventory_last[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_5,
346     &game.panel.inventory_last[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_6,
351     &game.panel.inventory_last[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_7,
356     &game.panel.inventory_last[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_INVENTORY_LAST_8,
361     &game.panel.inventory_last[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_1,
366     &game.panel.key[0],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_2,
371     &game.panel.key[1],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_3,
376     &game.panel.key[2],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_4,
381     &game.panel.key[3],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_5,
386     &game.panel.key[4],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_6,
391     &game.panel.key[5],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_7,
396     &game.panel.key[6],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_8,
401     &game.panel.key[7],
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE,
406     &game.panel.key_white,
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_PANEL_KEY_WHITE_COUNT,
411     &game.panel.key_white_count,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_SCORE,
416     &game.panel.score,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_HIGHSCORE,
421     &game.panel.highscore,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME,
426     &game.panel.time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_HH,
431     &game.panel.time_hh,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_TIME_MM,
436     &game.panel.time_mm,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_PANEL_TIME_SS,
441     &game.panel.time_ss,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_FRAME,
446     &game.panel.frame,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_PANEL_SHIELD_NORMAL,
451     &game.panel.shield_normal,
452     TYPE_ELEMENT,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL_TIME,
456     &game.panel.shield_normal_time,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_PANEL_SHIELD_DEADLY,
461     &game.panel.shield_deadly,
462     TYPE_ELEMENT,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY_TIME,
466     &game.panel.shield_deadly_time,
467     TYPE_INTEGER,
468   },
469   {
470     GAME_PANEL_EXIT,
471     &game.panel.exit,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_MAGIC_BALL,
476     &game.panel.emc_magic_ball,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481     &game.panel.emc_magic_ball_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_LIGHT_SWITCH,
486     &game.panel.light_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH_TIME,
491     &game.panel.light_switch_time,
492     TYPE_INTEGER,
493   },
494   {
495     GAME_PANEL_TIMEGATE_SWITCH,
496     &game.panel.timegate_switch,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH_TIME,
501     &game.panel.timegate_switch_time,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_SWITCHGATE_SWITCH,
506     &game.panel.switchgate_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_LENSES,
511     &game.panel.emc_lenses,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES_TIME,
516     &game.panel.emc_lenses_time,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_EMC_MAGNIFIER,
521     &game.panel.emc_magnifier,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER_TIME,
526     &game.panel.emc_magnifier_time,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_BALLOON_SWITCH,
531     &game.panel.balloon_switch,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_DYNABOMB_NUMBER,
536     &game.panel.dynabomb_number,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_DYNABOMB_SIZE,
541     &game.panel.dynabomb_size,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_POWER,
546     &game.panel.dynabomb_power,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_PENGUINS,
551     &game.panel.penguins,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_SOKOBAN_OBJECTS,
556     &game.panel.sokoban_objects,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_FIELDS,
561     &game.panel.sokoban_fields,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_ROBOT_WHEEL,
566     &game.panel.robot_wheel,
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_1,
571     &game.panel.conveyor_belt[0],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_2,
576     &game.panel.conveyor_belt[1],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_3,
581     &game.panel.conveyor_belt[2],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_4,
586     &game.panel.conveyor_belt[3],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591     &game.panel.conveyor_belt_switch[0],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596     &game.panel.conveyor_belt_switch[1],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601     &game.panel.conveyor_belt_switch[2],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606     &game.panel.conveyor_belt_switch[3],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_MAGIC_WALL,
611     &game.panel.magic_wall,
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL_TIME,
616     &game.panel.magic_wall_time,
617     TYPE_INTEGER,
618   },
619   {
620     GAME_PANEL_GRAVITY_STATE,
621     &game.panel.gravity_state,
622     TYPE_STRING,
623   },
624   {
625     GAME_PANEL_GRAPHIC_1,
626     &game.panel.graphic[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_GRAPHIC_2,
631     &game.panel.graphic[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_3,
636     &game.panel.graphic[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_4,
641     &game.panel.graphic[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_5,
646     &game.panel.graphic[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_6,
651     &game.panel.graphic[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_7,
656     &game.panel.graphic[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_8,
661     &game.panel.graphic[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_1,
666     &game.panel.element[0],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_2,
671     &game.panel.element[1],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_3,
676     &game.panel.element[2],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_4,
681     &game.panel.element[3],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_5,
686     &game.panel.element[4],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_6,
691     &game.panel.element[5],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_7,
696     &game.panel.element[6],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_8,
701     &game.panel.element[7],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_COUNT_1,
706     &game.panel.element_count[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_2,
711     &game.panel.element_count[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_3,
716     &game.panel.element_count[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_4,
721     &game.panel.element_count[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_5,
726     &game.panel.element_count[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_6,
731     &game.panel.element_count[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_7,
736     &game.panel.element_count[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_8,
741     &game.panel.element_count[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1,
746     &game.panel.ce_score[0],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2,
751     &game.panel.ce_score[1],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3,
756     &game.panel.ce_score[2],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4,
761     &game.panel.ce_score[3],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5,
766     &game.panel.ce_score[4],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6,
771     &game.panel.ce_score[5],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7,
776     &game.panel.ce_score[6],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8,
781     &game.panel.ce_score[7],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_1_ELEMENT,
786     &game.panel.ce_score_element[0],
787     TYPE_ELEMENT,
788   },
789   {
790     GAME_PANEL_CE_SCORE_2_ELEMENT,
791     &game.panel.ce_score_element[1],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_3_ELEMENT,
796     &game.panel.ce_score_element[2],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_4_ELEMENT,
801     &game.panel.ce_score_element[3],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_5_ELEMENT,
806     &game.panel.ce_score_element[4],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_6_ELEMENT,
811     &game.panel.ce_score_element[5],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_7_ELEMENT,
816     &game.panel.ce_score_element[6],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_8_ELEMENT,
821     &game.panel.ce_score_element[7],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_PLAYER_NAME,
826     &game.panel.player_name,
827     TYPE_STRING,
828   },
829   {
830     GAME_PANEL_LEVEL_NAME,
831     &game.panel.level_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_AUTHOR,
836     &game.panel.level_author,
837     TYPE_STRING,
838   },
839
840   {
841     -1,
842     NULL,
843     -1,
844   }
845 };
846
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING      3
849 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION   2
851 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
852
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF  -1
855 #define INITIAL_MOVE_DELAY_ON   0
856
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED    32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED   4
861 #define MOVE_DELAY_MAX_SPEED    1
862
863 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
865
866 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
868
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN       (1)
872 #define MOVE_STEPSIZE_MAX       (TILEX)
873
874 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
876
877 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
878
879 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
880                                  RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
882                                  RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
884                                  RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
886                                     (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
888                                  RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
891                                  RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
893                                  RND((c)->delay_random))
894
895
896 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
897          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
898
899 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
900         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
901          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
902          (be) + (e) - EL_SELF)
903
904 #define GET_PLAYER_FROM_BITS(p)                                         \
905         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
906
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
908         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
909          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
910          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
911          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
912          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
913          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
914          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
915          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
916          (e))
917
918 #define CAN_GROW_INTO(e)                                                \
919         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
920
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
922                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
923                                         (condition)))
924
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
926                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
927                                         (CAN_MOVE_INTO_ACID(e) &&       \
928                                          Feld[x][y] == EL_ACID) ||      \
929                                         (condition)))
930
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
932                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
933                                         (CAN_MOVE_INTO_ACID(e) &&       \
934                                          Feld[x][y] == EL_ACID) ||      \
935                                         (condition)))
936
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
938                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
939                                         (condition) ||                  \
940                                         (CAN_MOVE_INTO_ACID(e) &&       \
941                                          Feld[x][y] == EL_ACID) ||      \
942                                         (DONT_COLLIDE_WITH(e) &&        \
943                                          IS_PLAYER(x, y) &&             \
944                                          !PLAYER_ENEMY_PROTECTED(x, y))))
945
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
947         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
948
949 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
951
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
954
955 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
956         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
958
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
960         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
961
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
963         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
964
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
966         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
967
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
969         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
970
971 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
972         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
973
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
975         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979                                                  IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
981         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
982
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
984         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
985
986 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
987         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
990         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
991                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
992
993 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
994
995 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
996                 (!IS_PLAYER(x, y) &&                                    \
997                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
998
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1000         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1001
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1004
1005 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1009
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP               0
1012 #define GAME_CTRL_ID_PAUSE              1
1013 #define GAME_CTRL_ID_PLAY               2
1014 #define SOUND_CTRL_ID_MUSIC             3
1015 #define SOUND_CTRL_ID_LOOPS             4
1016 #define SOUND_CTRL_ID_SIMPLE            5
1017 #define GAME_CTRL_ID_SAVE               6
1018 #define GAME_CTRL_ID_LOAD               7
1019
1020 #define NUM_GAME_BUTTONS                8
1021
1022
1023 /* forward declaration for internal use */
1024
1025 static void CreateField(int, int, int);
1026
1027 static void ResetGfxAnimation(int, int);
1028
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1031
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1036
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1041
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1048
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1052 #if 0
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1054 #endif
1055
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1059
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1062         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1066         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1068         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1069
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev)                             \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1074         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1076         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1077
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1086
1087 static void HandleGameButtons(struct GadgetInfo *);
1088
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1096
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1107
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1111
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1114
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1116
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1120
1121 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1122 {                                                                       \
1123   if (recursion_loop_detected)                                          \
1124     return (rc);                                                        \
1125                                                                         \
1126   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1127   {                                                                     \
1128     recursion_loop_detected = TRUE;                                     \
1129     recursion_loop_element = (e);                                       \
1130   }                                                                     \
1131                                                                         \
1132   recursion_loop_depth++;                                               \
1133 }
1134
1135 #define RECURSION_LOOP_DETECTION_END()                                  \
1136 {                                                                       \
1137   recursion_loop_depth--;                                               \
1138 }
1139
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1143
1144 static int map_player_action[MAX_PLAYERS];
1145
1146
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after  */
1149 /* a specified time, eventually calling a function when changing             */
1150 /* ------------------------------------------------------------------------- */
1151
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1155
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1159
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1163
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1166
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1169
1170 struct ChangingElementInfo
1171 {
1172   int element;
1173   int target_element;
1174   int change_delay;
1175   void (*pre_change_function)(int x, int y);
1176   void (*change_function)(int x, int y);
1177   void (*post_change_function)(int x, int y);
1178 };
1179
1180 static struct ChangingElementInfo change_delay_list[] =
1181 {
1182   {
1183     EL_NUT_BREAKING,
1184     EL_EMERALD,
1185     6,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_PEARL_BREAKING,
1192     EL_EMPTY,
1193     8,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EXIT_OPENING,
1200     EL_EXIT_OPEN,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EXIT_CLOSING,
1208     EL_EXIT_CLOSED,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_STEEL_EXIT_OPENING,
1216     EL_STEEL_EXIT_OPEN,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_STEEL_EXIT_CLOSING,
1224     EL_STEEL_EXIT_CLOSED,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_EM_EXIT_OPENING,
1232     EL_EM_EXIT_OPEN,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_EM_EXIT_CLOSING,
1240 #if 1
1241     EL_EMPTY,
1242 #else
1243     EL_EM_EXIT_CLOSED,
1244 #endif
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260 #if 1
1261     EL_STEELWALL,
1262 #else
1263     EL_EM_STEEL_EXIT_CLOSED,
1264 #endif
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270   {
1271     EL_SP_EXIT_OPENING,
1272     EL_SP_EXIT_OPEN,
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_SP_EXIT_CLOSING,
1280     EL_SP_EXIT_CLOSED,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SWITCHGATE_OPENING,
1288     EL_SWITCHGATE_OPEN,
1289     29,
1290     NULL,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SWITCHGATE_CLOSING,
1296     EL_SWITCHGATE_CLOSED,
1297     29,
1298     NULL,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_TIMEGATE_OPENING,
1304     EL_TIMEGATE_OPEN,
1305     29,
1306     NULL,
1307     NULL,
1308     NULL
1309   },
1310   {
1311     EL_TIMEGATE_CLOSING,
1312     EL_TIMEGATE_CLOSED,
1313     29,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318
1319   {
1320     EL_ACID_SPLASH_LEFT,
1321     EL_EMPTY,
1322     8,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_ACID_SPLASH_RIGHT,
1329     EL_EMPTY,
1330     8,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_SP_BUGGY_BASE,
1337     EL_SP_BUGGY_BASE_ACTIVATING,
1338     0,
1339     InitBuggyBase,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_SP_BUGGY_BASE_ACTIVATING,
1345     EL_SP_BUGGY_BASE_ACTIVE,
1346     0,
1347     InitBuggyBase,
1348     NULL,
1349     NULL
1350   },
1351   {
1352     EL_SP_BUGGY_BASE_ACTIVE,
1353     EL_SP_BUGGY_BASE,
1354     0,
1355     InitBuggyBase,
1356     WarnBuggyBase,
1357     NULL
1358   },
1359   {
1360     EL_TRAP,
1361     EL_TRAP_ACTIVE,
1362     0,
1363     InitTrap,
1364     NULL,
1365     ActivateTrap
1366   },
1367   {
1368     EL_TRAP_ACTIVE,
1369     EL_TRAP,
1370     31,
1371     NULL,
1372     ChangeActiveTrap,
1373     NULL
1374   },
1375   {
1376     EL_ROBOT_WHEEL_ACTIVE,
1377     EL_ROBOT_WHEEL,
1378     0,
1379     InitRobotWheel,
1380     RunRobotWheel,
1381     StopRobotWheel
1382   },
1383   {
1384     EL_TIMEGATE_SWITCH_ACTIVE,
1385     EL_TIMEGATE_SWITCH,
1386     0,
1387     InitTimegateWheel,
1388     RunTimegateWheel,
1389     NULL
1390   },
1391   {
1392     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393     EL_DC_TIMEGATE_SWITCH,
1394     0,
1395     InitTimegateWheel,
1396     RunTimegateWheel,
1397     NULL
1398   },
1399   {
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     0,
1403     InitMagicBallDelay,
1404     NULL,
1405     ActivateMagicBall
1406   },
1407   {
1408     EL_EMC_SPRING_BUMPER_ACTIVE,
1409     EL_EMC_SPRING_BUMPER,
1410     8,
1411     NULL,
1412     NULL,
1413     NULL
1414   },
1415   {
1416     EL_DIAGONAL_SHRINKING,
1417     EL_UNDEFINED,
1418     0,
1419     NULL,
1420     NULL,
1421     NULL
1422   },
1423   {
1424     EL_DIAGONAL_GROWING,
1425     EL_UNDEFINED,
1426     0,
1427     NULL,
1428     NULL,
1429     NULL,
1430   },
1431
1432   {
1433     EL_UNDEFINED,
1434     EL_UNDEFINED,
1435     -1,
1436     NULL,
1437     NULL,
1438     NULL
1439   }
1440 };
1441
1442 struct
1443 {
1444   int element;
1445   int push_delay_fixed, push_delay_random;
1446 }
1447 push_delay_list[] =
1448 {
1449   { EL_SPRING,                  0, 0 },
1450   { EL_BALLOON,                 0, 0 },
1451
1452   { EL_SOKOBAN_OBJECT,          2, 0 },
1453   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1454   { EL_SATELLITE,               2, 0 },
1455   { EL_SP_DISK_YELLOW,          2, 0 },
1456
1457   { EL_UNDEFINED,               0, 0 },
1458 };
1459
1460 struct
1461 {
1462   int element;
1463   int move_stepsize;
1464 }
1465 move_stepsize_list[] =
1466 {
1467   { EL_AMOEBA_DROP,             2 },
1468   { EL_AMOEBA_DROPPING,         2 },
1469   { EL_QUICKSAND_FILLING,       1 },
1470   { EL_QUICKSAND_EMPTYING,      1 },
1471   { EL_QUICKSAND_FAST_FILLING,  2 },
1472   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473   { EL_MAGIC_WALL_FILLING,      2 },
1474   { EL_MAGIC_WALL_EMPTYING,     2 },
1475   { EL_BD_MAGIC_WALL_FILLING,   2 },
1476   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1477   { EL_DC_MAGIC_WALL_FILLING,   2 },
1478   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1479
1480   { EL_UNDEFINED,               0 },
1481 };
1482
1483 struct
1484 {
1485   int element;
1486   int count;
1487 }
1488 collect_count_list[] =
1489 {
1490   { EL_EMERALD,                 1 },
1491   { EL_BD_DIAMOND,              1 },
1492   { EL_EMERALD_YELLOW,          1 },
1493   { EL_EMERALD_RED,             1 },
1494   { EL_EMERALD_PURPLE,          1 },
1495   { EL_DIAMOND,                 3 },
1496   { EL_SP_INFOTRON,             1 },
1497   { EL_PEARL,                   5 },
1498   { EL_CRYSTAL,                 8 },
1499
1500   { EL_UNDEFINED,               0 },
1501 };
1502
1503 struct
1504 {
1505   int element;
1506   int direction;
1507 }
1508 access_direction_list[] =
1509 {
1510   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1513   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1514   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1515   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1516   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1517   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1518   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1519   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1520   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1521
1522   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1523   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1524   { EL_SP_PORT_UP,                                                   MV_DOWN },
1525   { EL_SP_PORT_DOWN,                                         MV_UP           },
1526   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1527   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1528   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1530   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1531   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1532   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1533   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1534   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1535   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1536   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1537   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1538   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1539   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1540   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1541
1542   { EL_UNDEFINED,                       MV_NONE                              }
1543 };
1544
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1546
1547 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1550                                  IS_JUST_CHANGING(x, y))
1551
1552 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1553
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1559
1560 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1561                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1562                                      (y) += playfield_scan_delta_y)     \
1563                                 for ((x) = playfield_scan_start_x;      \
1564                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1565                                      (x) += playfield_scan_delta_x)
1566
1567 #ifdef DEBUG
1568 void DEBUG_SetMaximumDynamite()
1569 {
1570   int i;
1571
1572   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574       local_player->inventory_element[local_player->inventory_size++] =
1575         EL_DYNAMITE;
1576 }
1577 #endif
1578
1579 static void InitPlayfieldScanModeVars()
1580 {
1581   if (game.use_reverse_scan_direction)
1582   {
1583     playfield_scan_start_x = lev_fieldx - 1;
1584     playfield_scan_start_y = lev_fieldy - 1;
1585
1586     playfield_scan_delta_x = -1;
1587     playfield_scan_delta_y = -1;
1588   }
1589   else
1590   {
1591     playfield_scan_start_x = 0;
1592     playfield_scan_start_y = 0;
1593
1594     playfield_scan_delta_x = 1;
1595     playfield_scan_delta_y = 1;
1596   }
1597 }
1598
1599 static void InitPlayfieldScanMode(int mode)
1600 {
1601   game.use_reverse_scan_direction =
1602     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1603
1604   InitPlayfieldScanModeVars();
1605 }
1606
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1608 {
1609   move_stepsize =
1610     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1611
1612   /* make sure that stepsize value is always a power of 2 */
1613   move_stepsize = (1 << log_2(move_stepsize));
1614
1615   return TILEX / move_stepsize;
1616 }
1617
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1619                                boolean init_game)
1620 {
1621   int player_nr = player->index_nr;
1622   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1624
1625   /* do no immediately change move delay -- the player might just be moving */
1626   player->move_delay_value_next = move_delay;
1627
1628   /* information if player can move must be set separately */
1629   player->cannot_move = cannot_move;
1630
1631   if (init_game)
1632   {
1633     player->move_delay       = game.initial_move_delay[player_nr];
1634     player->move_delay_value = game.initial_move_delay_value[player_nr];
1635
1636     player->move_delay_value_next = -1;
1637
1638     player->move_delay_reset_counter = 0;
1639   }
1640 }
1641
1642 void GetPlayerConfig()
1643 {
1644   GameFrameDelay = setup.game_frame_delay;
1645
1646   if (!audio.sound_available)
1647     setup.sound_simple = FALSE;
1648
1649   if (!audio.loops_available)
1650     setup.sound_loops = FALSE;
1651
1652   if (!audio.music_available)
1653     setup.sound_music = FALSE;
1654
1655   if (!video.fullscreen_available)
1656     setup.fullscreen = FALSE;
1657
1658   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1659
1660   SetAudioMode(setup.sound);
1661   InitJoysticks();
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1691 {
1692   if (element == EL_SP_MURPHY)
1693   {
1694     if (init_game)
1695     {
1696       if (stored_player[0].present)
1697       {
1698         Feld[x][y] = EL_SP_MURPHY_CLONE;
1699
1700         return;
1701       }
1702       else
1703       {
1704         stored_player[0].initial_element = element;
1705         stored_player[0].use_murphy = TRUE;
1706
1707         if (!level.use_artwork_element[0])
1708           stored_player[0].artwork_element = EL_SP_MURPHY;
1709       }
1710
1711       Feld[x][y] = EL_PLAYER_1;
1712     }
1713   }
1714
1715   if (init_game)
1716   {
1717     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718     int jx = player->jx, jy = player->jy;
1719
1720     player->present = TRUE;
1721
1722     player->block_last_field = (element == EL_SP_MURPHY ?
1723                                 level.sp_block_last_field :
1724                                 level.block_last_field);
1725
1726     /* ---------- initialize player's last field block delay --------------- */
1727
1728     /* always start with reliable default value (no adjustment needed) */
1729     player->block_delay_adjustment = 0;
1730
1731     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732     if (player->block_last_field && element == EL_SP_MURPHY)
1733       player->block_delay_adjustment = 1;
1734
1735     /* special case 2: in game engines before 3.1.1, blocking was different */
1736     if (game.use_block_last_field_bug)
1737       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1738
1739     if (!options.network || player->connected)
1740     {
1741       player->active = TRUE;
1742
1743       /* remove potentially duplicate players */
1744       if (StorePlayer[jx][jy] == Feld[x][y])
1745         StorePlayer[jx][jy] = 0;
1746
1747       StorePlayer[x][y] = Feld[x][y];
1748
1749 #if DEBUG_INIT_PLAYER
1750       if (options.debug)
1751       {
1752         printf("- player element %d activated", player->element_nr);
1753         printf(" (local player is %d and currently %s)\n",
1754                local_player->element_nr,
1755                local_player->active ? "active" : "not active");
1756       }
1757     }
1758 #endif
1759
1760     Feld[x][y] = EL_EMPTY;
1761
1762     player->jx = player->last_jx = x;
1763     player->jy = player->last_jy = y;
1764   }
1765
1766 #if USE_PLAYER_REANIMATION
1767   if (!init_game)
1768   {
1769     int player_nr = GET_PLAYER_NR(element);
1770     struct PlayerInfo *player = &stored_player[player_nr];
1771
1772     if (player->active && player->killed)
1773       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1774   }
1775 #endif
1776 }
1777
1778 static void InitField(int x, int y, boolean init_game)
1779 {
1780   int element = Feld[x][y];
1781
1782   switch (element)
1783   {
1784     case EL_SP_MURPHY:
1785     case EL_PLAYER_1:
1786     case EL_PLAYER_2:
1787     case EL_PLAYER_3:
1788     case EL_PLAYER_4:
1789       InitPlayerField(x, y, element, init_game);
1790       break;
1791
1792     case EL_SOKOBAN_FIELD_PLAYER:
1793       element = Feld[x][y] = EL_PLAYER_1;
1794       InitField(x, y, init_game);
1795
1796       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797       InitField(x, y, init_game);
1798       break;
1799
1800     case EL_SOKOBAN_FIELD_EMPTY:
1801       local_player->sokobanfields_still_needed++;
1802       break;
1803
1804     case EL_STONEBLOCK:
1805       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1815       break;
1816
1817     case EL_BUG:
1818     case EL_BUG_RIGHT:
1819     case EL_BUG_UP:
1820     case EL_BUG_LEFT:
1821     case EL_BUG_DOWN:
1822     case EL_SPACESHIP:
1823     case EL_SPACESHIP_RIGHT:
1824     case EL_SPACESHIP_UP:
1825     case EL_SPACESHIP_LEFT:
1826     case EL_SPACESHIP_DOWN:
1827     case EL_BD_BUTTERFLY:
1828     case EL_BD_BUTTERFLY_RIGHT:
1829     case EL_BD_BUTTERFLY_UP:
1830     case EL_BD_BUTTERFLY_LEFT:
1831     case EL_BD_BUTTERFLY_DOWN:
1832     case EL_BD_FIREFLY:
1833     case EL_BD_FIREFLY_RIGHT:
1834     case EL_BD_FIREFLY_UP:
1835     case EL_BD_FIREFLY_LEFT:
1836     case EL_BD_FIREFLY_DOWN:
1837     case EL_PACMAN_RIGHT:
1838     case EL_PACMAN_UP:
1839     case EL_PACMAN_LEFT:
1840     case EL_PACMAN_DOWN:
1841     case EL_YAMYAM:
1842     case EL_YAMYAM_LEFT:
1843     case EL_YAMYAM_RIGHT:
1844     case EL_YAMYAM_UP:
1845     case EL_YAMYAM_DOWN:
1846     case EL_DARK_YAMYAM:
1847     case EL_ROBOT:
1848     case EL_PACMAN:
1849     case EL_SP_SNIKSNAK:
1850     case EL_SP_ELECTRON:
1851     case EL_MOLE:
1852     case EL_MOLE_LEFT:
1853     case EL_MOLE_RIGHT:
1854     case EL_MOLE_UP:
1855     case EL_MOLE_DOWN:
1856       InitMovDir(x, y);
1857       break;
1858
1859     case EL_AMOEBA_FULL:
1860     case EL_BD_AMOEBA:
1861       InitAmoebaNr(x, y);
1862       break;
1863
1864     case EL_AMOEBA_DROP:
1865       if (y == lev_fieldy - 1)
1866       {
1867         Feld[x][y] = EL_AMOEBA_GROWING;
1868         Store[x][y] = EL_AMOEBA_WET;
1869       }
1870       break;
1871
1872     case EL_DYNAMITE_ACTIVE:
1873     case EL_SP_DISK_RED_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878       MovDelay[x][y] = 96;
1879       break;
1880
1881     case EL_EM_DYNAMITE_ACTIVE:
1882       MovDelay[x][y] = 32;
1883       break;
1884
1885     case EL_LAMP:
1886       local_player->lights_still_needed++;
1887       break;
1888
1889     case EL_PENGUIN:
1890       local_player->friends_still_needed++;
1891       break;
1892
1893     case EL_PIG:
1894     case EL_DRAGON:
1895       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1896       break;
1897
1898     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1910       if (init_game)
1911       {
1912         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1915
1916         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1917         {
1918           game.belt_dir[belt_nr] = belt_dir;
1919           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1920         }
1921         else    /* more than one switch -- set it like the first switch */
1922         {
1923           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1924         }
1925       }
1926       break;
1927
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1930       if (init_game)
1931         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1932       break;
1933
1934     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1935       if (init_game)
1936         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1937       break;
1938 #endif
1939
1940     case EL_LIGHT_SWITCH_ACTIVE:
1941       if (init_game)
1942         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1943       break;
1944
1945     case EL_INVISIBLE_STEELWALL:
1946     case EL_INVISIBLE_WALL:
1947     case EL_INVISIBLE_SAND:
1948       if (game.light_time_left > 0 ||
1949           game.lenses_time_left > 0)
1950         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1951       break;
1952
1953     case EL_EMC_MAGIC_BALL:
1954       if (game.ball_state)
1955         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1956       break;
1957
1958     case EL_EMC_MAGIC_BALL_SWITCH:
1959       if (game.ball_state)
1960         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1961       break;
1962
1963     case EL_TRIGGER_PLAYER:
1964     case EL_TRIGGER_ELEMENT:
1965     case EL_TRIGGER_CE_VALUE:
1966     case EL_TRIGGER_CE_SCORE:
1967     case EL_SELF:
1968     case EL_ANY_ELEMENT:
1969     case EL_CURRENT_CE_VALUE:
1970     case EL_CURRENT_CE_SCORE:
1971     case EL_PREV_CE_1:
1972     case EL_PREV_CE_2:
1973     case EL_PREV_CE_3:
1974     case EL_PREV_CE_4:
1975     case EL_PREV_CE_5:
1976     case EL_PREV_CE_6:
1977     case EL_PREV_CE_7:
1978     case EL_PREV_CE_8:
1979     case EL_NEXT_CE_1:
1980     case EL_NEXT_CE_2:
1981     case EL_NEXT_CE_3:
1982     case EL_NEXT_CE_4:
1983     case EL_NEXT_CE_5:
1984     case EL_NEXT_CE_6:
1985     case EL_NEXT_CE_7:
1986     case EL_NEXT_CE_8:
1987       /* reference elements should not be used on the playfield */
1988       Feld[x][y] = EL_EMPTY;
1989       break;
1990
1991     default:
1992       if (IS_CUSTOM_ELEMENT(element))
1993       {
1994         if (CAN_MOVE(element))
1995           InitMovDir(x, y);
1996
1997 #if USE_NEW_CUSTOM_VALUE
1998         if (!element_info[element].use_last_ce_value || init_game)
1999           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2000 #endif
2001       }
2002       else if (IS_GROUP_ELEMENT(element))
2003       {
2004         Feld[x][y] = GetElementFromGroupElement(element);
2005
2006         InitField(x, y, init_game);
2007       }
2008
2009       break;
2010   }
2011
2012   if (!init_game)
2013     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2014 }
2015
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2017 {
2018   InitField(x, y, init_game);
2019
2020   /* not needed to call InitMovDir() -- already done by InitField()! */
2021   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022       CAN_MOVE(Feld[x][y]))
2023     InitMovDir(x, y);
2024 }
2025
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2027 {
2028   int old_element = Feld[x][y];
2029
2030   InitField(x, y, init_game);
2031
2032   /* not needed to call InitMovDir() -- already done by InitField()! */
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(old_element) &&
2035       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2036     InitMovDir(x, y);
2037
2038   /* this case is in fact a combination of not less than three bugs:
2039      first, it calls InitMovDir() for elements that can move, although this is
2040      already done by InitField(); then, it checks the element that was at this
2041      field _before_ the call to InitField() (which can change it); lastly, it
2042      was not called for "mole with direction" elements, which were treated as
2043      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2044   */
2045 }
2046
2047 static int get_key_element_from_nr(int key_nr)
2048 {
2049   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051                           EL_EM_KEY_1 : EL_KEY_1);
2052
2053   return key_base_element + key_nr;
2054 }
2055
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2057 {
2058   return (player->inventory_size > 0 ?
2059           player->inventory_element[player->inventory_size - 1] :
2060           player->inventory_infinite_element != EL_UNDEFINED ?
2061           player->inventory_infinite_element :
2062           player->dynabombs_left > 0 ?
2063           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2064           EL_UNDEFINED);
2065 }
2066
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2068 {
2069   /* pos >= 0: get element from bottom of the stack;
2070      pos <  0: get element from top of the stack */
2071
2072   if (pos < 0)
2073   {
2074     int min_inventory_size = -pos;
2075     int inventory_pos = player->inventory_size - min_inventory_size;
2076     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2077
2078     return (player->inventory_size >= min_inventory_size ?
2079             player->inventory_element[inventory_pos] :
2080             player->inventory_infinite_element != EL_UNDEFINED ?
2081             player->inventory_infinite_element :
2082             player->dynabombs_left >= min_dynabombs_left ?
2083             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2084             EL_UNDEFINED);
2085   }
2086   else
2087   {
2088     int min_dynabombs_left = pos + 1;
2089     int min_inventory_size = pos + 1 - player->dynabombs_left;
2090     int inventory_pos = pos - player->dynabombs_left;
2091
2092     return (player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             player->inventory_size >= min_inventory_size ?
2097             player->inventory_element[inventory_pos] :
2098             EL_UNDEFINED);
2099   }
2100 }
2101
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2103 {
2104   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2106   int compare_result;
2107
2108   if (gpo1->sort_priority != gpo2->sort_priority)
2109     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2110   else
2111     compare_result = gpo1->nr - gpo2->nr;
2112
2113   return compare_result;
2114 }
2115
2116 void InitGameControlValues()
2117 {
2118   int i;
2119
2120   for (i = 0; game_panel_controls[i].nr != -1; i++)
2121   {
2122     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124     struct TextPosInfo *pos = gpc->pos;
2125     int nr = gpc->nr;
2126     int type = gpc->type;
2127
2128     if (nr != i)
2129     {
2130       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131       Error(ERR_EXIT, "this should not happen -- please debug");
2132     }
2133
2134     /* force update of game controls after initialization */
2135     gpc->value = gpc->last_value = -1;
2136     gpc->frame = gpc->last_frame = -1;
2137     gpc->gfx_frame = -1;
2138
2139     /* determine panel value width for later calculation of alignment */
2140     if (type == TYPE_INTEGER || type == TYPE_STRING)
2141     {
2142       pos->width = pos->size * getFontWidth(pos->font);
2143       pos->height = getFontHeight(pos->font);
2144     }
2145     else if (type == TYPE_ELEMENT)
2146     {
2147       pos->width = pos->size;
2148       pos->height = pos->size;
2149     }
2150
2151     /* fill structure for game panel draw order */
2152     gpo->nr = gpc->nr;
2153     gpo->sort_priority = pos->sort_priority;
2154   }
2155
2156   /* sort game panel controls according to sort_priority and control number */
2157   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2159 }
2160
2161 void UpdatePlayfieldElementCount()
2162 {
2163   boolean use_element_count = FALSE;
2164   int i, j, x, y;
2165
2166   /* first check if it is needed at all to calculate playfield element count */
2167   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169       use_element_count = TRUE;
2170
2171   if (!use_element_count)
2172     return;
2173
2174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175     element_info[i].element_count = 0;
2176
2177   SCAN_PLAYFIELD(x, y)
2178   {
2179     element_info[Feld[x][y]].element_count++;
2180   }
2181
2182   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184       if (IS_IN_GROUP(j, i))
2185         element_info[EL_GROUP_START + i].element_count +=
2186           element_info[j].element_count;
2187 }
2188
2189 void UpdateGameControlValues()
2190 {
2191   int i, k;
2192   int time = (local_player->LevelSolved ?
2193               local_player->LevelSolved_CountingTime :
2194               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195               level.native_em_level->lev->time :
2196               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197               level.native_sp_level->game_sp->time_played :
2198               game.no_time_limit ? TimePlayed : TimeLeft);
2199   int score = (local_player->LevelSolved ?
2200                local_player->LevelSolved_CountingScore :
2201                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202                level.native_em_level->lev->score :
2203                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204                level.native_sp_level->game_sp->score :
2205                local_player->score);
2206   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207               level.native_em_level->lev->required :
2208               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209               level.native_sp_level->game_sp->infotrons_still_needed :
2210               local_player->gems_still_needed);
2211   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212                      level.native_em_level->lev->required > 0 :
2213                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215                      local_player->gems_still_needed > 0 ||
2216                      local_player->sokobanfields_still_needed > 0 ||
2217                      local_player->lights_still_needed > 0);
2218
2219   UpdatePlayfieldElementCount();
2220
2221   /* update game panel control values */
2222
2223   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2225
2226   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227   for (i = 0; i < MAX_NUM_KEYS; i++)
2228     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2231
2232   if (game.centered_player_nr == -1)
2233   {
2234     for (i = 0; i < MAX_PLAYERS; i++)
2235     {
2236       /* only one player in Supaplex game engine */
2237       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2238         break;
2239
2240       for (k = 0; k < MAX_NUM_KEYS; k++)
2241       {
2242         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2243         {
2244           if (level.native_em_level->ply[i]->keys & (1 << k))
2245             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246               get_key_element_from_nr(k);
2247         }
2248         else if (stored_player[i].key[k])
2249           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250             get_key_element_from_nr(k);
2251       }
2252
2253       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255           level.native_em_level->ply[i]->dynamite;
2256       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258           level.native_sp_level->game_sp->red_disk_count;
2259       else
2260         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261           stored_player[i].inventory_size;
2262
2263       if (stored_player[i].num_white_keys > 0)
2264         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2265           EL_DC_KEY_WHITE;
2266
2267       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268         stored_player[i].num_white_keys;
2269     }
2270   }
2271   else
2272   {
2273     int player_nr = game.centered_player_nr;
2274
2275     for (k = 0; k < MAX_NUM_KEYS; k++)
2276     {
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278       {
2279         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281             get_key_element_from_nr(k);
2282       }
2283       else if (stored_player[player_nr].key[k])
2284         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285           get_key_element_from_nr(k);
2286     }
2287
2288     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         level.native_em_level->ply[player_nr]->dynamite;
2291     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293         level.native_sp_level->game_sp->red_disk_count;
2294     else
2295       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296         stored_player[player_nr].inventory_size;
2297
2298     if (stored_player[player_nr].num_white_keys > 0)
2299       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2300
2301     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302       stored_player[player_nr].num_white_keys;
2303   }
2304
2305   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2306   {
2307     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308       get_inventory_element_from_pos(local_player, i);
2309     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310       get_inventory_element_from_pos(local_player, -i - 1);
2311   }
2312
2313   game_panel_controls[GAME_PANEL_SCORE].value = score;
2314   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2315
2316   game_panel_controls[GAME_PANEL_TIME].value = time;
2317
2318   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2321
2322   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2323
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2326      EL_EMPTY);
2327   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328     local_player->shield_normal_time_left;
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2331      EL_EMPTY);
2332   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333     local_player->shield_deadly_time_left;
2334
2335   game_panel_controls[GAME_PANEL_EXIT].value =
2336     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2337
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342      EL_EMC_MAGIC_BALL_SWITCH);
2343
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347     game.light_time_left;
2348
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352     game.timegate_time_left;
2353
2354   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2356
2357   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360     game.lenses_time_left;
2361
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365     game.magnify_time_left;
2366
2367   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2369      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2371      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2372      EL_BALLOON_SWITCH_NONE);
2373
2374   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375     local_player->dynabomb_count;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377     local_player->dynabomb_size;
2378   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2380
2381   game_panel_controls[GAME_PANEL_PENGUINS].value =
2382     local_player->friends_still_needed;
2383
2384   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385     local_player->sokobanfields_still_needed;
2386   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387     local_player->sokobanfields_still_needed;
2388
2389   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2391
2392   for (i = 0; i < NUM_BELTS; i++)
2393   {
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399   }
2400
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404     game.magic_wall_time_left;
2405
2406 #if USE_PLAYER_GRAVITY
2407   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408     local_player->gravity;
2409 #else
2410   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2411 #endif
2412
2413   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419        game.panel.element[i].id : EL_UNDEFINED);
2420
2421   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424        element_info[game.panel.element_count[i].id].element_count : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429        element_info[game.panel.ce_score[i].id].collect_score : 0);
2430
2431   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434        element_info[game.panel.ce_score_element[i].id].collect_score :
2435        EL_UNDEFINED);
2436
2437   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2440
2441   /* update game panel control frames */
2442
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444   {
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2446
2447     if (gpc->type == TYPE_ELEMENT)
2448     {
2449       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2450       {
2451         int last_anim_random_frame = gfx.anim_random_frame;
2452         int element = gpc->value;
2453         int graphic = el2panelimg(element);
2454
2455         if (gpc->value != gpc->last_value)
2456         {
2457           gpc->gfx_frame = 0;
2458           gpc->gfx_random = INIT_GFX_RANDOM();
2459         }
2460         else
2461         {
2462           gpc->gfx_frame++;
2463
2464           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466             gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468
2469         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470           gfx.anim_random_frame = gpc->gfx_random;
2471
2472         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473           gpc->gfx_frame = element_info[element].collect_score;
2474
2475         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2476                                               gpc->gfx_frame);
2477
2478         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479           gfx.anim_random_frame = last_anim_random_frame;
2480       }
2481     }
2482   }
2483 }
2484
2485 void DisplayGameControlValues()
2486 {
2487   boolean redraw_panel = FALSE;
2488   int i;
2489
2490   for (i = 0; game_panel_controls[i].nr != -1; i++)
2491   {
2492     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2493
2494     if (PANEL_DEACTIVATED(gpc->pos))
2495       continue;
2496
2497     if (gpc->value == gpc->last_value &&
2498         gpc->frame == gpc->last_frame)
2499       continue;
2500
2501     redraw_panel = TRUE;
2502   }
2503
2504   if (!redraw_panel)
2505     return;
2506
2507   /* copy default game door content to main double buffer */
2508 #if 1
2509   /* !!! CHECK AGAIN !!! */
2510   SetPanelBackground();
2511   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2513 #else
2514   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516 #endif
2517
2518   /* redraw game control buttons */
2519 #if 1
2520   RedrawGameButtons();
2521 #else
2522   UnmapGameButtons();
2523   MapGameButtons();
2524 #endif
2525
2526   game_status = GAME_MODE_PSEUDO_PANEL;
2527
2528 #if 1
2529   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2530 #else
2531   for (i = 0; game_panel_controls[i].nr != -1; i++)
2532 #endif
2533   {
2534 #if 1
2535     int nr = game_panel_order[i].nr;
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2537 #else
2538     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2539     int nr = gpc->nr;
2540 #endif
2541     struct TextPosInfo *pos = gpc->pos;
2542     int type = gpc->type;
2543     int value = gpc->value;
2544     int frame = gpc->frame;
2545 #if 0
2546     int last_value = gpc->last_value;
2547     int last_frame = gpc->last_frame;
2548 #endif
2549     int size = pos->size;
2550     int font = pos->font;
2551     boolean draw_masked = pos->draw_masked;
2552     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2553
2554     if (PANEL_DEACTIVATED(pos))
2555       continue;
2556
2557 #if 0
2558     if (value == last_value && frame == last_frame)
2559       continue;
2560 #endif
2561
2562     gpc->last_value = value;
2563     gpc->last_frame = frame;
2564
2565 #if 0
2566     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2567 #endif
2568
2569     if (type == TYPE_INTEGER)
2570     {
2571       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572           nr == GAME_PANEL_TIME)
2573       {
2574         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2575
2576         if (use_dynamic_size)           /* use dynamic number of digits */
2577         {
2578           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580           int size2 = size1 + 1;
2581           int font1 = pos->font;
2582           int font2 = pos->font_alt;
2583
2584           size = (value < value_change ? size1 : size2);
2585           font = (value < value_change ? font1 : font2);
2586
2587 #if 0
2588           /* clear background if value just changed its size (dynamic digits) */
2589           if ((last_value < value_change) != (value < value_change))
2590           {
2591             int width1 = size1 * getFontWidth(font1);
2592             int width2 = size2 * getFontWidth(font2);
2593             int max_width = MAX(width1, width2);
2594             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2595
2596             pos->width = max_width;
2597
2598             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599                                        max_width, max_height);
2600           }
2601 #endif
2602         }
2603       }
2604
2605 #if 1
2606       /* correct text size if "digits" is zero or less */
2607       if (size <= 0)
2608         size = strlen(int2str(value, size));
2609
2610       /* dynamically correct text alignment */
2611       pos->width = size * getFontWidth(font);
2612 #endif
2613
2614       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615                   int2str(value, size), font, mask_mode);
2616     }
2617     else if (type == TYPE_ELEMENT)
2618     {
2619       int element, graphic;
2620       Bitmap *src_bitmap;
2621       int src_x, src_y;
2622       int width, height;
2623       int dst_x = PANEL_XPOS(pos);
2624       int dst_y = PANEL_YPOS(pos);
2625
2626 #if 1
2627       if (value != EL_UNDEFINED && value != EL_EMPTY)
2628       {
2629         element = value;
2630         graphic = el2panelimg(value);
2631
2632         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2633
2634 #if 1
2635         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2636           size = TILESIZE;
2637 #endif
2638
2639         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2640                               &src_x, &src_y);
2641
2642         width  = graphic_info[graphic].width  * size / TILESIZE;
2643         height = graphic_info[graphic].height * size / TILESIZE;
2644
2645         if (draw_masked)
2646         {
2647           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648                         dst_x - src_x, dst_y - src_y);
2649           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2650                            dst_x, dst_y);
2651         }
2652         else
2653         {
2654           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2655                      dst_x, dst_y);
2656         }
2657       }
2658 #else
2659       if (value == EL_UNDEFINED || value == EL_EMPTY)
2660       {
2661         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662         graphic = el2panelimg(element);
2663
2664         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2667       }
2668       else
2669       {
2670         element = value;
2671         graphic = el2panelimg(value);
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2674       }
2675
2676       width  = graphic_info[graphic].width  * size / TILESIZE;
2677       height = graphic_info[graphic].height * size / TILESIZE;
2678
2679       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2680 #endif
2681     }
2682     else if (type == TYPE_STRING)
2683     {
2684       boolean active = (value != 0);
2685       char *state_normal = "off";
2686       char *state_active = "on";
2687       char *state = (active ? state_active : state_normal);
2688       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2690                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2691                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2692
2693       if (nr == GAME_PANEL_GRAVITY_STATE)
2694       {
2695         int font1 = pos->font;          /* (used for normal state) */
2696         int font2 = pos->font_alt;      /* (used for active state) */
2697 #if 0
2698         int size1 = strlen(state_normal);
2699         int size2 = strlen(state_active);
2700         int width1 = size1 * getFontWidth(font1);
2701         int width2 = size2 * getFontWidth(font2);
2702         int max_width = MAX(width1, width2);
2703         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2704
2705         pos->width = max_width;
2706
2707         /* clear background for values that may have changed its size */
2708         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                                    max_width, max_height);
2710 #endif
2711
2712         font = (active ? font2 : font1);
2713       }
2714
2715       if (s != NULL)
2716       {
2717         char *s_cut;
2718
2719 #if 1
2720         if (size <= 0)
2721         {
2722           /* don't truncate output if "chars" is zero or less */
2723           size = strlen(s);
2724
2725           /* dynamically correct text alignment */
2726           pos->width = size * getFontWidth(font);
2727         }
2728 #endif
2729
2730         s_cut = getStringCopyN(s, size);
2731
2732         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733                     s_cut, font, mask_mode);
2734
2735         free(s_cut);
2736       }
2737     }
2738
2739     redraw_mask |= REDRAW_DOOR_1;
2740   }
2741
2742   game_status = GAME_MODE_PLAYING;
2743 }
2744
2745 void UpdateAndDisplayGameControlValues()
2746 {
2747   if (tape.warp_forward)
2748     return;
2749
2750   UpdateGameControlValues();
2751   DisplayGameControlValues();
2752 }
2753
2754 void DrawGameValue_Emeralds(int value)
2755 {
2756   struct TextPosInfo *pos = &game.panel.gems;
2757   int font_nr = pos->font;
2758   int font_width = getFontWidth(font_nr);
2759   int chars = pos->size;
2760
2761 #if 1
2762   return;       /* !!! USE NEW STUFF !!! */
2763 #endif
2764
2765   if (PANEL_DEACTIVATED(pos))
2766     return;
2767
2768   pos->width = chars * font_width;
2769
2770   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2771 }
2772
2773 void DrawGameValue_Dynamite(int value)
2774 {
2775   struct TextPosInfo *pos = &game.panel.inventory_count;
2776   int font_nr = pos->font;
2777   int font_width = getFontWidth(font_nr);
2778   int chars = pos->size;
2779
2780 #if 1
2781   return;       /* !!! USE NEW STUFF !!! */
2782 #endif
2783
2784   if (PANEL_DEACTIVATED(pos))
2785     return;
2786
2787   pos->width = chars * font_width;
2788
2789   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2790 }
2791
2792 void DrawGameValue_Score(int value)
2793 {
2794   struct TextPosInfo *pos = &game.panel.score;
2795   int font_nr = pos->font;
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Time(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.time;
2814   static int last_value = -1;
2815   int chars1 = 3;
2816   int chars2 = 4;
2817   int chars = pos->size;
2818   int font1_nr = pos->font;
2819   int font2_nr = pos->font_alt;
2820   int font_nr = font1_nr;
2821   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822
2823 #if 1
2824   return;       /* !!! USE NEW STUFF !!! */
2825 #endif
2826
2827   if (PANEL_DEACTIVATED(pos))
2828     return;
2829
2830   if (use_dynamic_chars)                /* use dynamic number of chars */
2831   {
2832     chars   = (value < 1000 ? chars1   : chars2);
2833     font_nr = (value < 1000 ? font1_nr : font2_nr);
2834   }
2835
2836   /* clear background if value just changed its size (dynamic chars only) */
2837   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838   {
2839     int width1 = chars1 * getFontWidth(font1_nr);
2840     int width2 = chars2 * getFontWidth(font2_nr);
2841     int max_width = MAX(width1, width2);
2842     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843
2844     pos->width = max_width;
2845
2846     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847                                max_width, max_height);
2848   }
2849
2850   pos->width = chars * getFontWidth(font_nr);
2851
2852   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2853
2854   last_value = value;
2855 }
2856
2857 void DrawGameValue_Level(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.level_number;
2860   int chars1 = 2;
2861   int chars2 = 3;
2862   int chars = pos->size;
2863   int font1_nr = pos->font;
2864   int font2_nr = pos->font_alt;
2865   int font_nr = font1_nr;
2866   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2867
2868 #if 1
2869   return;       /* !!! USE NEW STUFF !!! */
2870 #endif
2871
2872   if (PANEL_DEACTIVATED(pos))
2873     return;
2874
2875   if (use_dynamic_chars)                /* use dynamic number of chars */
2876   {
2877     chars   = (level_nr < 100 ? chars1   : chars2);
2878     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2879   }
2880
2881   pos->width = chars * getFontWidth(font_nr);
2882
2883   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2884 }
2885
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2887 {
2888   int i;
2889
2890 #if 1
2891   return;       /* !!! USE NEW STUFF !!! */
2892 #endif
2893
2894   for (i = 0; i < MAX_NUM_KEYS; i++)
2895   {
2896     struct TextPosInfo *pos = &game.panel.key[i];
2897     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898     int src_y = DOOR_GFX_PAGEY1 + 123;
2899     int dst_x = PANEL_XPOS(pos);
2900     int dst_y = PANEL_YPOS(pos);
2901
2902     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2904                    EL_KEY_1) + i;
2905     int graphic = el2edimg(element);
2906
2907     if (PANEL_DEACTIVATED(pos))
2908       continue;
2909
2910 #if 0
2911     /* masked blit with tiles from half-size scaled bitmap does not work yet
2912        (no mask bitmap created for these sizes after loading and scaling) --
2913        solution: load without creating mask, scale, then create final mask */
2914
2915     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2917
2918     if (key[i])
2919     {
2920       Bitmap *src_bitmap;
2921       int src_x, src_y;
2922
2923       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2924
2925       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926                     dst_x - src_x, dst_y - src_y);
2927       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2928                        dst_x, dst_y);
2929     }
2930 #else
2931     if (key[i])
2932       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2933     else
2934       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2936 #endif
2937   }
2938 }
2939
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2941                        int key_bits)
2942 {
2943   int key[MAX_NUM_KEYS];
2944   int i;
2945
2946   /* prevent EM engine from updating time/score values parallel to GameWon() */
2947   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948       local_player->LevelSolved)
2949     return;
2950
2951   for (i = 0; i < MAX_NUM_KEYS; i++)
2952     key[i] = key_bits & (1 << i);
2953
2954   DrawGameValue_Level(level_nr);
2955
2956   DrawGameValue_Emeralds(emeralds);
2957   DrawGameValue_Dynamite(dynamite);
2958   DrawGameValue_Score(score);
2959   DrawGameValue_Time(time);
2960
2961   DrawGameValue_Keys(key);
2962 }
2963
2964 void UpdateGameDoorValues()
2965 {
2966   UpdateGameControlValues();
2967 }
2968
2969 void DrawGameDoorValues()
2970 {
2971   DisplayGameControlValues();
2972 }
2973
2974 void DrawGameDoorValues_OLD()
2975 {
2976   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977   int dynamite_value = 0;
2978   int score_value = (local_player->LevelSolved ? local_player->score_final :
2979                      local_player->score);
2980   int gems_value = local_player->gems_still_needed;
2981   int key_bits = 0;
2982   int i, j;
2983
2984   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2985   {
2986     DrawGameDoorValues_EM();
2987
2988     return;
2989   }
2990
2991   if (game.centered_player_nr == -1)
2992   {
2993     for (i = 0; i < MAX_PLAYERS; i++)
2994     {
2995       for (j = 0; j < MAX_NUM_KEYS; j++)
2996         if (stored_player[i].key[j])
2997           key_bits |= (1 << j);
2998
2999       dynamite_value += stored_player[i].inventory_size;
3000     }
3001   }
3002   else
3003   {
3004     int player_nr = game.centered_player_nr;
3005
3006     for (i = 0; i < MAX_NUM_KEYS; i++)
3007       if (stored_player[player_nr].key[i])
3008         key_bits |= (1 << i);
3009
3010     dynamite_value = stored_player[player_nr].inventory_size;
3011   }
3012
3013   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3014                     key_bits);
3015 }
3016
3017
3018 /*
3019   =============================================================================
3020   InitGameEngine()
3021   -----------------------------------------------------------------------------
3022   initialize game engine due to level / tape version number
3023   =============================================================================
3024 */
3025
3026 static void InitGameEngine()
3027 {
3028   int i, j, k, l, x, y;
3029
3030   /* set game engine from tape file when re-playing, else from level file */
3031   game.engine_version = (tape.playing ? tape.engine_version :
3032                          level.game_version);
3033
3034   /* set single or multi-player game mode (needed for re-playing tapes) */
3035   game.team_mode = setup.team_mode;
3036
3037   if (tape.playing)
3038   {
3039     int num_players = 0;
3040
3041     for (i = 0; i < MAX_PLAYERS; i++)
3042       if (tape.player_participates[i])
3043         num_players++;
3044
3045     /* multi-player tapes contain input data for more than one player */
3046     game.team_mode = (num_players > 1);
3047   }
3048
3049   /* ---------------------------------------------------------------------- */
3050   /* set flags for bugs and changes according to active game engine version */
3051   /* ---------------------------------------------------------------------- */
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for custom elements that change when pushed by the player.
3056
3057     Fixed/changed in version:
3058     3.1.0
3059
3060     Description:
3061     Before 3.1.0, custom elements that "change when pushing" changed directly
3062     after the player started pushing them (until then handled in "DigField()").
3063     Since 3.1.0, these custom elements are not changed until the "pushing"
3064     move of the element is finished (now handled in "ContinueMoving()").
3065
3066     Affected levels/tapes:
3067     The first condition is generally needed for all levels/tapes before version
3068     3.1.0, which might use the old behaviour before it was changed; known tapes
3069     that are affected are some tapes from the level set "Walpurgis Gardens" by
3070     Jamie Cullen.
3071     The second condition is an exception from the above case and is needed for
3072     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073     above (including some development versions of 3.1.0), but before it was
3074     known that this change would break tapes like the above and was fixed in
3075     3.1.1, so that the changed behaviour was active although the engine version
3076     while recording maybe was before 3.1.0. There is at least one tape that is
3077     affected by this exception, which is the tape for the one-level set "Bug
3078     Machine" by Juergen Bonhagen.
3079   */
3080
3081   game.use_change_when_pushing_bug =
3082     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3083      !(tape.playing &&
3084        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3086
3087   /*
3088     Summary of bugfix/change:
3089     Fixed handling for blocking the field the player leaves when moving.
3090
3091     Fixed/changed in version:
3092     3.1.1
3093
3094     Description:
3095     Before 3.1.1, when "block last field when moving" was enabled, the field
3096     the player is leaving when moving was blocked for the time of the move,
3097     and was directly unblocked afterwards. This resulted in the last field
3098     being blocked for exactly one less than the number of frames of one player
3099     move. Additionally, even when blocking was disabled, the last field was
3100     blocked for exactly one frame.
3101     Since 3.1.1, due to changes in player movement handling, the last field
3102     is not blocked at all when blocking is disabled. When blocking is enabled,
3103     the last field is blocked for exactly the number of frames of one player
3104     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105     last field is blocked for exactly one more than the number of frames of
3106     one player move.
3107
3108     Affected levels/tapes:
3109     (!!! yet to be determined -- probably many !!!)
3110   */
3111
3112   game.use_block_last_field_bug =
3113     (game.engine_version < VERSION_IDENT(3,1,1,0));
3114
3115   /*
3116     Summary of bugfix/change:
3117     Changed behaviour of CE changes with multiple changes per single frame.
3118
3119     Fixed/changed in version:
3120     3.2.0-6
3121
3122     Description:
3123     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124     This resulted in race conditions where CEs seem to behave strange in some
3125     situations (where triggered CE changes were just skipped because there was
3126     already a CE change on that tile in the playfield in that engine frame).
3127     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128     (The number of changes per frame must be limited in any case, because else
3129     it is easily possible to define CE changes that would result in an infinite
3130     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131     should be set large enough so that it would only be reached in cases where
3132     the corresponding CE change conditions run into a loop. Therefore, it seems
3133     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134     maximal number of change pages for custom elements.)
3135
3136     Affected levels/tapes:
3137     Probably many.
3138   */
3139
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141   game.max_num_changes_per_frame = 1;
3142 #else
3143   game.max_num_changes_per_frame =
3144     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3145 #endif
3146
3147   /* ---------------------------------------------------------------------- */
3148
3149   /* default scan direction: scan playfield from top/left to bottom/right */
3150   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3151
3152   /* dynamically adjust element properties according to game engine version */
3153   InitElementPropertiesEngine(game.engine_version);
3154
3155 #if 0
3156   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157   printf("          tape version == %06d [%s] [file: %06d]\n",
3158          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3159          tape.file_version);
3160   printf("       => game.engine_version == %06d\n", game.engine_version);
3161 #endif
3162
3163   /* ---------- initialize player's initial move delay --------------------- */
3164
3165   /* dynamically adjust player properties according to level information */
3166   for (i = 0; i < MAX_PLAYERS; i++)
3167     game.initial_move_delay_value[i] =
3168       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3169
3170   /* dynamically adjust player properties according to game engine version */
3171   for (i = 0; i < MAX_PLAYERS; i++)
3172     game.initial_move_delay[i] =
3173       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174        game.initial_move_delay_value[i] : 0);
3175
3176   /* ---------- initialize player's initial push delay --------------------- */
3177
3178   /* dynamically adjust player properties according to game engine version */
3179   game.initial_push_delay_value =
3180     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3181
3182   /* ---------- initialize changing elements ------------------------------- */
3183
3184   /* initialize changing elements information */
3185   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3186   {
3187     struct ElementInfo *ei = &element_info[i];
3188
3189     /* this pointer might have been changed in the level editor */
3190     ei->change = &ei->change_page[0];
3191
3192     if (!IS_CUSTOM_ELEMENT(i))
3193     {
3194       ei->change->target_element = EL_EMPTY_SPACE;
3195       ei->change->delay_fixed = 0;
3196       ei->change->delay_random = 0;
3197       ei->change->delay_frames = 1;
3198     }
3199
3200     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3201     {
3202       ei->has_change_event[j] = FALSE;
3203
3204       ei->event_page_nr[j] = 0;
3205       ei->event_page[j] = &ei->change_page[0];
3206     }
3207   }
3208
3209   /* add changing elements from pre-defined list */
3210   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3211   {
3212     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213     struct ElementInfo *ei = &element_info[ch_delay->element];
3214
3215     ei->change->target_element       = ch_delay->target_element;
3216     ei->change->delay_fixed          = ch_delay->change_delay;
3217
3218     ei->change->pre_change_function  = ch_delay->pre_change_function;
3219     ei->change->change_function      = ch_delay->change_function;
3220     ei->change->post_change_function = ch_delay->post_change_function;
3221
3222     ei->change->can_change = TRUE;
3223     ei->change->can_change_or_has_action = TRUE;
3224
3225     ei->has_change_event[CE_DELAY] = TRUE;
3226
3227     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3229   }
3230
3231   /* ---------- initialize internal run-time variables --------------------- */
3232
3233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3234   {
3235     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3236
3237     for (j = 0; j < ei->num_change_pages; j++)
3238     {
3239       ei->change_page[j].can_change_or_has_action =
3240         (ei->change_page[j].can_change |
3241          ei->change_page[j].has_action);
3242     }
3243   }
3244
3245   /* add change events from custom element configuration */
3246   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3249
3250     for (j = 0; j < ei->num_change_pages; j++)
3251     {
3252       if (!ei->change_page[j].can_change_or_has_action)
3253         continue;
3254
3255       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3256       {
3257         /* only add event page for the first page found with this event */
3258         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3259         {
3260           ei->has_change_event[k] = TRUE;
3261
3262           ei->event_page_nr[k] = j;
3263           ei->event_page[k] = &ei->change_page[j];
3264         }
3265       }
3266     }
3267   }
3268
3269 #if 1
3270   /* ---------- initialize reference elements in change conditions --------- */
3271
3272   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3273   {
3274     int element = EL_CUSTOM_START + i;
3275     struct ElementInfo *ei = &element_info[element];
3276
3277     for (j = 0; j < ei->num_change_pages; j++)
3278     {
3279       int trigger_element = ei->change_page[j].initial_trigger_element;
3280
3281       if (trigger_element >= EL_PREV_CE_8 &&
3282           trigger_element <= EL_NEXT_CE_8)
3283         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3284
3285       ei->change_page[j].trigger_element = trigger_element;
3286     }
3287   }
3288 #endif
3289
3290   /* ---------- initialize run-time trigger player and element ------------- */
3291
3292   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3293   {
3294     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302       ei->change_page[j].actual_trigger_ce_value = 0;
3303       ei->change_page[j].actual_trigger_ce_score = 0;
3304     }
3305   }
3306
3307   /* ---------- initialize trigger events ---------------------------------- */
3308
3309   /* initialize trigger events information */
3310   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312       trigger_events[i][j] = FALSE;
3313
3314   /* add trigger events from element change event properties */
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     struct ElementInfo *ei = &element_info[i];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       if (!ei->change_page[j].can_change_or_has_action)
3322         continue;
3323
3324       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3325       {
3326         int trigger_element = ei->change_page[j].trigger_element;
3327
3328         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3329         {
3330           if (ei->change_page[j].has_event[k])
3331           {
3332             if (IS_GROUP_ELEMENT(trigger_element))
3333             {
3334               struct ElementGroupInfo *group =
3335                 element_info[trigger_element].group;
3336
3337               for (l = 0; l < group->num_elements_resolved; l++)
3338                 trigger_events[group->element_resolved[l]][k] = TRUE;
3339             }
3340             else if (trigger_element == EL_ANY_ELEMENT)
3341               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342                 trigger_events[l][k] = TRUE;
3343             else
3344               trigger_events[trigger_element][k] = TRUE;
3345           }
3346         }
3347       }
3348     }
3349   }
3350
3351   /* ---------- initialize push delay -------------------------------------- */
3352
3353   /* initialize push delay values to default */
3354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355   {
3356     if (!IS_CUSTOM_ELEMENT(i))
3357     {
3358       /* set default push delay values (corrected since version 3.0.7-1) */
3359       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3360       {
3361         element_info[i].push_delay_fixed = 2;
3362         element_info[i].push_delay_random = 8;
3363       }
3364       else
3365       {
3366         element_info[i].push_delay_fixed = 8;
3367         element_info[i].push_delay_random = 8;
3368       }
3369     }
3370   }
3371
3372   /* set push delay value for certain elements from pre-defined list */
3373   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3374   {
3375     int e = push_delay_list[i].element;
3376
3377     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3378     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3379   }
3380
3381   /* set push delay value for Supaplex elements for newer engine versions */
3382   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3383   {
3384     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3385     {
3386       if (IS_SP_ELEMENT(i))
3387       {
3388         /* set SP push delay to just enough to push under a falling zonk */
3389         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3390
3391         element_info[i].push_delay_fixed  = delay;
3392         element_info[i].push_delay_random = 0;
3393       }
3394     }
3395   }
3396
3397   /* ---------- initialize move stepsize ----------------------------------- */
3398
3399   /* initialize move stepsize values to default */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401     if (!IS_CUSTOM_ELEMENT(i))
3402       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3403
3404   /* set move stepsize value for certain elements from pre-defined list */
3405   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3406   {
3407     int e = move_stepsize_list[i].element;
3408
3409     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3410   }
3411
3412   /* ---------- initialize collect score ----------------------------------- */
3413
3414   /* initialize collect score values for custom elements from initial value */
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416     if (IS_CUSTOM_ELEMENT(i))
3417       element_info[i].collect_score = element_info[i].collect_score_initial;
3418
3419   /* ---------- initialize collect count ----------------------------------- */
3420
3421   /* initialize collect count values for non-custom elements */
3422   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423     if (!IS_CUSTOM_ELEMENT(i))
3424       element_info[i].collect_count_initial = 0;
3425
3426   /* add collect count values for all elements from pre-defined list */
3427   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428     element_info[collect_count_list[i].element].collect_count_initial =
3429       collect_count_list[i].count;
3430
3431   /* ---------- initialize access direction -------------------------------- */
3432
3433   /* initialize access direction values to default (access from every side) */
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435     if (!IS_CUSTOM_ELEMENT(i))
3436       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3437
3438   /* set access direction value for certain elements from pre-defined list */
3439   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440     element_info[access_direction_list[i].element].access_direction =
3441       access_direction_list[i].direction;
3442
3443   /* ---------- initialize explosion content ------------------------------- */
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445   {
3446     if (IS_CUSTOM_ELEMENT(i))
3447       continue;
3448
3449     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3450     {
3451       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3452
3453       element_info[i].content.e[x][y] =
3454         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456          i == EL_PLAYER_3 ? EL_EMERALD :
3457          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458          i == EL_MOLE ? EL_EMERALD_RED :
3459          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464          i == EL_WALL_EMERALD ? EL_EMERALD :
3465          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470          i == EL_WALL_PEARL ? EL_PEARL :
3471          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3472          EL_EMPTY);
3473     }
3474   }
3475
3476   /* ---------- initialize recursion detection ------------------------------ */
3477   recursion_loop_depth = 0;
3478   recursion_loop_detected = FALSE;
3479   recursion_loop_element = EL_UNDEFINED;
3480
3481   /* ---------- initialize graphics engine ---------------------------------- */
3482   game.scroll_delay_value =
3483     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3485   game.scroll_delay_value =
3486     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3487 }
3488
3489 int get_num_special_action(int element, int action_first, int action_last)
3490 {
3491   int num_special_action = 0;
3492   int i, j;
3493
3494   for (i = action_first; i <= action_last; i++)
3495   {
3496     boolean found = FALSE;
3497
3498     for (j = 0; j < NUM_DIRECTIONS; j++)
3499       if (el_act_dir2img(element, i, j) !=
3500           el_act_dir2img(element, ACTION_DEFAULT, j))
3501         found = TRUE;
3502
3503     if (found)
3504       num_special_action++;
3505     else
3506       break;
3507   }
3508
3509   return num_special_action;
3510 }
3511
3512
3513 /*
3514   =============================================================================
3515   InitGame()
3516   -----------------------------------------------------------------------------
3517   initialize and start new game
3518   =============================================================================
3519 */
3520
3521 void InitGame()
3522 {
3523   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3524   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3525
3526   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3527   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3528   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3529 #if 0
3530   boolean do_fading = (game_status == GAME_MODE_MAIN);
3531 #endif
3532 #if 1
3533   int initial_move_dir = MV_DOWN;
3534 #else
3535   int initial_move_dir = MV_NONE;
3536 #endif
3537   int i, j, x, y;
3538
3539 #if 1
3540   game_status = GAME_MODE_PLAYING;
3541 #endif
3542
3543 #if 1
3544
3545   StopAnimation();
3546
3547   if (!game.restart_level)
3548     CloseDoor(DOOR_CLOSE_1);
3549
3550 #if 1
3551   if (level_editor_test_game)
3552     FadeSkipNextFadeIn();
3553   else
3554     FadeSetEnterScreen();
3555 #else
3556   if (level_editor_test_game)
3557     fading = fading_none;
3558   else
3559     fading = menu.destination;
3560 #endif
3561
3562 #if 1
3563   FadeOut(REDRAW_FIELD);
3564 #else
3565   if (do_fading)
3566     FadeOut(REDRAW_FIELD);
3567 #endif
3568
3569 #endif
3570
3571 #if 0
3572   printf("::: FADING OUT: DONE\n");
3573   Delay(1000);
3574 #endif
3575
3576 #if 0
3577   game_status = GAME_MODE_PLAYING;
3578 #endif
3579
3580 #if 1
3581   /* needed if different viewport properties defined for playing */
3582   ChangeViewportPropertiesIfNeeded();
3583 #endif
3584
3585 #if 1
3586   DrawCompleteVideoDisplay();
3587 #endif
3588
3589   InitGameEngine();
3590   InitGameControlValues();
3591
3592   /* don't play tapes over network */
3593   network_playing = (options.network && !tape.playing);
3594
3595   for (i = 0; i < MAX_PLAYERS; i++)
3596   {
3597     struct PlayerInfo *player = &stored_player[i];
3598
3599     player->index_nr = i;
3600     player->index_bit = (1 << i);
3601     player->element_nr = EL_PLAYER_1 + i;
3602
3603     player->present = FALSE;
3604     player->active = FALSE;
3605     player->mapped = FALSE;
3606
3607     player->killed = FALSE;
3608     player->reanimated = FALSE;
3609
3610     player->action = 0;
3611     player->effective_action = 0;
3612     player->programmed_action = 0;
3613
3614     player->score = 0;
3615     player->score_final = 0;
3616
3617     player->gems_still_needed = level.gems_needed;
3618     player->sokobanfields_still_needed = 0;
3619     player->lights_still_needed = 0;
3620     player->friends_still_needed = 0;
3621
3622     for (j = 0; j < MAX_NUM_KEYS; j++)
3623       player->key[j] = FALSE;
3624
3625     player->num_white_keys = 0;
3626
3627     player->dynabomb_count = 0;
3628     player->dynabomb_size = 1;
3629     player->dynabombs_left = 0;
3630     player->dynabomb_xl = FALSE;
3631
3632     player->MovDir = initial_move_dir;
3633     player->MovPos = 0;
3634     player->GfxPos = 0;
3635     player->GfxDir = initial_move_dir;
3636     player->GfxAction = ACTION_DEFAULT;
3637     player->Frame = 0;
3638     player->StepFrame = 0;
3639
3640     player->initial_element = player->element_nr;
3641     player->artwork_element =
3642       (level.use_artwork_element[i] ? level.artwork_element[i] :
3643        player->element_nr);
3644     player->use_murphy = FALSE;
3645
3646     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3647     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3648
3649     player->gravity = level.initial_player_gravity[i];
3650
3651     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3652
3653     player->actual_frame_counter = 0;
3654
3655     player->step_counter = 0;
3656
3657     player->last_move_dir = initial_move_dir;
3658
3659     player->is_active = FALSE;
3660
3661     player->is_waiting = FALSE;
3662     player->is_moving = FALSE;
3663     player->is_auto_moving = FALSE;
3664     player->is_digging = FALSE;
3665     player->is_snapping = FALSE;
3666     player->is_collecting = FALSE;
3667     player->is_pushing = FALSE;
3668     player->is_switching = FALSE;
3669     player->is_dropping = FALSE;
3670     player->is_dropping_pressed = FALSE;
3671
3672     player->is_bored = FALSE;
3673     player->is_sleeping = FALSE;
3674
3675     player->frame_counter_bored = -1;
3676     player->frame_counter_sleeping = -1;
3677
3678     player->anim_delay_counter = 0;
3679     player->post_delay_counter = 0;
3680
3681     player->dir_waiting = initial_move_dir;
3682     player->action_waiting = ACTION_DEFAULT;
3683     player->last_action_waiting = ACTION_DEFAULT;
3684     player->special_action_bored = ACTION_DEFAULT;
3685     player->special_action_sleeping = ACTION_DEFAULT;
3686
3687     player->switch_x = -1;
3688     player->switch_y = -1;
3689
3690     player->drop_x = -1;
3691     player->drop_y = -1;
3692
3693     player->show_envelope = 0;
3694
3695     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3696
3697     player->push_delay       = -1;      /* initialized when pushing starts */
3698     player->push_delay_value = game.initial_push_delay_value;
3699
3700     player->drop_delay = 0;
3701     player->drop_pressed_delay = 0;
3702
3703     player->last_jx = -1;
3704     player->last_jy = -1;
3705     player->jx = -1;
3706     player->jy = -1;
3707
3708     player->shield_normal_time_left = 0;
3709     player->shield_deadly_time_left = 0;
3710
3711     player->inventory_infinite_element = EL_UNDEFINED;
3712     player->inventory_size = 0;
3713
3714     if (level.use_initial_inventory[i])
3715     {
3716       for (j = 0; j < level.initial_inventory_size[i]; j++)
3717       {
3718         int element = level.initial_inventory_content[i][j];
3719         int collect_count = element_info[element].collect_count_initial;
3720         int k;
3721
3722         if (!IS_CUSTOM_ELEMENT(element))
3723           collect_count = 1;
3724
3725         if (collect_count == 0)
3726           player->inventory_infinite_element = element;
3727         else
3728           for (k = 0; k < collect_count; k++)
3729             if (player->inventory_size < MAX_INVENTORY_SIZE)
3730               player->inventory_element[player->inventory_size++] = element;
3731       }
3732     }
3733
3734     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3735     SnapField(player, 0, 0);
3736
3737     player->LevelSolved = FALSE;
3738     player->GameOver = FALSE;
3739
3740     player->LevelSolved_GameWon = FALSE;
3741     player->LevelSolved_GameEnd = FALSE;
3742     player->LevelSolved_PanelOff = FALSE;
3743     player->LevelSolved_SaveTape = FALSE;
3744     player->LevelSolved_SaveScore = FALSE;
3745     player->LevelSolved_CountingTime = 0;
3746     player->LevelSolved_CountingScore = 0;
3747
3748     map_player_action[i] = i;
3749   }
3750
3751   network_player_action_received = FALSE;
3752
3753 #if defined(NETWORK_AVALIABLE)
3754   /* initial null action */
3755   if (network_playing)
3756     SendToServer_MovePlayer(MV_NONE);
3757 #endif
3758
3759   ZX = ZY = -1;
3760   ExitX = ExitY = -1;
3761
3762   FrameCounter = 0;
3763   TimeFrames = 0;
3764   TimePlayed = 0;
3765   TimeLeft = level.time;
3766   TapeTime = 0;
3767
3768   ScreenMovDir = MV_NONE;
3769   ScreenMovPos = 0;
3770   ScreenGfxPos = 0;
3771
3772   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3773
3774   AllPlayersGone = FALSE;
3775
3776   game.no_time_limit = (level.time == 0);
3777
3778   game.yamyam_content_nr = 0;
3779   game.robot_wheel_active = FALSE;
3780   game.magic_wall_active = FALSE;
3781   game.magic_wall_time_left = 0;
3782   game.light_time_left = 0;
3783   game.timegate_time_left = 0;
3784   game.switchgate_pos = 0;
3785   game.wind_direction = level.wind_direction_initial;
3786
3787 #if !USE_PLAYER_GRAVITY
3788   game.gravity = FALSE;
3789   game.explosions_delayed = TRUE;
3790 #endif
3791
3792   game.lenses_time_left = 0;
3793   game.magnify_time_left = 0;
3794
3795   game.ball_state = level.ball_state_initial;
3796   game.ball_content_nr = 0;
3797
3798   game.envelope_active = FALSE;
3799
3800   /* set focus to local player for network games, else to all players */
3801   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3802   game.centered_player_nr_next = game.centered_player_nr;
3803   game.set_centered_player = FALSE;
3804
3805   if (network_playing && tape.recording)
3806   {
3807     /* store client dependent player focus when recording network games */
3808     tape.centered_player_nr_next = game.centered_player_nr_next;
3809     tape.set_centered_player = TRUE;
3810   }
3811
3812   for (i = 0; i < NUM_BELTS; i++)
3813   {
3814     game.belt_dir[i] = MV_NONE;
3815     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3816   }
3817
3818   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3819     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3820
3821 #if DEBUG_INIT_PLAYER
3822   if (options.debug)
3823   {
3824     printf("Player status at level initialization:\n");
3825   }
3826 #endif
3827
3828   SCAN_PLAYFIELD(x, y)
3829   {
3830     Feld[x][y] = level.field[x][y];
3831     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3832     ChangeDelay[x][y] = 0;
3833     ChangePage[x][y] = -1;
3834 #if USE_NEW_CUSTOM_VALUE
3835     CustomValue[x][y] = 0;              /* initialized in InitField() */
3836 #endif
3837     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3838     AmoebaNr[x][y] = 0;
3839     WasJustMoving[x][y] = 0;
3840     WasJustFalling[x][y] = 0;
3841     CheckCollision[x][y] = 0;
3842     CheckImpact[x][y] = 0;
3843     Stop[x][y] = FALSE;
3844     Pushed[x][y] = FALSE;
3845
3846     ChangeCount[x][y] = 0;
3847     ChangeEvent[x][y] = -1;
3848
3849     ExplodePhase[x][y] = 0;
3850     ExplodeDelay[x][y] = 0;
3851     ExplodeField[x][y] = EX_TYPE_NONE;
3852
3853     RunnerVisit[x][y] = 0;
3854     PlayerVisit[x][y] = 0;
3855
3856     GfxFrame[x][y] = 0;
3857     GfxRandom[x][y] = INIT_GFX_RANDOM();
3858     GfxElement[x][y] = EL_UNDEFINED;
3859     GfxAction[x][y] = ACTION_DEFAULT;
3860     GfxDir[x][y] = MV_NONE;
3861     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3862   }
3863
3864   SCAN_PLAYFIELD(x, y)
3865   {
3866     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3867       emulate_bd = FALSE;
3868     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3869       emulate_sb = FALSE;
3870     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3871       emulate_sp = FALSE;
3872
3873     InitField(x, y, TRUE);
3874
3875     ResetGfxAnimation(x, y);
3876   }
3877
3878   InitBeltMovement();
3879
3880   for (i = 0; i < MAX_PLAYERS; i++)
3881   {
3882     struct PlayerInfo *player = &stored_player[i];
3883
3884     /* set number of special actions for bored and sleeping animation */
3885     player->num_special_action_bored =
3886       get_num_special_action(player->artwork_element,
3887                              ACTION_BORING_1, ACTION_BORING_LAST);
3888     player->num_special_action_sleeping =
3889       get_num_special_action(player->artwork_element,
3890                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3891   }
3892
3893   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3894                     emulate_sb ? EMU_SOKOBAN :
3895                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3896
3897 #if USE_NEW_ALL_SLIPPERY
3898   /* initialize type of slippery elements */
3899   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3900   {
3901     if (!IS_CUSTOM_ELEMENT(i))
3902     {
3903       /* default: elements slip down either to the left or right randomly */
3904       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3905
3906       /* SP style elements prefer to slip down on the left side */
3907       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3908         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3909
3910       /* BD style elements prefer to slip down on the left side */
3911       if (game.emulation == EMU_BOULDERDASH)
3912         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3913     }
3914   }
3915 #endif
3916
3917   /* initialize explosion and ignition delay */
3918   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3919   {
3920     if (!IS_CUSTOM_ELEMENT(i))
3921     {
3922       int num_phase = 8;
3923       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3924                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3925                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3926       int last_phase = (num_phase + 1) * delay;
3927       int half_phase = (num_phase / 2) * delay;
3928
3929       element_info[i].explosion_delay = last_phase - 1;
3930       element_info[i].ignition_delay = half_phase;
3931
3932       if (i == EL_BLACK_ORB)
3933         element_info[i].ignition_delay = 1;
3934     }
3935
3936 #if 0
3937     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3938       element_info[i].explosion_delay = 1;
3939
3940     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3941       element_info[i].ignition_delay = 1;
3942 #endif
3943   }
3944
3945   /* correct non-moving belts to start moving left */
3946   for (i = 0; i < NUM_BELTS; i++)
3947     if (game.belt_dir[i] == MV_NONE)
3948       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3949
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3952   /* choose default local player */
3953   local_player = &stored_player[0];
3954
3955   for (i = 0; i < MAX_PLAYERS; i++)
3956     stored_player[i].connected = FALSE;
3957
3958   local_player->connected = TRUE;
3959   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3960
3961 #if 0
3962   printf("::: TEAM MODE: %d\n", game.team_mode);
3963 #endif
3964
3965   if (tape.playing)
3966   {
3967 #if 1
3968     for (i = 0; i < MAX_PLAYERS; i++)
3969       stored_player[i].connected = tape.player_participates[i];
3970 #else
3971     /* try to guess locally connected team mode players (needed for correct
3972        assignment of player figures from level to locally playing players) */
3973
3974     for (i = 0; i < MAX_PLAYERS; i++)
3975       if (tape.player_participates[i])
3976         stored_player[i].connected = TRUE;
3977 #endif
3978   }
3979   else if (game.team_mode && !options.network)
3980   {
3981     /* try to guess locally connected team mode players (needed for correct
3982        assignment of player figures from level to locally playing players) */
3983
3984     for (i = 0; i < MAX_PLAYERS; i++)
3985       if (setup.input[i].use_joystick ||
3986           setup.input[i].key.left != KSYM_UNDEFINED)
3987         stored_player[i].connected = TRUE;
3988   }
3989
3990 #if DEBUG_INIT_PLAYER
3991   if (options.debug)
3992   {
3993     printf("Player status after level initialization:\n");
3994
3995     for (i = 0; i < MAX_PLAYERS; i++)
3996     {
3997       struct PlayerInfo *player = &stored_player[i];
3998
3999       printf("- player %d: present == %d, connected == %d, active == %d",
4000              i + 1,
4001              player->present,
4002              player->connected,
4003              player->active);
4004
4005       if (local_player == player)
4006         printf(" (local player)");
4007
4008       printf("\n");
4009     }
4010   }
4011 #endif
4012
4013 #if DEBUG_INIT_PLAYER
4014   if (options.debug)
4015     printf("Reassigning players ...\n");
4016 #endif
4017
4018   /* check if any connected player was not found in playfield */
4019   for (i = 0; i < MAX_PLAYERS; i++)
4020   {
4021     struct PlayerInfo *player = &stored_player[i];
4022
4023     if (player->connected && !player->present)
4024     {
4025       struct PlayerInfo *field_player = NULL;
4026
4027 #if DEBUG_INIT_PLAYER
4028       if (options.debug)
4029         printf("- looking for field player for player %d ...\n", i + 1);
4030 #endif
4031
4032       /* assign first free player found that is present in the playfield */
4033
4034 #if 1
4035       /* first try: look for unmapped playfield player that is not connected */
4036       for (j = 0; j < MAX_PLAYERS; j++)
4037         if (field_player == NULL &&
4038             stored_player[j].present &&
4039             !stored_player[j].mapped &&
4040             !stored_player[j].connected)
4041           field_player = &stored_player[j];
4042
4043       /* second try: look for *any* unmapped playfield player */
4044       for (j = 0; j < MAX_PLAYERS; j++)
4045         if (field_player == NULL &&
4046             stored_player[j].present &&
4047             !stored_player[j].mapped)
4048           field_player = &stored_player[j];
4049 #else
4050       /* first try: look for unmapped playfield player that is not connected */
4051       if (field_player == NULL)
4052         for (j = 0; j < MAX_PLAYERS; j++)
4053           if (stored_player[j].present &&
4054               !stored_player[j].mapped &&
4055               !stored_player[j].connected)
4056             field_player = &stored_player[j];
4057
4058       /* second try: look for *any* unmapped playfield player */
4059       if (field_player == NULL)
4060         for (j = 0; j < MAX_PLAYERS; j++)
4061           if (stored_player[j].present &&
4062               !stored_player[j].mapped)
4063             field_player = &stored_player[j];
4064 #endif
4065
4066       if (field_player != NULL)
4067       {
4068         int jx = field_player->jx, jy = field_player->jy;
4069
4070 #if DEBUG_INIT_PLAYER
4071         if (options.debug)
4072           printf("- found player %d\n", field_player->index_nr + 1);
4073 #endif
4074
4075         player->present = FALSE;
4076         player->active = FALSE;
4077
4078         field_player->present = TRUE;
4079         field_player->active = TRUE;
4080
4081         /*
4082         player->initial_element = field_player->initial_element;
4083         player->artwork_element = field_player->artwork_element;
4084
4085         player->block_last_field       = field_player->block_last_field;
4086         player->block_delay_adjustment = field_player->block_delay_adjustment;
4087         */
4088
4089         StorePlayer[jx][jy] = field_player->element_nr;
4090
4091         field_player->jx = field_player->last_jx = jx;
4092         field_player->jy = field_player->last_jy = jy;
4093
4094         if (local_player == player)
4095           local_player = field_player;
4096
4097         map_player_action[field_player->index_nr] = i;
4098
4099         field_player->mapped = TRUE;
4100
4101 #if DEBUG_INIT_PLAYER
4102         if (options.debug)
4103           printf("- map_player_action[%d] == %d\n",
4104                  field_player->index_nr + 1, i + 1);
4105 #endif
4106       }
4107     }
4108
4109     if (player->connected && player->present)
4110       player->mapped = TRUE;
4111   }
4112
4113 #if DEBUG_INIT_PLAYER
4114   if (options.debug)
4115   {
4116     printf("Player status after player assignment (first stage):\n");
4117
4118     for (i = 0; i < MAX_PLAYERS; i++)
4119     {
4120       struct PlayerInfo *player = &stored_player[i];
4121
4122       printf("- player %d: present == %d, connected == %d, active == %d",
4123              i + 1,
4124              player->present,
4125              player->connected,
4126              player->active);
4127
4128       if (local_player == player)
4129         printf(" (local player)");
4130
4131       printf("\n");
4132     }
4133   }
4134 #endif
4135
4136 #else
4137
4138   /* check if any connected player was not found in playfield */
4139   for (i = 0; i < MAX_PLAYERS; i++)
4140   {
4141     struct PlayerInfo *player = &stored_player[i];
4142
4143     if (player->connected && !player->present)
4144     {
4145       for (j = 0; j < MAX_PLAYERS; j++)
4146       {
4147         struct PlayerInfo *field_player = &stored_player[j];
4148         int jx = field_player->jx, jy = field_player->jy;
4149
4150         /* assign first free player found that is present in the playfield */
4151         if (field_player->present && !field_player->connected)
4152         {
4153           player->present = TRUE;
4154           player->active = TRUE;
4155
4156           field_player->present = FALSE;
4157           field_player->active = FALSE;
4158
4159           player->initial_element = field_player->initial_element;
4160           player->artwork_element = field_player->artwork_element;
4161
4162           player->block_last_field       = field_player->block_last_field;
4163           player->block_delay_adjustment = field_player->block_delay_adjustment;
4164
4165           StorePlayer[jx][jy] = player->element_nr;
4166
4167           player->jx = player->last_jx = jx;
4168           player->jy = player->last_jy = jy;
4169
4170           break;
4171         }
4172       }
4173     }
4174   }
4175 #endif
4176
4177 #if 0
4178   printf("::: local_player->present == %d\n", local_player->present);
4179 #endif
4180
4181   if (tape.playing)
4182   {
4183     /* when playing a tape, eliminate all players who do not participate */
4184
4185 #if USE_NEW_PLAYER_ASSIGNMENTS
4186
4187 #if 1
4188     if (!game.team_mode)
4189 #endif
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[map_player_action[i]])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199 #if DEBUG_INIT_PLAYER
4200         if (options.debug)
4201           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4202 #endif
4203
4204         player->active = FALSE;
4205         StorePlayer[jx][jy] = 0;
4206         Feld[jx][jy] = EL_EMPTY;
4207       }
4208     }
4209
4210 #else
4211
4212     for (i = 0; i < MAX_PLAYERS; i++)
4213     {
4214       if (stored_player[i].active &&
4215           !tape.player_participates[i])
4216       {
4217         struct PlayerInfo *player = &stored_player[i];
4218         int jx = player->jx, jy = player->jy;
4219
4220         player->active = FALSE;
4221         StorePlayer[jx][jy] = 0;
4222         Feld[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225 #endif
4226   }
4227   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4228   {
4229     /* when in single player mode, eliminate all but the first active player */
4230
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232     {
4233       if (stored_player[i].active)
4234       {
4235         for (j = i + 1; j < MAX_PLAYERS; j++)
4236         {
4237           if (stored_player[j].active)
4238           {
4239             struct PlayerInfo *player = &stored_player[j];
4240             int jx = player->jx, jy = player->jy;
4241
4242             player->active = FALSE;
4243             player->present = FALSE;
4244
4245             StorePlayer[jx][jy] = 0;
4246             Feld[jx][jy] = EL_EMPTY;
4247           }
4248         }
4249       }
4250     }
4251   }
4252
4253   /* when recording the game, store which players take part in the game */
4254   if (tape.recording)
4255   {
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257     for (i = 0; i < MAX_PLAYERS; i++)
4258       if (stored_player[i].connected)
4259         tape.player_participates[i] = TRUE;
4260 #else
4261     for (i = 0; i < MAX_PLAYERS; i++)
4262       if (stored_player[i].active)
4263         tape.player_participates[i] = TRUE;
4264 #endif
4265   }
4266
4267 #if DEBUG_INIT_PLAYER
4268   if (options.debug)
4269   {
4270     printf("Player status after player assignment (final stage):\n");
4271
4272     for (i = 0; i < MAX_PLAYERS; i++)
4273     {
4274       struct PlayerInfo *player = &stored_player[i];
4275
4276       printf("- player %d: present == %d, connected == %d, active == %d",
4277              i + 1,
4278              player->present,
4279              player->connected,
4280              player->active);
4281
4282       if (local_player == player)
4283         printf(" (local player)");
4284
4285       printf("\n");
4286     }
4287   }
4288 #endif
4289
4290   if (BorderElement == EL_EMPTY)
4291   {
4292     SBX_Left = 0;
4293     SBX_Right = lev_fieldx - SCR_FIELDX;
4294     SBY_Upper = 0;
4295     SBY_Lower = lev_fieldy - SCR_FIELDY;
4296   }
4297   else
4298   {
4299     SBX_Left = -1;
4300     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4301     SBY_Upper = -1;
4302     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4303   }
4304
4305 #if NEW_TILESIZE
4306
4307   // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
4308   // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
4309
4310 #if 1
4311   if (full_lev_fieldx <= SCR_FIELDX)
4312     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4313
4314   if (full_lev_fieldy <= SCR_FIELDY)
4315     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4316 #else
4317   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4318     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4319
4320   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4321     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4322 #endif
4323
4324   /*
4325   printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
4326          SBX_Right - SBX_Left + 1);
4327   */
4328
4329 #if 1
4330   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4331     SBX_Left--;
4332   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4333     SBY_Upper--;
4334 #else
4335   if (EVEN(SCR_FIELDX))
4336     SBX_Left--;
4337   if (EVEN(SCR_FIELDY))
4338     SBY_Upper--;
4339 #endif
4340
4341 #if 0
4342   printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
4343   printf("\n");
4344 #endif
4345
4346 #else
4347
4348   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4349     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4350
4351   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4352     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4353 #endif
4354
4355   /* if local player not found, look for custom element that might create
4356      the player (make some assumptions about the right custom element) */
4357   if (!local_player->present)
4358   {
4359     int start_x = 0, start_y = 0;
4360     int found_rating = 0;
4361     int found_element = EL_UNDEFINED;
4362     int player_nr = local_player->index_nr;
4363
4364     SCAN_PLAYFIELD(x, y)
4365     {
4366       int element = Feld[x][y];
4367       int content;
4368       int xx, yy;
4369       boolean is_player;
4370
4371       if (level.use_start_element[player_nr] &&
4372           level.start_element[player_nr] == element &&
4373           found_rating < 4)
4374       {
4375         start_x = x;
4376         start_y = y;
4377
4378         found_rating = 4;
4379         found_element = element;
4380       }
4381
4382       if (!IS_CUSTOM_ELEMENT(element))
4383         continue;
4384
4385       if (CAN_CHANGE(element))
4386       {
4387         for (i = 0; i < element_info[element].num_change_pages; i++)
4388         {
4389           /* check for player created from custom element as single target */
4390           content = element_info[element].change_page[i].target_element;
4391           is_player = ELEM_IS_PLAYER(content);
4392
4393           if (is_player && (found_rating < 3 ||
4394                             (found_rating == 3 && element < found_element)))
4395           {
4396             start_x = x;
4397             start_y = y;
4398
4399             found_rating = 3;
4400             found_element = element;
4401           }
4402         }
4403       }
4404
4405       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4406       {
4407         /* check for player created from custom element as explosion content */
4408         content = element_info[element].content.e[xx][yy];
4409         is_player = ELEM_IS_PLAYER(content);
4410
4411         if (is_player && (found_rating < 2 ||
4412                           (found_rating == 2 && element < found_element)))
4413         {
4414           start_x = x + xx - 1;
4415           start_y = y + yy - 1;
4416
4417           found_rating = 2;
4418           found_element = element;
4419         }
4420
4421         if (!CAN_CHANGE(element))
4422           continue;
4423
4424         for (i = 0; i < element_info[element].num_change_pages; i++)
4425         {
4426           /* check for player created from custom element as extended target */
4427           content =
4428             element_info[element].change_page[i].target_content.e[xx][yy];
4429
4430           is_player = ELEM_IS_PLAYER(content);
4431
4432           if (is_player && (found_rating < 1 ||
4433                             (found_rating == 1 && element < found_element)))
4434           {
4435             start_x = x + xx - 1;
4436             start_y = y + yy - 1;
4437
4438             found_rating = 1;
4439             found_element = element;
4440           }
4441         }
4442       }
4443     }
4444
4445     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4446                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4447                 start_x - MIDPOSX);
4448
4449     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4450                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4451                 start_y - MIDPOSY);
4452   }
4453   else
4454   {
4455     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4456                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4457                 local_player->jx - MIDPOSX);
4458
4459     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4460                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4461                 local_player->jy - MIDPOSY);
4462   }
4463
4464 #if 0
4465   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4466 #endif
4467
4468 #if 0
4469   /* do not use PLAYING mask for fading out from main screen */
4470   game_status = GAME_MODE_MAIN;
4471 #endif
4472
4473 #if 0
4474
4475   StopAnimation();
4476
4477   if (!game.restart_level)
4478     CloseDoor(DOOR_CLOSE_1);
4479
4480 #if 1
4481   if (level_editor_test_game)
4482     FadeSkipNextFadeIn();
4483   else
4484     FadeSetEnterScreen();
4485 #else
4486   if (level_editor_test_game)
4487     fading = fading_none;
4488   else
4489     fading = menu.destination;
4490 #endif
4491
4492 #if 1
4493   FadeOut(REDRAW_FIELD);
4494 #else
4495   if (do_fading)
4496     FadeOut(REDRAW_FIELD);
4497 #endif
4498
4499 #endif
4500
4501 #if 0
4502   game_status = GAME_MODE_PLAYING;
4503 #endif
4504
4505   /* !!! FIX THIS (START) !!! */
4506   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4507   {
4508     InitGameEngine_EM();
4509
4510 #if 0
4511     /* blit playfield from scroll buffer to normal back buffer for fading in */
4512     BlitScreenToBitmap_EM(backbuffer);
4513 #endif
4514   }
4515   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4516   {
4517     InitGameEngine_SP();
4518
4519 #if 0
4520     /* blit playfield from scroll buffer to normal back buffer for fading in */
4521     BlitScreenToBitmap_SP(backbuffer);
4522 #endif
4523   }
4524   else
4525   {
4526     DrawLevel(REDRAW_FIELD);
4527     DrawAllPlayers();
4528
4529     /* after drawing the level, correct some elements */
4530     if (game.timegate_time_left == 0)
4531       CloseAllOpenTimegates();
4532
4533 #if 0
4534     /* blit playfield from scroll buffer to normal back buffer for fading in */
4535 #if NEW_TILESIZE
4536     BlitScreenToBitmap(backbuffer);
4537 #else
4538     if (setup.soft_scrolling)
4539       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4540 #endif
4541 #endif
4542
4543 #if 0
4544     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4545 #endif
4546   }
4547 #if 1
4548   /* blit playfield from scroll buffer to normal back buffer for fading in */
4549   BlitScreenToBitmap(backbuffer);
4550
4551   redraw_mask |= REDRAW_FROM_BACKBUFFER;
4552 #endif
4553   /* !!! FIX THIS (END) !!! */
4554
4555 #if 1
4556   FadeIn(REDRAW_FIELD);
4557 #else
4558   if (do_fading)
4559     FadeIn(REDRAW_FIELD);
4560
4561   BackToFront();
4562 #endif
4563
4564   if (!game.restart_level)
4565   {
4566     /* copy default game door content to main double buffer */
4567 #if 1
4568 #if 1
4569     /* !!! CHECK AGAIN !!! */
4570     SetPanelBackground();
4571     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4572     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4573 #else
4574     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4575
4576     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4577     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4578     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4579                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4580 #endif
4581 #else
4582     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4583                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4584 #endif
4585   }
4586
4587   SetPanelBackground();
4588   SetDrawBackgroundMask(REDRAW_DOOR_1);
4589
4590 #if 1
4591   UpdateAndDisplayGameControlValues();
4592 #else
4593   UpdateGameDoorValues();
4594   DrawGameDoorValues();
4595 #endif
4596
4597   if (!game.restart_level)
4598   {
4599     UnmapGameButtons();
4600     UnmapTapeButtons();
4601     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4602     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4603     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4604     MapGameButtons();
4605     MapTapeButtons();
4606
4607     /* copy actual game door content to door double buffer for OpenDoor() */
4608 #if 1
4609     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4610 #else
4611     BlitBitmap(drawto, bitmap_db_door,
4612                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4613 #endif
4614
4615     OpenDoor(DOOR_OPEN_ALL);
4616
4617     PlaySound(SND_GAME_STARTING);
4618
4619     if (setup.sound_music)
4620       PlayLevelMusic();
4621
4622     KeyboardAutoRepeatOffUnlessAutoplay();
4623
4624 #if DEBUG_INIT_PLAYER
4625     if (options.debug)
4626     {
4627       printf("Player status (final):\n");
4628
4629       for (i = 0; i < MAX_PLAYERS; i++)
4630       {
4631         struct PlayerInfo *player = &stored_player[i];
4632
4633         printf("- player %d: present == %d, connected == %d, active == %d",
4634                i + 1,
4635                player->present,
4636                player->connected,
4637                player->active);
4638
4639         if (local_player == player)
4640           printf(" (local player)");
4641
4642         printf("\n");
4643       }
4644     }
4645 #endif
4646   }
4647
4648 #if 1
4649   UnmapAllGadgets();
4650
4651   MapGameButtons();
4652   MapTapeButtons();
4653 #endif
4654
4655   if (!game.restart_level && !tape.playing)
4656   {
4657     LevelStats_incPlayed(level_nr);
4658
4659     SaveLevelSetup_SeriesInfo();
4660
4661 #if 0
4662     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4663 #endif
4664   }
4665
4666   game.restart_level = FALSE;
4667 }
4668
4669 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4670 {
4671   /* this is used for non-R'n'D game engines to update certain engine values */
4672
4673   /* needed to determine if sounds are played within the visible screen area */
4674   scroll_x = actual_scroll_x;
4675   scroll_y = actual_scroll_y;
4676 }
4677
4678 void InitMovDir(int x, int y)
4679 {
4680   int i, element = Feld[x][y];
4681   static int xy[4][2] =
4682   {
4683     {  0, +1 },
4684     { +1,  0 },
4685     {  0, -1 },
4686     { -1,  0 }
4687   };
4688   static int direction[3][4] =
4689   {
4690     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4691     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4692     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4693   };
4694
4695   switch (element)
4696   {
4697     case EL_BUG_RIGHT:
4698     case EL_BUG_UP:
4699     case EL_BUG_LEFT:
4700     case EL_BUG_DOWN:
4701       Feld[x][y] = EL_BUG;
4702       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4703       break;
4704
4705     case EL_SPACESHIP_RIGHT:
4706     case EL_SPACESHIP_UP:
4707     case EL_SPACESHIP_LEFT:
4708     case EL_SPACESHIP_DOWN:
4709       Feld[x][y] = EL_SPACESHIP;
4710       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4711       break;
4712
4713     case EL_BD_BUTTERFLY_RIGHT:
4714     case EL_BD_BUTTERFLY_UP:
4715     case EL_BD_BUTTERFLY_LEFT:
4716     case EL_BD_BUTTERFLY_DOWN:
4717       Feld[x][y] = EL_BD_BUTTERFLY;
4718       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4719       break;
4720
4721     case EL_BD_FIREFLY_RIGHT:
4722     case EL_BD_FIREFLY_UP:
4723     case EL_BD_FIREFLY_LEFT:
4724     case EL_BD_FIREFLY_DOWN:
4725       Feld[x][y] = EL_BD_FIREFLY;
4726       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4727       break;
4728
4729     case EL_PACMAN_RIGHT:
4730     case EL_PACMAN_UP:
4731     case EL_PACMAN_LEFT:
4732     case EL_PACMAN_DOWN:
4733       Feld[x][y] = EL_PACMAN;
4734       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4735       break;
4736
4737     case EL_YAMYAM_LEFT:
4738     case EL_YAMYAM_RIGHT:
4739     case EL_YAMYAM_UP:
4740     case EL_YAMYAM_DOWN:
4741       Feld[x][y] = EL_YAMYAM;
4742       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4743       break;
4744
4745     case EL_SP_SNIKSNAK:
4746       MovDir[x][y] = MV_UP;
4747       break;
4748
4749     case EL_SP_ELECTRON:
4750       MovDir[x][y] = MV_LEFT;
4751       break;
4752
4753     case EL_MOLE_LEFT:
4754     case EL_MOLE_RIGHT:
4755     case EL_MOLE_UP:
4756     case EL_MOLE_DOWN:
4757       Feld[x][y] = EL_MOLE;
4758       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4759       break;
4760
4761     default:
4762       if (IS_CUSTOM_ELEMENT(element))
4763       {
4764         struct ElementInfo *ei = &element_info[element];
4765         int move_direction_initial = ei->move_direction_initial;
4766         int move_pattern = ei->move_pattern;
4767
4768         if (move_direction_initial == MV_START_PREVIOUS)
4769         {
4770           if (MovDir[x][y] != MV_NONE)
4771             return;
4772
4773           move_direction_initial = MV_START_AUTOMATIC;
4774         }
4775
4776         if (move_direction_initial == MV_START_RANDOM)
4777           MovDir[x][y] = 1 << RND(4);
4778         else if (move_direction_initial & MV_ANY_DIRECTION)
4779           MovDir[x][y] = move_direction_initial;
4780         else if (move_pattern == MV_ALL_DIRECTIONS ||
4781                  move_pattern == MV_TURNING_LEFT ||
4782                  move_pattern == MV_TURNING_RIGHT ||
4783                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4784                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4785                  move_pattern == MV_TURNING_RANDOM)
4786           MovDir[x][y] = 1 << RND(4);
4787         else if (move_pattern == MV_HORIZONTAL)
4788           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4789         else if (move_pattern == MV_VERTICAL)
4790           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4791         else if (move_pattern & MV_ANY_DIRECTION)
4792           MovDir[x][y] = element_info[element].move_pattern;
4793         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4794                  move_pattern == MV_ALONG_RIGHT_SIDE)
4795         {
4796           /* use random direction as default start direction */
4797           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4798             MovDir[x][y] = 1 << RND(4);
4799
4800           for (i = 0; i < NUM_DIRECTIONS; i++)
4801           {
4802             int x1 = x + xy[i][0];
4803             int y1 = y + xy[i][1];
4804
4805             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4806             {
4807               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4808                 MovDir[x][y] = direction[0][i];
4809               else
4810                 MovDir[x][y] = direction[1][i];
4811
4812               break;
4813             }
4814           }
4815         }                
4816       }
4817       else
4818       {
4819         MovDir[x][y] = 1 << RND(4);
4820
4821         if (element != EL_BUG &&
4822             element != EL_SPACESHIP &&
4823             element != EL_BD_BUTTERFLY &&
4824             element != EL_BD_FIREFLY)
4825           break;
4826
4827         for (i = 0; i < NUM_DIRECTIONS; i++)
4828         {
4829           int x1 = x + xy[i][0];
4830           int y1 = y + xy[i][1];
4831
4832           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4833           {
4834             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4835             {
4836               MovDir[x][y] = direction[0][i];
4837               break;
4838             }
4839             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4840                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4841             {
4842               MovDir[x][y] = direction[1][i];
4843               break;
4844             }
4845           }
4846         }
4847       }
4848       break;
4849   }
4850
4851   GfxDir[x][y] = MovDir[x][y];
4852 }
4853
4854 void InitAmoebaNr(int x, int y)
4855 {
4856   int i;
4857   int group_nr = AmoebeNachbarNr(x, y);
4858
4859   if (group_nr == 0)
4860   {
4861     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4862     {
4863       if (AmoebaCnt[i] == 0)
4864       {
4865         group_nr = i;
4866         break;
4867       }
4868     }
4869   }
4870
4871   AmoebaNr[x][y] = group_nr;
4872   AmoebaCnt[group_nr]++;
4873   AmoebaCnt2[group_nr]++;
4874 }
4875
4876 static void PlayerWins(struct PlayerInfo *player)
4877 {
4878   player->LevelSolved = TRUE;
4879   player->GameOver = TRUE;
4880
4881   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4882                          level.native_em_level->lev->score : player->score);
4883
4884   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4885                                       TimeLeft);
4886   player->LevelSolved_CountingScore = player->score_final;
4887 }
4888
4889 void GameWon()
4890 {
4891   static int time, time_final;
4892   static int score, score_final;
4893   static int game_over_delay_1 = 0;
4894   static int game_over_delay_2 = 0;
4895   int game_over_delay_value_1 = 50;
4896   int game_over_delay_value_2 = 50;
4897
4898   if (!local_player->LevelSolved_GameWon)
4899   {
4900     int i;
4901
4902     /* do not start end game actions before the player stops moving (to exit) */
4903     if (local_player->MovPos)
4904       return;
4905
4906     local_player->LevelSolved_GameWon = TRUE;
4907     local_player->LevelSolved_SaveTape = tape.recording;
4908     local_player->LevelSolved_SaveScore = !tape.playing;
4909
4910     if (!tape.playing)
4911     {
4912       LevelStats_incSolved(level_nr);
4913
4914       SaveLevelSetup_SeriesInfo();
4915
4916 #if 0
4917       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4918 #endif
4919     }
4920
4921     if (tape.auto_play)         /* tape might already be stopped here */
4922       tape.auto_play_level_solved = TRUE;
4923
4924 #if 1
4925     TapeStop();
4926 #endif
4927
4928     game_over_delay_1 = game_over_delay_value_1;
4929     game_over_delay_2 = game_over_delay_value_2;
4930
4931     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4932     score = score_final = local_player->score_final;
4933
4934     if (TimeLeft > 0)
4935     {
4936       time_final = 0;
4937       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4938     }
4939     else if (game.no_time_limit && TimePlayed < 999)
4940     {
4941       time_final = 999;
4942       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4943     }
4944
4945     local_player->score_final = score_final;
4946
4947     if (level_editor_test_game)
4948     {
4949       time = time_final;
4950       score = score_final;
4951
4952 #if 1
4953       local_player->LevelSolved_CountingTime = time;
4954       local_player->LevelSolved_CountingScore = score;
4955
4956       game_panel_controls[GAME_PANEL_TIME].value = time;
4957       game_panel_controls[GAME_PANEL_SCORE].value = score;
4958
4959       DisplayGameControlValues();
4960 #else
4961       DrawGameValue_Time(time);
4962       DrawGameValue_Score(score);
4963 #endif
4964     }
4965
4966     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4967     {
4968       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4969       {
4970         /* close exit door after last player */
4971         if ((AllPlayersGone &&
4972              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4973               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4974               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4975             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4976             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4977         {
4978           int element = Feld[ExitX][ExitY];
4979
4980 #if 0
4981           if (element == EL_EM_EXIT_OPEN ||
4982               element == EL_EM_STEEL_EXIT_OPEN)
4983           {
4984             Bang(ExitX, ExitY);
4985           }
4986           else
4987 #endif
4988           {
4989             Feld[ExitX][ExitY] =
4990               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4991                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4992                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4993                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4994                EL_EM_STEEL_EXIT_CLOSING);
4995
4996             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4997           }
4998         }
4999
5000         /* player disappears */
5001         DrawLevelField(ExitX, ExitY);
5002       }
5003
5004       for (i = 0; i < MAX_PLAYERS; i++)
5005       {
5006         struct PlayerInfo *player = &stored_player[i];
5007
5008         if (player->present)
5009         {
5010           RemovePlayer(player);
5011
5012           /* player disappears */
5013           DrawLevelField(player->jx, player->jy);
5014         }
5015       }
5016     }
5017
5018     PlaySound(SND_GAME_WINNING);
5019   }
5020
5021   if (game_over_delay_1 > 0)
5022   {
5023     game_over_delay_1--;
5024
5025     return;
5026   }
5027
5028   if (time != time_final)
5029   {
5030     int time_to_go = ABS(time_final - time);
5031     int time_count_dir = (time < time_final ? +1 : -1);
5032     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5033
5034     time  += time_count_steps * time_count_dir;
5035     score += time_count_steps * level.score[SC_TIME_BONUS];
5036
5037 #if 1
5038     local_player->LevelSolved_CountingTime = time;
5039     local_player->LevelSolved_CountingScore = score;
5040
5041     game_panel_controls[GAME_PANEL_TIME].value = time;
5042     game_panel_controls[GAME_PANEL_SCORE].value = score;
5043
5044     DisplayGameControlValues();
5045 #else
5046     DrawGameValue_Time(time);
5047     DrawGameValue_Score(score);
5048 #endif
5049
5050     if (time == time_final)
5051       StopSound(SND_GAME_LEVELTIME_BONUS);
5052     else if (setup.sound_loops)
5053       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5054     else
5055       PlaySound(SND_GAME_LEVELTIME_BONUS);
5056
5057     return;
5058   }
5059
5060   local_player->LevelSolved_PanelOff = TRUE;
5061
5062   if (game_over_delay_2 > 0)
5063   {
5064     game_over_delay_2--;
5065
5066     return;
5067   }
5068
5069 #if 1
5070   GameEnd();
5071 #endif
5072 }
5073
5074 void GameEnd()
5075 {
5076   int hi_pos;
5077   boolean raise_level = FALSE;
5078
5079   local_player->LevelSolved_GameEnd = TRUE;
5080
5081   CloseDoor(DOOR_CLOSE_1);
5082
5083   if (local_player->LevelSolved_SaveTape)
5084   {
5085 #if 0
5086     TapeStop();
5087 #endif
5088
5089 #if 1
5090     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5091 #else
5092     SaveTape(tape.level_nr);            /* ask to save tape */
5093 #endif
5094   }
5095
5096   if (level_editor_test_game)
5097   {
5098     game_status = GAME_MODE_MAIN;
5099
5100 #if 1
5101     DrawAndFadeInMainMenu(REDRAW_FIELD);
5102 #else
5103     DrawMainMenu();
5104 #endif
5105
5106     return;
5107   }
5108
5109   if (!local_player->LevelSolved_SaveScore)
5110   {
5111 #if 1
5112     FadeOut(REDRAW_FIELD);
5113 #endif
5114
5115     game_status = GAME_MODE_MAIN;
5116
5117     DrawAndFadeInMainMenu(REDRAW_FIELD);
5118
5119     return;
5120   }
5121
5122   if (level_nr == leveldir_current->handicap_level)
5123   {
5124     leveldir_current->handicap_level++;
5125
5126     SaveLevelSetup_SeriesInfo();
5127   }
5128
5129   if (level_nr < leveldir_current->last_level)
5130     raise_level = TRUE;                 /* advance to next level */
5131
5132   if ((hi_pos = NewHiScore()) >= 0) 
5133   {
5134     game_status = GAME_MODE_SCORES;
5135
5136     DrawHallOfFame(hi_pos);
5137
5138     if (raise_level)
5139     {
5140       level_nr++;
5141       TapeErase();
5142     }
5143   }
5144   else
5145   {
5146 #if 1
5147     FadeOut(REDRAW_FIELD);
5148 #endif
5149
5150     game_status = GAME_MODE_MAIN;
5151
5152     if (raise_level)
5153     {
5154       level_nr++;
5155       TapeErase();
5156     }
5157
5158     DrawAndFadeInMainMenu(REDRAW_FIELD);
5159   }
5160 }
5161
5162 int NewHiScore()
5163 {
5164   int k, l;
5165   int position = -1;
5166
5167   LoadScore(level_nr);
5168
5169   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5170       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5171     return -1;
5172
5173   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5174   {
5175     if (local_player->score_final > highscore[k].Score)
5176     {
5177       /* player has made it to the hall of fame */
5178
5179       if (k < MAX_SCORE_ENTRIES - 1)
5180       {
5181         int m = MAX_SCORE_ENTRIES - 1;
5182
5183 #ifdef ONE_PER_NAME
5184         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5185           if (strEqual(setup.player_name, highscore[l].Name))
5186             m = l;
5187         if (m == k)     /* player's new highscore overwrites his old one */
5188           goto put_into_list;
5189 #endif
5190
5191         for (l = m; l > k; l--)
5192         {
5193           strcpy(highscore[l].Name, highscore[l - 1].Name);
5194           highscore[l].Score = highscore[l - 1].Score;
5195         }
5196       }
5197
5198 #ifdef ONE_PER_NAME
5199       put_into_list:
5200 #endif
5201       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5202       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5203       highscore[k].Score = local_player->score_final; 
5204       position = k;
5205       break;
5206     }
5207
5208 #ifdef ONE_PER_NAME
5209     else if (!strncmp(setup.player_name, highscore[k].Name,
5210                       MAX_PLAYER_NAME_LEN))
5211       break;    /* player already there with a higher score */
5212 #endif
5213
5214   }
5215
5216   if (position >= 0) 
5217     SaveScore(level_nr);
5218
5219   return position;
5220 }
5221
5222 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5223 {
5224   int element = Feld[x][y];
5225   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5226   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5227   int horiz_move = (dx != 0);
5228   int sign = (horiz_move ? dx : dy);
5229   int step = sign * element_info[element].move_stepsize;
5230
5231   /* special values for move stepsize for spring and things on conveyor belt */
5232   if (horiz_move)
5233   {
5234     if (CAN_FALL(element) &&
5235         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5236       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5237     else if (element == EL_SPRING)
5238       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5239   }
5240
5241   return step;
5242 }
5243
5244 inline static int getElementMoveStepsize(int x, int y)
5245 {
5246   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5247 }
5248
5249 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5250 {
5251   if (player->GfxAction != action || player->GfxDir != dir)
5252   {
5253 #if 0
5254     printf("Player frame reset! (%d => %d, %d => %d)\n",
5255            player->GfxAction, action, player->GfxDir, dir);
5256 #endif
5257
5258     player->GfxAction = action;
5259     player->GfxDir = dir;
5260     player->Frame = 0;
5261     player->StepFrame = 0;
5262   }
5263 }
5264
5265 #if USE_GFX_RESET_GFX_ANIMATION
5266 static void ResetGfxFrame(int x, int y, boolean redraw)
5267 {
5268   int element = Feld[x][y];
5269   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5270   int last_gfx_frame = GfxFrame[x][y];
5271
5272   if (graphic_info[graphic].anim_global_sync)
5273     GfxFrame[x][y] = FrameCounter;
5274   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5275     GfxFrame[x][y] = CustomValue[x][y];
5276   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5277     GfxFrame[x][y] = element_info[element].collect_score;
5278   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5279     GfxFrame[x][y] = ChangeDelay[x][y];
5280
5281   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5282     DrawLevelGraphicAnimation(x, y, graphic);
5283 }
5284 #endif
5285
5286 static void ResetGfxAnimation(int x, int y)
5287 {
5288   GfxAction[x][y] = ACTION_DEFAULT;
5289   GfxDir[x][y] = MovDir[x][y];
5290   GfxFrame[x][y] = 0;
5291
5292 #if USE_GFX_RESET_GFX_ANIMATION
5293   ResetGfxFrame(x, y, FALSE);
5294 #endif
5295 }
5296
5297 static void ResetRandomAnimationValue(int x, int y)
5298 {
5299   GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 }
5301
5302 void InitMovingField(int x, int y, int direction)
5303 {
5304   int element = Feld[x][y];
5305   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5306   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5307   int newx = x + dx;
5308   int newy = y + dy;
5309   boolean is_moving_before, is_moving_after;
5310 #if 0
5311   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5312 #endif
5313
5314   /* check if element was/is moving or being moved before/after mode change */
5315 #if 1
5316 #if 1
5317   is_moving_before = (WasJustMoving[x][y] != 0);
5318 #else
5319   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5320   is_moving_before = WasJustMoving[x][y];
5321 #endif
5322 #else
5323   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5324 #endif
5325   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5326
5327   /* reset animation only for moving elements which change direction of moving
5328      or which just started or stopped moving
5329      (else CEs with property "can move" / "not moving" are reset each frame) */
5330 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5331 #if 1
5332   if (is_moving_before != is_moving_after ||
5333       direction != MovDir[x][y])
5334     ResetGfxAnimation(x, y);
5335 #else
5336   if ((is_moving_before || is_moving_after) && !continues_moving)
5337     ResetGfxAnimation(x, y);
5338 #endif
5339 #else
5340   if (!continues_moving)
5341     ResetGfxAnimation(x, y);
5342 #endif
5343
5344   MovDir[x][y] = direction;
5345   GfxDir[x][y] = direction;
5346
5347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5348   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5349                      direction == MV_DOWN && CAN_FALL(element) ?
5350                      ACTION_FALLING : ACTION_MOVING);
5351 #else
5352   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5353                      ACTION_FALLING : ACTION_MOVING);
5354 #endif
5355
5356   /* this is needed for CEs with property "can move" / "not moving" */
5357
5358   if (is_moving_after)
5359   {
5360     if (Feld[newx][newy] == EL_EMPTY)
5361       Feld[newx][newy] = EL_BLOCKED;
5362
5363     MovDir[newx][newy] = MovDir[x][y];
5364
5365 #if USE_NEW_CUSTOM_VALUE
5366     CustomValue[newx][newy] = CustomValue[x][y];
5367 #endif
5368
5369     GfxFrame[newx][newy] = GfxFrame[x][y];
5370     GfxRandom[newx][newy] = GfxRandom[x][y];
5371     GfxAction[newx][newy] = GfxAction[x][y];
5372     GfxDir[newx][newy] = GfxDir[x][y];
5373   }
5374 }
5375
5376 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5377 {
5378   int direction = MovDir[x][y];
5379   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5380   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5381
5382   *goes_to_x = newx;
5383   *goes_to_y = newy;
5384 }
5385
5386 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5387 {
5388   int oldx = x, oldy = y;
5389   int direction = MovDir[x][y];
5390
5391   if (direction == MV_LEFT)
5392     oldx++;
5393   else if (direction == MV_RIGHT)
5394     oldx--;
5395   else if (direction == MV_UP)
5396     oldy++;
5397   else if (direction == MV_DOWN)
5398     oldy--;
5399
5400   *comes_from_x = oldx;
5401   *comes_from_y = oldy;
5402 }
5403
5404 int MovingOrBlocked2Element(int x, int y)
5405 {
5406   int element = Feld[x][y];
5407
5408   if (element == EL_BLOCKED)
5409   {
5410     int oldx, oldy;
5411
5412     Blocked2Moving(x, y, &oldx, &oldy);
5413     return Feld[oldx][oldy];
5414   }
5415   else
5416     return element;
5417 }
5418
5419 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5420 {
5421   /* like MovingOrBlocked2Element(), but if element is moving
5422      and (x,y) is the field the moving element is just leaving,
5423      return EL_BLOCKED instead of the element value */
5424   int element = Feld[x][y];
5425
5426   if (IS_MOVING(x, y))
5427   {
5428     if (element == EL_BLOCKED)
5429     {
5430       int oldx, oldy;
5431
5432       Blocked2Moving(x, y, &oldx, &oldy);
5433       return Feld[oldx][oldy];
5434     }
5435     else
5436       return EL_BLOCKED;
5437   }
5438   else
5439     return element;
5440 }
5441
5442 static void RemoveField(int x, int y)
5443 {
5444   Feld[x][y] = EL_EMPTY;
5445
5446   MovPos[x][y] = 0;
5447   MovDir[x][y] = 0;
5448   MovDelay[x][y] = 0;
5449
5450 #if USE_NEW_CUSTOM_VALUE
5451   CustomValue[x][y] = 0;
5452 #endif
5453
5454   AmoebaNr[x][y] = 0;
5455   ChangeDelay[x][y] = 0;
5456   ChangePage[x][y] = -1;
5457   Pushed[x][y] = FALSE;
5458
5459 #if 0
5460   ExplodeField[x][y] = EX_TYPE_NONE;
5461 #endif
5462
5463   GfxElement[x][y] = EL_UNDEFINED;
5464   GfxAction[x][y] = ACTION_DEFAULT;
5465   GfxDir[x][y] = MV_NONE;
5466 #if 0
5467   /* !!! this would prevent the removed tile from being redrawn !!! */
5468   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5469 #endif
5470 }
5471
5472 void RemoveMovingField(int x, int y)
5473 {
5474   int oldx = x, oldy = y, newx = x, newy = y;
5475   int element = Feld[x][y];
5476   int next_element = EL_UNDEFINED;
5477
5478   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5479     return;
5480
5481   if (IS_MOVING(x, y))
5482   {
5483     Moving2Blocked(x, y, &newx, &newy);
5484
5485     if (Feld[newx][newy] != EL_BLOCKED)
5486     {
5487       /* element is moving, but target field is not free (blocked), but
5488          already occupied by something different (example: acid pool);
5489          in this case, only remove the moving field, but not the target */
5490
5491       RemoveField(oldx, oldy);
5492
5493       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5494
5495       TEST_DrawLevelField(oldx, oldy);
5496
5497       return;
5498     }
5499   }
5500   else if (element == EL_BLOCKED)
5501   {
5502     Blocked2Moving(x, y, &oldx, &oldy);
5503     if (!IS_MOVING(oldx, oldy))
5504       return;
5505   }
5506
5507   if (element == EL_BLOCKED &&
5508       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5509        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5510        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5511        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5512        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5513        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5514     next_element = get_next_element(Feld[oldx][oldy]);
5515
5516   RemoveField(oldx, oldy);
5517   RemoveField(newx, newy);
5518
5519   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5520
5521   if (next_element != EL_UNDEFINED)
5522     Feld[oldx][oldy] = next_element;
5523
5524   TEST_DrawLevelField(oldx, oldy);
5525   TEST_DrawLevelField(newx, newy);
5526 }
5527
5528 void DrawDynamite(int x, int y)
5529 {
5530   int sx = SCREENX(x), sy = SCREENY(y);
5531   int graphic = el2img(Feld[x][y]);
5532   int frame;
5533
5534   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5535     return;
5536
5537   if (IS_WALKABLE_INSIDE(Back[x][y]))
5538     return;
5539
5540   if (Back[x][y])
5541     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5542   else if (Store[x][y])
5543     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5544
5545   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5546
5547   if (Back[x][y] || Store[x][y])
5548     DrawGraphicThruMask(sx, sy, graphic, frame);
5549   else
5550     DrawGraphic(sx, sy, graphic, frame);
5551 }
5552
5553 void CheckDynamite(int x, int y)
5554 {
5555   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5556   {
5557     MovDelay[x][y]--;
5558
5559     if (MovDelay[x][y] != 0)
5560     {
5561       DrawDynamite(x, y);
5562       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5563
5564       return;
5565     }
5566   }
5567
5568   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5569
5570   Bang(x, y);
5571 }
5572
5573 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5574 {
5575   boolean num_checked_players = 0;
5576   int i;
5577
5578   for (i = 0; i < MAX_PLAYERS; i++)
5579   {
5580     if (stored_player[i].active)
5581     {
5582       int sx = stored_player[i].jx;
5583       int sy = stored_player[i].jy;
5584
5585       if (num_checked_players == 0)
5586       {
5587         *sx1 = *sx2 = sx;
5588         *sy1 = *sy2 = sy;
5589       }
5590       else
5591       {
5592         *sx1 = MIN(*sx1, sx);
5593         *sy1 = MIN(*sy1, sy);
5594         *sx2 = MAX(*sx2, sx);
5595         *sy2 = MAX(*sy2, sy);
5596       }
5597
5598       num_checked_players++;
5599     }
5600   }
5601 }
5602
5603 static boolean checkIfAllPlayersFitToScreen_RND()
5604 {
5605   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5606
5607   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5608
5609   return (sx2 - sx1 < SCR_FIELDX &&
5610           sy2 - sy1 < SCR_FIELDY);
5611 }
5612
5613 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5614 {
5615   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5616
5617   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5618
5619   *sx = (sx1 + sx2) / 2;
5620   *sy = (sy1 + sy2) / 2;
5621 }
5622
5623 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5624                         boolean center_screen, boolean quick_relocation)
5625 {
5626   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5627   boolean no_delay = (tape.warp_forward);
5628   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5629   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5630
5631   if (quick_relocation)
5632   {
5633     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5634     {
5635       if (!level.shifted_relocation || center_screen)
5636       {
5637         /* quick relocation (without scrolling), with centering of screen */
5638
5639         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5640                     x > SBX_Right + MIDPOSX ? SBX_Right :
5641                     x - MIDPOSX);
5642
5643         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645                     y - MIDPOSY);
5646       }
5647       else
5648       {
5649         /* quick relocation (without scrolling), but do not center screen */
5650
5651         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5652                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5653                                old_x - MIDPOSX);
5654
5655         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5656                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5657                                old_y - MIDPOSY);
5658
5659         int offset_x = x + (scroll_x - center_scroll_x);
5660         int offset_y = y + (scroll_y - center_scroll_y);
5661
5662         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5663                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5664                     offset_x - MIDPOSX);
5665
5666         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5667                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668                     offset_y - MIDPOSY);
5669       }
5670     }
5671     else
5672     {
5673 #if 1
5674       if (!level.shifted_relocation || center_screen)
5675       {
5676         /* quick relocation (without scrolling), with centering of screen */
5677
5678         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5679                     x > SBX_Right + MIDPOSX ? SBX_Right :
5680                     x - MIDPOSX);
5681
5682         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5683                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5684                     y - MIDPOSY);
5685       }
5686       else
5687       {
5688         /* quick relocation (without scrolling), but do not center screen */
5689
5690         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5691                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5692                                old_x - MIDPOSX);
5693
5694         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5695                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5696                                old_y - MIDPOSY);
5697
5698         int offset_x = x + (scroll_x - center_scroll_x);
5699         int offset_y = y + (scroll_y - center_scroll_y);
5700
5701         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5702                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5703                     offset_x - MIDPOSX);
5704
5705         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5706                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5707                     offset_y - MIDPOSY);
5708       }
5709 #else
5710       /* quick relocation (without scrolling), inside visible screen area */
5711
5712       int offset = game.scroll_delay_value;
5713
5714       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5715           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5716         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5717
5718       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5719           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5720         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5721
5722       /* don't scroll over playfield boundaries */
5723       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5724         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5725
5726       /* don't scroll over playfield boundaries */
5727       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5728         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5729 #endif
5730     }
5731
5732     RedrawPlayfield(TRUE, 0,0,0,0);
5733   }
5734   else
5735   {
5736 #if 1
5737     int scroll_xx, scroll_yy;
5738
5739     if (!level.shifted_relocation || center_screen)
5740     {
5741       /* visible relocation (with scrolling), with centering of screen */
5742
5743       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5744                    x > SBX_Right + MIDPOSX ? SBX_Right :
5745                    x - MIDPOSX);
5746
5747       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5748                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5749                    y - MIDPOSY);
5750     }
5751     else
5752     {
5753       /* visible relocation (with scrolling), but do not center screen */
5754
5755       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5756                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5757                              old_x - MIDPOSX);
5758
5759       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5760                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5761                              old_y - MIDPOSY);
5762
5763       int offset_x = x + (scroll_x - center_scroll_x);
5764       int offset_y = y + (scroll_y - center_scroll_y);
5765
5766       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5767                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5768                    offset_x - MIDPOSX);
5769
5770       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5771                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5772                    offset_y - MIDPOSY);
5773     }
5774
5775 #else
5776
5777     /* visible relocation (with scrolling), with centering of screen */
5778
5779     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5780                      x > SBX_Right + MIDPOSX ? SBX_Right :
5781                      x - MIDPOSX);
5782
5783     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5784                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5785                      y - MIDPOSY);
5786 #endif
5787
5788     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5789
5790     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5791     {
5792       int dx = 0, dy = 0;
5793       int fx = FX, fy = FY;
5794
5795       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5796       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5797
5798       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5799         break;
5800
5801       scroll_x -= dx;
5802       scroll_y -= dy;
5803
5804       fx += dx * TILEX / 2;
5805       fy += dy * TILEY / 2;
5806
5807       ScrollLevel(dx, dy);
5808       DrawAllPlayers();
5809
5810       /* scroll in two steps of half tile size to make things smoother */
5811       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5812       FlushDisplay();
5813       Delay(wait_delay_value);
5814
5815       /* scroll second step to align at full tile size */
5816       BackToFront();
5817       Delay(wait_delay_value);
5818     }
5819
5820     DrawAllPlayers();
5821     BackToFront();
5822     Delay(wait_delay_value);
5823   }
5824 }
5825
5826 void RelocatePlayer(int jx, int jy, int el_player_raw)
5827 {
5828   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5829   int player_nr = GET_PLAYER_NR(el_player);
5830   struct PlayerInfo *player = &stored_player[player_nr];
5831   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5832   boolean no_delay = (tape.warp_forward);
5833   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5834   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5835   int old_jx = player->jx;
5836   int old_jy = player->jy;
5837   int old_element = Feld[old_jx][old_jy];
5838   int element = Feld[jx][jy];
5839   boolean player_relocated = (old_jx != jx || old_jy != jy);
5840
5841   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5842   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5843   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5844   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5845   int leave_side_horiz = move_dir_horiz;
5846   int leave_side_vert  = move_dir_vert;
5847   int enter_side = enter_side_horiz | enter_side_vert;
5848   int leave_side = leave_side_horiz | leave_side_vert;
5849
5850   if (player->GameOver)         /* do not reanimate dead player */
5851     return;
5852
5853   if (!player_relocated)        /* no need to relocate the player */
5854     return;
5855
5856   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5857   {
5858     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5859     DrawLevelField(jx, jy);
5860   }
5861
5862   if (player->present)
5863   {
5864     while (player->MovPos)
5865     {
5866       ScrollPlayer(player, SCROLL_GO_ON);
5867       ScrollScreen(NULL, SCROLL_GO_ON);
5868
5869       AdvanceFrameAndPlayerCounters(player->index_nr);
5870
5871       DrawPlayer(player);
5872
5873       BackToFront();
5874       Delay(wait_delay_value);
5875     }
5876
5877     DrawPlayer(player);         /* needed here only to cleanup last field */
5878     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5879
5880     player->is_moving = FALSE;
5881   }
5882
5883   if (IS_CUSTOM_ELEMENT(old_element))
5884     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5885                                CE_LEFT_BY_PLAYER,
5886                                player->index_bit, leave_side);
5887
5888   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5889                                       CE_PLAYER_LEAVES_X,
5890                                       player->index_bit, leave_side);
5891
5892   Feld[jx][jy] = el_player;
5893   InitPlayerField(jx, jy, el_player, TRUE);
5894
5895   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5896      possible that the relocation target field did not contain a player element,
5897      but a walkable element, to which the new player was relocated -- in this
5898      case, restore that (already initialized!) element on the player field */
5899   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5900   {
5901     Feld[jx][jy] = element;     /* restore previously existing element */
5902 #if 0
5903     /* !!! do not initialize already initialized element a second time !!! */
5904     /* (this causes at least problems with "element creation" CE trigger for
5905        already existing elements, and existing Sokoban fields counted twice) */
5906     InitField(jx, jy, FALSE);
5907 #endif
5908   }
5909
5910   /* only visually relocate centered player */
5911   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5912                      FALSE, level.instant_relocation);
5913
5914   TestIfPlayerTouchesBadThing(jx, jy);
5915   TestIfPlayerTouchesCustomElement(jx, jy);
5916
5917   if (IS_CUSTOM_ELEMENT(element))
5918     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5919                                player->index_bit, enter_side);
5920
5921   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5922                                       player->index_bit, enter_side);
5923
5924 #if 1
5925   if (player->is_switching)
5926   {
5927     /* ensure that relocation while still switching an element does not cause
5928        a new element to be treated as also switched directly after relocation
5929        (this is important for teleporter switches that teleport the player to
5930        a place where another teleporter switch is in the same direction, which
5931        would then incorrectly be treated as immediately switched before the
5932        direction key that caused the switch was released) */
5933
5934     player->switch_x += jx - old_jx;
5935     player->switch_y += jy - old_jy;
5936   }
5937 #endif
5938 }
5939
5940 void Explode(int ex, int ey, int phase, int mode)
5941 {
5942   int x, y;
5943   int last_phase;
5944   int border_element;
5945
5946   /* !!! eliminate this variable !!! */
5947   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5948
5949   if (game.explosions_delayed)
5950   {
5951     ExplodeField[ex][ey] = mode;
5952     return;
5953   }
5954
5955   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5956   {
5957     int center_element = Feld[ex][ey];
5958     int artwork_element, explosion_element;     /* set these values later */
5959
5960 #if 0
5961     /* --- This is only really needed (and now handled) in "Impact()". --- */
5962     /* do not explode moving elements that left the explode field in time */
5963     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5964         center_element == EL_EMPTY &&
5965         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5966       return;
5967 #endif
5968
5969 #if 0
5970     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5971     if (mode == EX_TYPE_NORMAL ||
5972         mode == EX_TYPE_CENTER ||
5973         mode == EX_TYPE_CROSS)
5974       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5975 #endif
5976
5977     /* remove things displayed in background while burning dynamite */
5978     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5979       Back[ex][ey] = 0;
5980
5981     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5982     {
5983       /* put moving element to center field (and let it explode there) */
5984       center_element = MovingOrBlocked2Element(ex, ey);
5985       RemoveMovingField(ex, ey);
5986       Feld[ex][ey] = center_element;
5987     }
5988
5989     /* now "center_element" is finally determined -- set related values now */
5990     artwork_element = center_element;           /* for custom player artwork */
5991     explosion_element = center_element;         /* for custom player artwork */
5992
5993     if (IS_PLAYER(ex, ey))
5994     {
5995       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5996
5997       artwork_element = stored_player[player_nr].artwork_element;
5998
5999       if (level.use_explosion_element[player_nr])
6000       {
6001         explosion_element = level.explosion_element[player_nr];
6002         artwork_element = explosion_element;
6003       }
6004     }
6005
6006 #if 1
6007     if (mode == EX_TYPE_NORMAL ||
6008         mode == EX_TYPE_CENTER ||
6009         mode == EX_TYPE_CROSS)
6010       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6011 #endif
6012
6013     last_phase = element_info[explosion_element].explosion_delay + 1;
6014
6015     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6016     {
6017       int xx = x - ex + 1;
6018       int yy = y - ey + 1;
6019       int element;
6020
6021       if (!IN_LEV_FIELD(x, y) ||
6022           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6023           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6024         continue;
6025
6026       element = Feld[x][y];
6027
6028       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6029       {
6030         element = MovingOrBlocked2Element(x, y);
6031
6032         if (!IS_EXPLOSION_PROOF(element))
6033           RemoveMovingField(x, y);
6034       }
6035
6036       /* indestructible elements can only explode in center (but not flames) */
6037       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6038                                            mode == EX_TYPE_BORDER)) ||
6039           element == EL_FLAMES)
6040         continue;
6041
6042       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6043          behaviour, for example when touching a yamyam that explodes to rocks
6044          with active deadly shield, a rock is created under the player !!! */
6045       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6046 #if 0
6047       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6048           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6049            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6050 #else
6051       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6052 #endif
6053       {
6054         if (IS_ACTIVE_BOMB(element))
6055         {
6056           /* re-activate things under the bomb like gate or penguin */
6057           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6058           Back[x][y] = 0;
6059         }
6060
6061         continue;
6062       }
6063
6064       /* save walkable background elements while explosion on same tile */
6065       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6066           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6067         Back[x][y] = element;
6068
6069       /* ignite explodable elements reached by other explosion */
6070       if (element == EL_EXPLOSION)
6071         element = Store2[x][y];
6072
6073       if (AmoebaNr[x][y] &&
6074           (element == EL_AMOEBA_FULL ||
6075            element == EL_BD_AMOEBA ||
6076            element == EL_AMOEBA_GROWING))
6077       {
6078         AmoebaCnt[AmoebaNr[x][y]]--;
6079         AmoebaCnt2[AmoebaNr[x][y]]--;
6080       }
6081
6082       RemoveField(x, y);
6083
6084       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6085       {
6086         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6087
6088         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6089
6090         if (PLAYERINFO(ex, ey)->use_murphy)
6091           Store[x][y] = EL_EMPTY;
6092       }
6093
6094       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6095          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6096       else if (ELEM_IS_PLAYER(center_element))
6097         Store[x][y] = EL_EMPTY;
6098       else if (center_element == EL_YAMYAM)
6099         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6100       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6101         Store[x][y] = element_info[center_element].content.e[xx][yy];
6102 #if 1
6103       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6104          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6105          otherwise) -- FIX THIS !!! */
6106       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6107         Store[x][y] = element_info[element].content.e[1][1];
6108 #else
6109       else if (!CAN_EXPLODE(element))
6110         Store[x][y] = element_info[element].content.e[1][1];
6111 #endif
6112       else
6113         Store[x][y] = EL_EMPTY;
6114
6115       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6116           center_element == EL_AMOEBA_TO_DIAMOND)
6117         Store2[x][y] = element;
6118
6119       Feld[x][y] = EL_EXPLOSION;
6120       GfxElement[x][y] = artwork_element;
6121
6122       ExplodePhase[x][y] = 1;
6123       ExplodeDelay[x][y] = last_phase;
6124
6125       Stop[x][y] = TRUE;
6126     }
6127
6128     if (center_element == EL_YAMYAM)
6129       game.yamyam_content_nr =
6130         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6131
6132     return;
6133   }
6134
6135   if (Stop[ex][ey])
6136     return;
6137
6138   x = ex;
6139   y = ey;
6140
6141   if (phase == 1)
6142     GfxFrame[x][y] = 0;         /* restart explosion animation */
6143
6144   last_phase = ExplodeDelay[x][y];
6145
6146   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6147
6148 #ifdef DEBUG
6149
6150   /* activate this even in non-DEBUG version until cause for crash in
6151      getGraphicAnimationFrame() (see below) is found and eliminated */
6152
6153 #endif
6154 #if 1
6155
6156 #if 1
6157   /* this can happen if the player leaves an explosion just in time */
6158   if (GfxElement[x][y] == EL_UNDEFINED)
6159     GfxElement[x][y] = EL_EMPTY;
6160 #else
6161   if (GfxElement[x][y] == EL_UNDEFINED)
6162   {
6163     printf("\n\n");
6164     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6165     printf("Explode(): This should never happen!\n");
6166     printf("\n\n");
6167
6168     GfxElement[x][y] = EL_EMPTY;
6169   }
6170 #endif
6171
6172 #endif
6173
6174   border_element = Store2[x][y];
6175   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6176     border_element = StorePlayer[x][y];
6177
6178   if (phase == element_info[border_element].ignition_delay ||
6179       phase == last_phase)
6180   {
6181     boolean border_explosion = FALSE;
6182
6183     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6184         !PLAYER_EXPLOSION_PROTECTED(x, y))
6185     {
6186       KillPlayerUnlessExplosionProtected(x, y);
6187       border_explosion = TRUE;
6188     }
6189     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6190     {
6191       Feld[x][y] = Store2[x][y];
6192       Store2[x][y] = 0;
6193       Bang(x, y);
6194       border_explosion = TRUE;
6195     }
6196     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6197     {
6198       AmoebeUmwandeln(x, y);
6199       Store2[x][y] = 0;
6200       border_explosion = TRUE;
6201     }
6202
6203     /* if an element just explodes due to another explosion (chain-reaction),
6204        do not immediately end the new explosion when it was the last frame of
6205        the explosion (as it would be done in the following "if"-statement!) */
6206     if (border_explosion && phase == last_phase)
6207       return;
6208   }
6209
6210   if (phase == last_phase)
6211   {
6212     int element;
6213
6214     element = Feld[x][y] = Store[x][y];
6215     Store[x][y] = Store2[x][y] = 0;
6216     GfxElement[x][y] = EL_UNDEFINED;
6217
6218     /* player can escape from explosions and might therefore be still alive */
6219     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6220         element <= EL_PLAYER_IS_EXPLODING_4)
6221     {
6222       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6223       int explosion_element = EL_PLAYER_1 + player_nr;
6224       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6225       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6226
6227       if (level.use_explosion_element[player_nr])
6228         explosion_element = level.explosion_element[player_nr];
6229
6230       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6231                     element_info[explosion_element].content.e[xx][yy]);
6232     }
6233
6234     /* restore probably existing indestructible background element */
6235     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6236       element = Feld[x][y] = Back[x][y];
6237     Back[x][y] = 0;
6238
6239     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6240     GfxDir[x][y] = MV_NONE;
6241     ChangeDelay[x][y] = 0;
6242     ChangePage[x][y] = -1;
6243
6244 #if USE_NEW_CUSTOM_VALUE
6245     CustomValue[x][y] = 0;
6246 #endif
6247
6248     InitField_WithBug2(x, y, FALSE);
6249
6250     TEST_DrawLevelField(x, y);
6251
6252     TestIfElementTouchesCustomElement(x, y);
6253
6254     if (GFX_CRUMBLED(element))
6255       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6256
6257     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6258       StorePlayer[x][y] = 0;
6259
6260     if (ELEM_IS_PLAYER(element))
6261       RelocatePlayer(x, y, element);
6262   }
6263   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6264   {
6265     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6266     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6267
6268     if (phase == delay)
6269       TEST_DrawLevelFieldCrumbled(x, y);
6270
6271     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6272     {
6273       DrawLevelElement(x, y, Back[x][y]);
6274       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6275     }
6276     else if (IS_WALKABLE_UNDER(Back[x][y]))
6277     {
6278       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6279       DrawLevelElementThruMask(x, y, Back[x][y]);
6280     }
6281     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6282       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6283   }
6284 }
6285
6286 void DynaExplode(int ex, int ey)
6287 {
6288   int i, j;
6289   int dynabomb_element = Feld[ex][ey];
6290   int dynabomb_size = 1;
6291   boolean dynabomb_xl = FALSE;
6292   struct PlayerInfo *player;
6293   static int xy[4][2] =
6294   {
6295     { 0, -1 },
6296     { -1, 0 },
6297     { +1, 0 },
6298     { 0, +1 }
6299   };
6300
6301   if (IS_ACTIVE_BOMB(dynabomb_element))
6302   {
6303     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6304     dynabomb_size = player->dynabomb_size;
6305     dynabomb_xl = player->dynabomb_xl;
6306     player->dynabombs_left++;
6307   }
6308
6309   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6310
6311   for (i = 0; i < NUM_DIRECTIONS; i++)
6312   {
6313     for (j = 1; j <= dynabomb_size; j++)
6314     {
6315       int x = ex + j * xy[i][0];
6316       int y = ey + j * xy[i][1];
6317       int element;
6318
6319       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6320         break;
6321
6322       element = Feld[x][y];
6323
6324       /* do not restart explosions of fields with active bombs */
6325       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6326         continue;
6327
6328       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6329
6330       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6331           !IS_DIGGABLE(element) && !dynabomb_xl)
6332         break;
6333     }
6334   }
6335 }
6336
6337 void Bang(int x, int y)
6338 {
6339   int element = MovingOrBlocked2Element(x, y);
6340   int explosion_type = EX_TYPE_NORMAL;
6341
6342   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6343   {
6344     struct PlayerInfo *player = PLAYERINFO(x, y);
6345
6346 #if USE_FIX_CE_ACTION_WITH_PLAYER
6347     element = Feld[x][y] = player->initial_element;
6348 #else
6349     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6350                             player->element_nr);
6351 #endif
6352
6353     if (level.use_explosion_element[player->index_nr])
6354     {
6355       int explosion_element = level.explosion_element[player->index_nr];
6356
6357       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6358         explosion_type = EX_TYPE_CROSS;
6359       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6360         explosion_type = EX_TYPE_CENTER;
6361     }
6362   }
6363
6364   switch (element)
6365   {
6366     case EL_BUG:
6367     case EL_SPACESHIP:
6368     case EL_BD_BUTTERFLY:
6369     case EL_BD_FIREFLY:
6370     case EL_YAMYAM:
6371     case EL_DARK_YAMYAM:
6372     case EL_ROBOT:
6373     case EL_PACMAN:
6374     case EL_MOLE:
6375       RaiseScoreElement(element);
6376       break;
6377
6378     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6379     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6380     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6381     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6382     case EL_DYNABOMB_INCREASE_NUMBER:
6383     case EL_DYNABOMB_INCREASE_SIZE:
6384     case EL_DYNABOMB_INCREASE_POWER:
6385       explosion_type = EX_TYPE_DYNA;
6386       break;
6387
6388     case EL_DC_LANDMINE:
6389 #if 0
6390     case EL_EM_EXIT_OPEN:
6391     case EL_EM_STEEL_EXIT_OPEN:
6392 #endif
6393       explosion_type = EX_TYPE_CENTER;
6394       break;
6395
6396     case EL_PENGUIN:
6397     case EL_LAMP:
6398     case EL_LAMP_ACTIVE:
6399     case EL_AMOEBA_TO_DIAMOND:
6400       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6401         explosion_type = EX_TYPE_CENTER;
6402       break;
6403
6404     default:
6405       if (element_info[element].explosion_type == EXPLODES_CROSS)
6406         explosion_type = EX_TYPE_CROSS;
6407       else if (element_info[element].explosion_type == EXPLODES_1X1)
6408         explosion_type = EX_TYPE_CENTER;
6409       break;
6410   }
6411
6412   if (explosion_type == EX_TYPE_DYNA)
6413     DynaExplode(x, y);
6414   else
6415     Explode(x, y, EX_PHASE_START, explosion_type);
6416
6417   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6418 }
6419
6420 void SplashAcid(int x, int y)
6421 {
6422   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6423       (!IN_LEV_FIELD(x - 1, y - 2) ||
6424        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6425     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6426
6427   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6428       (!IN_LEV_FIELD(x + 1, y - 2) ||
6429        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6430     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6431
6432   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6433 }
6434
6435 static void InitBeltMovement()
6436 {
6437   static int belt_base_element[4] =
6438   {
6439     EL_CONVEYOR_BELT_1_LEFT,
6440     EL_CONVEYOR_BELT_2_LEFT,
6441     EL_CONVEYOR_BELT_3_LEFT,
6442     EL_CONVEYOR_BELT_4_LEFT
6443   };
6444   static int belt_base_active_element[4] =
6445   {
6446     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6448     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6449     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6450   };
6451
6452   int x, y, i, j;
6453
6454   /* set frame order for belt animation graphic according to belt direction */
6455   for (i = 0; i < NUM_BELTS; i++)
6456   {
6457     int belt_nr = i;
6458
6459     for (j = 0; j < NUM_BELT_PARTS; j++)
6460     {
6461       int element = belt_base_active_element[belt_nr] + j;
6462       int graphic_1 = el2img(element);
6463       int graphic_2 = el2panelimg(element);
6464
6465       if (game.belt_dir[i] == MV_LEFT)
6466       {
6467         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6468         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6469       }
6470       else
6471       {
6472         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6473         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6474       }
6475     }
6476   }
6477
6478   SCAN_PLAYFIELD(x, y)
6479   {
6480     int element = Feld[x][y];
6481
6482     for (i = 0; i < NUM_BELTS; i++)
6483     {
6484       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6485       {
6486         int e_belt_nr = getBeltNrFromBeltElement(element);
6487         int belt_nr = i;
6488
6489         if (e_belt_nr == belt_nr)
6490         {
6491           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6492
6493           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6494         }
6495       }
6496     }
6497   }
6498 }
6499
6500 static void ToggleBeltSwitch(int x, int y)
6501 {
6502   static int belt_base_element[4] =
6503   {
6504     EL_CONVEYOR_BELT_1_LEFT,
6505     EL_CONVEYOR_BELT_2_LEFT,
6506     EL_CONVEYOR_BELT_3_LEFT,
6507     EL_CONVEYOR_BELT_4_LEFT
6508   };
6509   static int belt_base_active_element[4] =
6510   {
6511     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6512     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6513     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6514     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6515   };
6516   static int belt_base_switch_element[4] =
6517   {
6518     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6519     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6520     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6521     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6522   };
6523   static int belt_move_dir[4] =
6524   {
6525     MV_LEFT,
6526     MV_NONE,
6527     MV_RIGHT,
6528     MV_NONE,
6529   };
6530
6531   int element = Feld[x][y];
6532   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6533   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6534   int belt_dir = belt_move_dir[belt_dir_nr];
6535   int xx, yy, i;
6536
6537   if (!IS_BELT_SWITCH(element))
6538     return;
6539
6540   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6541   game.belt_dir[belt_nr] = belt_dir;
6542
6543   if (belt_dir_nr == 3)
6544     belt_dir_nr = 1;
6545
6546   /* set frame order for belt animation graphic according to belt direction */
6547   for (i = 0; i < NUM_BELT_PARTS; i++)
6548   {
6549     int element = belt_base_active_element[belt_nr] + i;
6550     int graphic_1 = el2img(element);
6551     int graphic_2 = el2panelimg(element);
6552
6553     if (belt_dir == MV_LEFT)
6554     {
6555       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6556       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6557     }
6558     else
6559     {
6560       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6561       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6562     }
6563   }
6564
6565   SCAN_PLAYFIELD(xx, yy)
6566   {
6567     int element = Feld[xx][yy];
6568
6569     if (IS_BELT_SWITCH(element))
6570     {
6571       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6572
6573       if (e_belt_nr == belt_nr)
6574       {
6575         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6576         TEST_DrawLevelField(xx, yy);
6577       }
6578     }
6579     else if (IS_BELT(element) && belt_dir != MV_NONE)
6580     {
6581       int e_belt_nr = getBeltNrFromBeltElement(element);
6582
6583       if (e_belt_nr == belt_nr)
6584       {
6585         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6586
6587         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6588         TEST_DrawLevelField(xx, yy);
6589       }
6590     }
6591     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6592     {
6593       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6594
6595       if (e_belt_nr == belt_nr)
6596       {
6597         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6598
6599         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6600         TEST_DrawLevelField(xx, yy);
6601       }
6602     }
6603   }
6604 }
6605
6606 static void ToggleSwitchgateSwitch(int x, int y)
6607 {
6608   int xx, yy;
6609
6610   game.switchgate_pos = !game.switchgate_pos;
6611
6612   SCAN_PLAYFIELD(xx, yy)
6613   {
6614     int element = Feld[xx][yy];
6615
6616 #if !USE_BOTH_SWITCHGATE_SWITCHES
6617     if (element == EL_SWITCHGATE_SWITCH_UP ||
6618         element == EL_SWITCHGATE_SWITCH_DOWN)
6619     {
6620       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6621       TEST_DrawLevelField(xx, yy);
6622     }
6623     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6624              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6625     {
6626       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6627       TEST_DrawLevelField(xx, yy);
6628     }
6629 #else
6630     if (element == EL_SWITCHGATE_SWITCH_UP)
6631     {
6632       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6633       TEST_DrawLevelField(xx, yy);
6634     }
6635     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6636     {
6637       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6638       TEST_DrawLevelField(xx, yy);
6639     }
6640     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6641     {
6642       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6643       TEST_DrawLevelField(xx, yy);
6644     }
6645     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6646     {
6647       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6648       TEST_DrawLevelField(xx, yy);
6649     }
6650 #endif
6651     else if (element == EL_SWITCHGATE_OPEN ||
6652              element == EL_SWITCHGATE_OPENING)
6653     {
6654       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6655
6656       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6657     }
6658     else if (element == EL_SWITCHGATE_CLOSED ||
6659              element == EL_SWITCHGATE_CLOSING)
6660     {
6661       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6662
6663       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6664     }
6665   }
6666 }
6667
6668 static int getInvisibleActiveFromInvisibleElement(int element)
6669 {
6670   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6671           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6672           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6673           element);
6674 }
6675
6676 static int getInvisibleFromInvisibleActiveElement(int element)
6677 {
6678   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6679           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6680           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6681           element);
6682 }
6683
6684 static void RedrawAllLightSwitchesAndInvisibleElements()
6685 {
6686   int x, y;
6687
6688   SCAN_PLAYFIELD(x, y)
6689   {
6690     int element = Feld[x][y];
6691
6692     if (element == EL_LIGHT_SWITCH &&
6693         game.light_time_left > 0)
6694     {
6695       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6696       TEST_DrawLevelField(x, y);
6697     }
6698     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6699              game.light_time_left == 0)
6700     {
6701       Feld[x][y] = EL_LIGHT_SWITCH;
6702       TEST_DrawLevelField(x, y);
6703     }
6704     else if (element == EL_EMC_DRIPPER &&
6705              game.light_time_left > 0)
6706     {
6707       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6708       TEST_DrawLevelField(x, y);
6709     }
6710     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6711              game.light_time_left == 0)
6712     {
6713       Feld[x][y] = EL_EMC_DRIPPER;
6714       TEST_DrawLevelField(x, y);
6715     }
6716     else if (element == EL_INVISIBLE_STEELWALL ||
6717              element == EL_INVISIBLE_WALL ||
6718              element == EL_INVISIBLE_SAND)
6719     {
6720       if (game.light_time_left > 0)
6721         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6722
6723       TEST_DrawLevelField(x, y);
6724
6725       /* uncrumble neighbour fields, if needed */
6726       if (element == EL_INVISIBLE_SAND)
6727         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6728     }
6729     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6730              element == EL_INVISIBLE_WALL_ACTIVE ||
6731              element == EL_INVISIBLE_SAND_ACTIVE)
6732     {
6733       if (game.light_time_left == 0)
6734         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6735
6736       TEST_DrawLevelField(x, y);
6737
6738       /* re-crumble neighbour fields, if needed */
6739       if (element == EL_INVISIBLE_SAND)
6740         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6741     }
6742   }
6743 }
6744
6745 static void RedrawAllInvisibleElementsForLenses()
6746 {
6747   int x, y;
6748
6749   SCAN_PLAYFIELD(x, y)
6750   {
6751     int element = Feld[x][y];
6752
6753     if (element == EL_EMC_DRIPPER &&
6754         game.lenses_time_left > 0)
6755     {
6756       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6757       TEST_DrawLevelField(x, y);
6758     }
6759     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6760              game.lenses_time_left == 0)
6761     {
6762       Feld[x][y] = EL_EMC_DRIPPER;
6763       TEST_DrawLevelField(x, y);
6764     }
6765     else if (element == EL_INVISIBLE_STEELWALL ||
6766              element == EL_INVISIBLE_WALL ||
6767              element == EL_INVISIBLE_SAND)
6768     {
6769       if (game.lenses_time_left > 0)
6770         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6771
6772       TEST_DrawLevelField(x, y);
6773
6774       /* uncrumble neighbour fields, if needed */
6775       if (element == EL_INVISIBLE_SAND)
6776         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6777     }
6778     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6779              element == EL_INVISIBLE_WALL_ACTIVE ||
6780              element == EL_INVISIBLE_SAND_ACTIVE)
6781     {
6782       if (game.lenses_time_left == 0)
6783         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6784
6785       TEST_DrawLevelField(x, y);
6786
6787       /* re-crumble neighbour fields, if needed */
6788       if (element == EL_INVISIBLE_SAND)
6789         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6790     }
6791   }
6792 }
6793
6794 static void RedrawAllInvisibleElementsForMagnifier()
6795 {
6796   int x, y;
6797
6798   SCAN_PLAYFIELD(x, y)
6799   {
6800     int element = Feld[x][y];
6801
6802     if (element == EL_EMC_FAKE_GRASS &&
6803         game.magnify_time_left > 0)
6804     {
6805       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6806       TEST_DrawLevelField(x, y);
6807     }
6808     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6809              game.magnify_time_left == 0)
6810     {
6811       Feld[x][y] = EL_EMC_FAKE_GRASS;
6812       TEST_DrawLevelField(x, y);
6813     }
6814     else if (IS_GATE_GRAY(element) &&
6815              game.magnify_time_left > 0)
6816     {
6817       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6818                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6819                     IS_EM_GATE_GRAY(element) ?
6820                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6821                     IS_EMC_GATE_GRAY(element) ?
6822                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6823                     IS_DC_GATE_GRAY(element) ?
6824                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6825                     element);
6826       TEST_DrawLevelField(x, y);
6827     }
6828     else if (IS_GATE_GRAY_ACTIVE(element) &&
6829              game.magnify_time_left == 0)
6830     {
6831       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6832                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6833                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6834                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6835                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6836                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6837                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6838                     EL_DC_GATE_WHITE_GRAY :
6839                     element);
6840       TEST_DrawLevelField(x, y);
6841     }
6842   }
6843 }
6844
6845 static void ToggleLightSwitch(int x, int y)
6846 {
6847   int element = Feld[x][y];
6848
6849   game.light_time_left =
6850     (element == EL_LIGHT_SWITCH ?
6851      level.time_light * FRAMES_PER_SECOND : 0);
6852
6853   RedrawAllLightSwitchesAndInvisibleElements();
6854 }
6855
6856 static void ActivateTimegateSwitch(int x, int y)
6857 {
6858   int xx, yy;
6859
6860   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6861
6862   SCAN_PLAYFIELD(xx, yy)
6863   {
6864     int element = Feld[xx][yy];
6865
6866     if (element == EL_TIMEGATE_CLOSED ||
6867         element == EL_TIMEGATE_CLOSING)
6868     {
6869       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6870       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6871     }
6872
6873     /*
6874     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6875     {
6876       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6877       TEST_DrawLevelField(xx, yy);
6878     }
6879     */
6880
6881   }
6882
6883 #if 1
6884   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6885                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6886 #else
6887   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6888 #endif
6889 }
6890
6891 void Impact(int x, int y)
6892 {
6893   boolean last_line = (y == lev_fieldy - 1);
6894   boolean object_hit = FALSE;
6895   boolean impact = (last_line || object_hit);
6896   int element = Feld[x][y];
6897   int smashed = EL_STEELWALL;
6898
6899   if (!last_line)       /* check if element below was hit */
6900   {
6901     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6902       return;
6903
6904     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6905                                          MovDir[x][y + 1] != MV_DOWN ||
6906                                          MovPos[x][y + 1] <= TILEY / 2));
6907
6908     /* do not smash moving elements that left the smashed field in time */
6909     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6910         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6911       object_hit = FALSE;
6912
6913 #if USE_QUICKSAND_IMPACT_BUGFIX
6914     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6915     {
6916       RemoveMovingField(x, y + 1);
6917       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6918       Feld[x][y + 2] = EL_ROCK;
6919       TEST_DrawLevelField(x, y + 2);
6920
6921       object_hit = TRUE;
6922     }
6923
6924     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6925     {
6926       RemoveMovingField(x, y + 1);
6927       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6928       Feld[x][y + 2] = EL_ROCK;
6929       TEST_DrawLevelField(x, y + 2);
6930
6931       object_hit = TRUE;
6932     }
6933 #endif
6934
6935     if (object_hit)
6936       smashed = MovingOrBlocked2Element(x, y + 1);
6937
6938     impact = (last_line || object_hit);
6939   }
6940
6941   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6942   {
6943     SplashAcid(x, y + 1);
6944     return;
6945   }
6946
6947   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6948   /* only reset graphic animation if graphic really changes after impact */
6949   if (impact &&
6950       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6951   {
6952     ResetGfxAnimation(x, y);
6953     TEST_DrawLevelField(x, y);
6954   }
6955
6956   if (impact && CAN_EXPLODE_IMPACT(element))
6957   {
6958     Bang(x, y);
6959     return;
6960   }
6961   else if (impact && element == EL_PEARL &&
6962            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6963   {
6964     ResetGfxAnimation(x, y);
6965
6966     Feld[x][y] = EL_PEARL_BREAKING;
6967     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968     return;
6969   }
6970   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6971   {
6972     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6973
6974     return;
6975   }
6976
6977   if (impact && element == EL_AMOEBA_DROP)
6978   {
6979     if (object_hit && IS_PLAYER(x, y + 1))
6980       KillPlayerUnlessEnemyProtected(x, y + 1);
6981     else if (object_hit && smashed == EL_PENGUIN)
6982       Bang(x, y + 1);
6983     else
6984     {
6985       Feld[x][y] = EL_AMOEBA_GROWING;
6986       Store[x][y] = EL_AMOEBA_WET;
6987
6988       ResetRandomAnimationValue(x, y);
6989     }
6990     return;
6991   }
6992
6993   if (object_hit)               /* check which object was hit */
6994   {
6995     if ((CAN_PASS_MAGIC_WALL(element) && 
6996          (smashed == EL_MAGIC_WALL ||
6997           smashed == EL_BD_MAGIC_WALL)) ||
6998         (CAN_PASS_DC_MAGIC_WALL(element) &&
6999          smashed == EL_DC_MAGIC_WALL))
7000     {
7001       int xx, yy;
7002       int activated_magic_wall =
7003         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7004          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7005          EL_DC_MAGIC_WALL_ACTIVE);
7006
7007       /* activate magic wall / mill */
7008       SCAN_PLAYFIELD(xx, yy)
7009       {
7010         if (Feld[xx][yy] == smashed)
7011           Feld[xx][yy] = activated_magic_wall;
7012       }
7013
7014       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7015       game.magic_wall_active = TRUE;
7016
7017       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7018                             SND_MAGIC_WALL_ACTIVATING :
7019                             smashed == EL_BD_MAGIC_WALL ?
7020                             SND_BD_MAGIC_WALL_ACTIVATING :
7021                             SND_DC_MAGIC_WALL_ACTIVATING));
7022     }
7023
7024     if (IS_PLAYER(x, y + 1))
7025     {
7026       if (CAN_SMASH_PLAYER(element))
7027       {
7028         KillPlayerUnlessEnemyProtected(x, y + 1);
7029         return;
7030       }
7031     }
7032     else if (smashed == EL_PENGUIN)
7033     {
7034       if (CAN_SMASH_PLAYER(element))
7035       {
7036         Bang(x, y + 1);
7037         return;
7038       }
7039     }
7040     else if (element == EL_BD_DIAMOND)
7041     {
7042       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7043       {
7044         Bang(x, y + 1);
7045         return;
7046       }
7047     }
7048     else if (((element == EL_SP_INFOTRON ||
7049                element == EL_SP_ZONK) &&
7050               (smashed == EL_SP_SNIKSNAK ||
7051                smashed == EL_SP_ELECTRON ||
7052                smashed == EL_SP_DISK_ORANGE)) ||
7053              (element == EL_SP_INFOTRON &&
7054               smashed == EL_SP_DISK_YELLOW))
7055     {
7056       Bang(x, y + 1);
7057       return;
7058     }
7059     else if (CAN_SMASH_EVERYTHING(element))
7060     {
7061       if (IS_CLASSIC_ENEMY(smashed) ||
7062           CAN_EXPLODE_SMASHED(smashed))
7063       {
7064         Bang(x, y + 1);
7065         return;
7066       }
7067       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7068       {
7069         if (smashed == EL_LAMP ||
7070             smashed == EL_LAMP_ACTIVE)
7071         {
7072           Bang(x, y + 1);
7073           return;
7074         }
7075         else if (smashed == EL_NUT)
7076         {
7077           Feld[x][y + 1] = EL_NUT_BREAKING;
7078           PlayLevelSound(x, y, SND_NUT_BREAKING);
7079           RaiseScoreElement(EL_NUT);
7080           return;
7081         }
7082         else if (smashed == EL_PEARL)
7083         {
7084           ResetGfxAnimation(x, y);
7085
7086           Feld[x][y + 1] = EL_PEARL_BREAKING;
7087           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7088           return;
7089         }
7090         else if (smashed == EL_DIAMOND)
7091         {
7092           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7093           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7094           return;
7095         }
7096         else if (IS_BELT_SWITCH(smashed))
7097         {
7098           ToggleBeltSwitch(x, y + 1);
7099         }
7100         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7101                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7102                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7103                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7104         {
7105           ToggleSwitchgateSwitch(x, y + 1);
7106         }
7107         else if (smashed == EL_LIGHT_SWITCH ||
7108                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7109         {
7110           ToggleLightSwitch(x, y + 1);
7111         }
7112         else
7113         {
7114 #if 0
7115           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7116 #endif
7117
7118           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7119
7120           CheckElementChangeBySide(x, y + 1, smashed, element,
7121                                    CE_SWITCHED, CH_SIDE_TOP);
7122           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7123                                             CH_SIDE_TOP);
7124         }
7125       }
7126       else
7127       {
7128         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7129       }
7130     }
7131   }
7132
7133   /* play sound of magic wall / mill */
7134   if (!last_line &&
7135       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7136        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7137        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7138   {
7139     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7140       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7141     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7142       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7143     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7144       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7145
7146     return;
7147   }
7148
7149   /* play sound of object that hits the ground */
7150   if (last_line || object_hit)
7151     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7152 }
7153
7154 inline static void TurnRoundExt(int x, int y)
7155 {
7156   static struct
7157   {
7158     int dx, dy;
7159   } move_xy[] =
7160   {
7161     {  0,  0 },
7162     { -1,  0 },
7163     { +1,  0 },
7164     {  0,  0 },
7165     {  0, -1 },
7166     {  0,  0 }, { 0, 0 }, { 0, 0 },
7167     {  0, +1 }
7168   };
7169   static struct
7170   {
7171     int left, right, back;
7172   } turn[] =
7173   {
7174     { 0,        0,              0        },
7175     { MV_DOWN,  MV_UP,          MV_RIGHT },
7176     { MV_UP,    MV_DOWN,        MV_LEFT  },
7177     { 0,        0,              0        },
7178     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7179     { 0,        0,              0        },
7180     { 0,        0,              0        },
7181     { 0,        0,              0        },
7182     { MV_RIGHT, MV_LEFT,        MV_UP    }
7183   };
7184
7185   int element = Feld[x][y];
7186   int move_pattern = element_info[element].move_pattern;
7187
7188   int old_move_dir = MovDir[x][y];
7189   int left_dir  = turn[old_move_dir].left;
7190   int right_dir = turn[old_move_dir].right;
7191   int back_dir  = turn[old_move_dir].back;
7192
7193   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7194   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7195   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7196   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7197
7198   int left_x  = x + left_dx,  left_y  = y + left_dy;
7199   int right_x = x + right_dx, right_y = y + right_dy;
7200   int move_x  = x + move_dx,  move_y  = y + move_dy;
7201
7202   int xx, yy;
7203
7204   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7205   {
7206     TestIfBadThingTouchesOtherBadThing(x, y);
7207
7208     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7209       MovDir[x][y] = right_dir;
7210     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7211       MovDir[x][y] = left_dir;
7212
7213     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7214       MovDelay[x][y] = 9;
7215     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7216       MovDelay[x][y] = 1;
7217   }
7218   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7219   {
7220     TestIfBadThingTouchesOtherBadThing(x, y);
7221
7222     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7223       MovDir[x][y] = left_dir;
7224     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7225       MovDir[x][y] = right_dir;
7226
7227     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7228       MovDelay[x][y] = 9;
7229     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7230       MovDelay[x][y] = 1;
7231   }
7232   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7233   {
7234     TestIfBadThingTouchesOtherBadThing(x, y);
7235
7236     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7237       MovDir[x][y] = left_dir;
7238     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7239       MovDir[x][y] = right_dir;
7240
7241     if (MovDir[x][y] != old_move_dir)
7242       MovDelay[x][y] = 9;
7243   }
7244   else if (element == EL_YAMYAM)
7245   {
7246     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7247     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7248
7249     if (can_turn_left && can_turn_right)
7250       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7251     else if (can_turn_left)
7252       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7253     else if (can_turn_right)
7254       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7255     else
7256       MovDir[x][y] = back_dir;
7257
7258     MovDelay[x][y] = 16 + 16 * RND(3);
7259   }
7260   else if (element == EL_DARK_YAMYAM)
7261   {
7262     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7263                                                          left_x, left_y);
7264     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7265                                                          right_x, right_y);
7266
7267     if (can_turn_left && can_turn_right)
7268       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7269     else if (can_turn_left)
7270       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7271     else if (can_turn_right)
7272       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7273     else
7274       MovDir[x][y] = back_dir;
7275
7276     MovDelay[x][y] = 16 + 16 * RND(3);
7277   }
7278   else if (element == EL_PACMAN)
7279   {
7280     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7281     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7282
7283     if (can_turn_left && can_turn_right)
7284       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7285     else if (can_turn_left)
7286       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7287     else if (can_turn_right)
7288       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7289     else
7290       MovDir[x][y] = back_dir;
7291
7292     MovDelay[x][y] = 6 + RND(40);
7293   }
7294   else if (element == EL_PIG)
7295   {
7296     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7297     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7298     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7299     boolean should_turn_left, should_turn_right, should_move_on;
7300     int rnd_value = 24;
7301     int rnd = RND(rnd_value);
7302
7303     should_turn_left = (can_turn_left &&
7304                         (!can_move_on ||
7305                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7306                                                    y + back_dy + left_dy)));
7307     should_turn_right = (can_turn_right &&
7308                          (!can_move_on ||
7309                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7310                                                     y + back_dy + right_dy)));
7311     should_move_on = (can_move_on &&
7312                       (!can_turn_left ||
7313                        !can_turn_right ||
7314                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7315                                                  y + move_dy + left_dy) ||
7316                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7317                                                  y + move_dy + right_dy)));
7318
7319     if (should_turn_left || should_turn_right || should_move_on)
7320     {
7321       if (should_turn_left && should_turn_right && should_move_on)
7322         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7323                         rnd < 2 * rnd_value / 3 ? right_dir :
7324                         old_move_dir);
7325       else if (should_turn_left && should_turn_right)
7326         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7327       else if (should_turn_left && should_move_on)
7328         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7329       else if (should_turn_right && should_move_on)
7330         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7331       else if (should_turn_left)
7332         MovDir[x][y] = left_dir;
7333       else if (should_turn_right)
7334         MovDir[x][y] = right_dir;
7335       else if (should_move_on)
7336         MovDir[x][y] = old_move_dir;
7337     }
7338     else if (can_move_on && rnd > rnd_value / 8)
7339       MovDir[x][y] = old_move_dir;
7340     else if (can_turn_left && can_turn_right)
7341       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7342     else if (can_turn_left && rnd > rnd_value / 8)
7343       MovDir[x][y] = left_dir;
7344     else if (can_turn_right && rnd > rnd_value/8)
7345       MovDir[x][y] = right_dir;
7346     else
7347       MovDir[x][y] = back_dir;
7348
7349     xx = x + move_xy[MovDir[x][y]].dx;
7350     yy = y + move_xy[MovDir[x][y]].dy;
7351
7352     if (!IN_LEV_FIELD(xx, yy) ||
7353         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7354       MovDir[x][y] = old_move_dir;
7355
7356     MovDelay[x][y] = 0;
7357   }
7358   else if (element == EL_DRAGON)
7359   {
7360     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7361     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7362     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7363     int rnd_value = 24;
7364     int rnd = RND(rnd_value);
7365
7366     if (can_move_on && rnd > rnd_value / 8)
7367       MovDir[x][y] = old_move_dir;
7368     else if (can_turn_left && can_turn_right)
7369       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7370     else if (can_turn_left && rnd > rnd_value / 8)
7371       MovDir[x][y] = left_dir;
7372     else if (can_turn_right && rnd > rnd_value / 8)
7373       MovDir[x][y] = right_dir;
7374     else
7375       MovDir[x][y] = back_dir;
7376
7377     xx = x + move_xy[MovDir[x][y]].dx;
7378     yy = y + move_xy[MovDir[x][y]].dy;
7379
7380     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7381       MovDir[x][y] = old_move_dir;
7382
7383     MovDelay[x][y] = 0;
7384   }
7385   else if (element == EL_MOLE)
7386   {
7387     boolean can_move_on =
7388       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7389                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7390                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7391     if (!can_move_on)
7392     {
7393       boolean can_turn_left =
7394         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7395                               IS_AMOEBOID(Feld[left_x][left_y])));
7396
7397       boolean can_turn_right =
7398         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7399                               IS_AMOEBOID(Feld[right_x][right_y])));
7400
7401       if (can_turn_left && can_turn_right)
7402         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7403       else if (can_turn_left)
7404         MovDir[x][y] = left_dir;
7405       else
7406         MovDir[x][y] = right_dir;
7407     }
7408
7409     if (MovDir[x][y] != old_move_dir)
7410       MovDelay[x][y] = 9;
7411   }
7412   else if (element == EL_BALLOON)
7413   {
7414     MovDir[x][y] = game.wind_direction;
7415     MovDelay[x][y] = 0;
7416   }
7417   else if (element == EL_SPRING)
7418   {
7419 #if USE_NEW_SPRING_BUMPER
7420     if (MovDir[x][y] & MV_HORIZONTAL)
7421     {
7422       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7423           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7424       {
7425         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7426         ResetGfxAnimation(move_x, move_y);
7427         TEST_DrawLevelField(move_x, move_y);
7428
7429         MovDir[x][y] = back_dir;
7430       }
7431       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7432                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7433         MovDir[x][y] = MV_NONE;
7434     }
7435 #else
7436     if (MovDir[x][y] & MV_HORIZONTAL &&
7437         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7438          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7439       MovDir[x][y] = MV_NONE;
7440 #endif
7441
7442     MovDelay[x][y] = 0;
7443   }
7444   else if (element == EL_ROBOT ||
7445            element == EL_SATELLITE ||
7446            element == EL_PENGUIN ||
7447            element == EL_EMC_ANDROID)
7448   {
7449     int attr_x = -1, attr_y = -1;
7450
7451     if (AllPlayersGone)
7452     {
7453       attr_x = ExitX;
7454       attr_y = ExitY;
7455     }
7456     else
7457     {
7458       int i;
7459
7460       for (i = 0; i < MAX_PLAYERS; i++)
7461       {
7462         struct PlayerInfo *player = &stored_player[i];
7463         int jx = player->jx, jy = player->jy;
7464
7465         if (!player->active)
7466           continue;
7467
7468         if (attr_x == -1 ||
7469             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7470         {
7471           attr_x = jx;
7472           attr_y = jy;
7473         }
7474       }
7475     }
7476
7477     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7478         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7479          game.engine_version < VERSION_IDENT(3,1,0,0)))
7480     {
7481       attr_x = ZX;
7482       attr_y = ZY;
7483     }
7484
7485     if (element == EL_PENGUIN)
7486     {
7487       int i;
7488       static int xy[4][2] =
7489       {
7490         { 0, -1 },
7491         { -1, 0 },
7492         { +1, 0 },
7493         { 0, +1 }
7494       };
7495
7496       for (i = 0; i < NUM_DIRECTIONS; i++)
7497       {
7498         int ex = x + xy[i][0];
7499         int ey = y + xy[i][1];
7500
7501         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7502                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7503                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7504                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7505         {
7506           attr_x = ex;
7507           attr_y = ey;
7508           break;
7509         }
7510       }
7511     }
7512
7513     MovDir[x][y] = MV_NONE;
7514     if (attr_x < x)
7515       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7516     else if (attr_x > x)
7517       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7518     if (attr_y < y)
7519       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7520     else if (attr_y > y)
7521       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7522
7523     if (element == EL_ROBOT)
7524     {
7525       int newx, newy;
7526
7527       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7528         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7529       Moving2Blocked(x, y, &newx, &newy);
7530
7531       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7532         MovDelay[x][y] = 8 + 8 * !RND(3);
7533       else
7534         MovDelay[x][y] = 16;
7535     }
7536     else if (element == EL_PENGUIN)
7537     {
7538       int newx, newy;
7539
7540       MovDelay[x][y] = 1;
7541
7542       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7543       {
7544         boolean first_horiz = RND(2);
7545         int new_move_dir = MovDir[x][y];
7546
7547         MovDir[x][y] =
7548           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7549         Moving2Blocked(x, y, &newx, &newy);
7550
7551         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7552           return;
7553
7554         MovDir[x][y] =
7555           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7556         Moving2Blocked(x, y, &newx, &newy);
7557
7558         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7559           return;
7560
7561         MovDir[x][y] = old_move_dir;
7562         return;
7563       }
7564     }
7565     else if (element == EL_SATELLITE)
7566     {
7567       int newx, newy;
7568
7569       MovDelay[x][y] = 1;
7570
7571       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7572       {
7573         boolean first_horiz = RND(2);
7574         int new_move_dir = MovDir[x][y];
7575
7576         MovDir[x][y] =
7577           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7578         Moving2Blocked(x, y, &newx, &newy);
7579
7580         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7581           return;
7582
7583         MovDir[x][y] =
7584           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7585         Moving2Blocked(x, y, &newx, &newy);
7586
7587         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7588           return;
7589
7590         MovDir[x][y] = old_move_dir;
7591         return;
7592       }
7593     }
7594     else if (element == EL_EMC_ANDROID)
7595     {
7596       static int check_pos[16] =
7597       {
7598         -1,             /*  0 => (invalid)          */
7599         7,              /*  1 => MV_LEFT            */
7600         3,              /*  2 => MV_RIGHT           */
7601         -1,             /*  3 => (invalid)          */
7602         1,              /*  4 =>            MV_UP   */
7603         0,              /*  5 => MV_LEFT  | MV_UP   */
7604         2,              /*  6 => MV_RIGHT | MV_UP   */
7605         -1,             /*  7 => (invalid)          */
7606         5,              /*  8 =>            MV_DOWN */
7607         6,              /*  9 => MV_LEFT  | MV_DOWN */
7608         4,              /* 10 => MV_RIGHT | MV_DOWN */
7609         -1,             /* 11 => (invalid)          */
7610         -1,             /* 12 => (invalid)          */
7611         -1,             /* 13 => (invalid)          */
7612         -1,             /* 14 => (invalid)          */
7613         -1,             /* 15 => (invalid)          */
7614       };
7615       static struct
7616       {
7617         int dx, dy;
7618         int dir;
7619       } check_xy[8] =
7620       {
7621         { -1, -1,       MV_LEFT  | MV_UP   },
7622         {  0, -1,                  MV_UP   },
7623         { +1, -1,       MV_RIGHT | MV_UP   },
7624         { +1,  0,       MV_RIGHT           },
7625         { +1, +1,       MV_RIGHT | MV_DOWN },
7626         {  0, +1,                  MV_DOWN },
7627         { -1, +1,       MV_LEFT  | MV_DOWN },
7628         { -1,  0,       MV_LEFT            },
7629       };
7630       int start_pos, check_order;
7631       boolean can_clone = FALSE;
7632       int i;
7633
7634       /* check if there is any free field around current position */
7635       for (i = 0; i < 8; i++)
7636       {
7637         int newx = x + check_xy[i].dx;
7638         int newy = y + check_xy[i].dy;
7639
7640         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7641         {
7642           can_clone = TRUE;
7643
7644           break;
7645         }
7646       }
7647
7648       if (can_clone)            /* randomly find an element to clone */
7649       {
7650         can_clone = FALSE;
7651
7652         start_pos = check_pos[RND(8)];
7653         check_order = (RND(2) ? -1 : +1);
7654
7655         for (i = 0; i < 8; i++)
7656         {
7657           int pos_raw = start_pos + i * check_order;
7658           int pos = (pos_raw + 8) % 8;
7659           int newx = x + check_xy[pos].dx;
7660           int newy = y + check_xy[pos].dy;
7661
7662           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7663           {
7664             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7665             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7666
7667             Store[x][y] = Feld[newx][newy];
7668
7669             can_clone = TRUE;
7670
7671             break;
7672           }
7673         }
7674       }
7675
7676       if (can_clone)            /* randomly find a direction to move */
7677       {
7678         can_clone = FALSE;
7679
7680         start_pos = check_pos[RND(8)];
7681         check_order = (RND(2) ? -1 : +1);
7682
7683         for (i = 0; i < 8; i++)
7684         {
7685           int pos_raw = start_pos + i * check_order;
7686           int pos = (pos_raw + 8) % 8;
7687           int newx = x + check_xy[pos].dx;
7688           int newy = y + check_xy[pos].dy;
7689           int new_move_dir = check_xy[pos].dir;
7690
7691           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7692           {
7693             MovDir[x][y] = new_move_dir;
7694             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7695
7696             can_clone = TRUE;
7697
7698             break;
7699           }
7700         }
7701       }
7702
7703       if (can_clone)            /* cloning and moving successful */
7704         return;
7705
7706       /* cannot clone -- try to move towards player */
7707
7708       start_pos = check_pos[MovDir[x][y] & 0x0f];
7709       check_order = (RND(2) ? -1 : +1);
7710
7711       for (i = 0; i < 3; i++)
7712       {
7713         /* first check start_pos, then previous/next or (next/previous) pos */
7714         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7715         int pos = (pos_raw + 8) % 8;
7716         int newx = x + check_xy[pos].dx;
7717         int newy = y + check_xy[pos].dy;
7718         int new_move_dir = check_xy[pos].dir;
7719
7720         if (IS_PLAYER(newx, newy))
7721           break;
7722
7723         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7724         {
7725           MovDir[x][y] = new_move_dir;
7726           MovDelay[x][y] = level.android_move_time * 8 + 1;
7727
7728           break;
7729         }
7730       }
7731     }
7732   }
7733   else if (move_pattern == MV_TURNING_LEFT ||
7734            move_pattern == MV_TURNING_RIGHT ||
7735            move_pattern == MV_TURNING_LEFT_RIGHT ||
7736            move_pattern == MV_TURNING_RIGHT_LEFT ||
7737            move_pattern == MV_TURNING_RANDOM ||
7738            move_pattern == MV_ALL_DIRECTIONS)
7739   {
7740     boolean can_turn_left =
7741       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7742     boolean can_turn_right =
7743       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7744
7745     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7746       return;
7747
7748     if (move_pattern == MV_TURNING_LEFT)
7749       MovDir[x][y] = left_dir;
7750     else if (move_pattern == MV_TURNING_RIGHT)
7751       MovDir[x][y] = right_dir;
7752     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7753       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7754     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7755       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7756     else if (move_pattern == MV_TURNING_RANDOM)
7757       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7758                       can_turn_right && !can_turn_left ? right_dir :
7759                       RND(2) ? left_dir : right_dir);
7760     else if (can_turn_left && can_turn_right)
7761       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7762     else if (can_turn_left)
7763       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7764     else if (can_turn_right)
7765       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7766     else
7767       MovDir[x][y] = back_dir;
7768
7769     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7770   }
7771   else if (move_pattern == MV_HORIZONTAL ||
7772            move_pattern == MV_VERTICAL)
7773   {
7774     if (move_pattern & old_move_dir)
7775       MovDir[x][y] = back_dir;
7776     else if (move_pattern == MV_HORIZONTAL)
7777       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7778     else if (move_pattern == MV_VERTICAL)
7779       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7780
7781     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7782   }
7783   else if (move_pattern & MV_ANY_DIRECTION)
7784   {
7785     MovDir[x][y] = move_pattern;
7786     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7787   }
7788   else if (move_pattern & MV_WIND_DIRECTION)
7789   {
7790     MovDir[x][y] = game.wind_direction;
7791     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7792   }
7793   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7794   {
7795     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7796       MovDir[x][y] = left_dir;
7797     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7798       MovDir[x][y] = right_dir;
7799
7800     if (MovDir[x][y] != old_move_dir)
7801       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7802   }
7803   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7804   {
7805     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7806       MovDir[x][y] = right_dir;
7807     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7808       MovDir[x][y] = left_dir;
7809
7810     if (MovDir[x][y] != old_move_dir)
7811       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7812   }
7813   else if (move_pattern == MV_TOWARDS_PLAYER ||
7814            move_pattern == MV_AWAY_FROM_PLAYER)
7815   {
7816     int attr_x = -1, attr_y = -1;
7817     int newx, newy;
7818     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7819
7820     if (AllPlayersGone)
7821     {
7822       attr_x = ExitX;
7823       attr_y = ExitY;
7824     }
7825     else
7826     {
7827       int i;
7828
7829       for (i = 0; i < MAX_PLAYERS; i++)
7830       {
7831         struct PlayerInfo *player = &stored_player[i];
7832         int jx = player->jx, jy = player->jy;
7833
7834         if (!player->active)
7835           continue;
7836
7837         if (attr_x == -1 ||
7838             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7839         {
7840           attr_x = jx;
7841           attr_y = jy;
7842         }
7843       }
7844     }
7845
7846     MovDir[x][y] = MV_NONE;
7847     if (attr_x < x)
7848       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7849     else if (attr_x > x)
7850       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7851     if (attr_y < y)
7852       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7853     else if (attr_y > y)
7854       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7855
7856     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7857
7858     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7859     {
7860       boolean first_horiz = RND(2);
7861       int new_move_dir = MovDir[x][y];
7862
7863       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7864       {
7865         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7866         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7867
7868         return;
7869       }
7870
7871       MovDir[x][y] =
7872         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7873       Moving2Blocked(x, y, &newx, &newy);
7874
7875       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7876         return;
7877
7878       MovDir[x][y] =
7879         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7880       Moving2Blocked(x, y, &newx, &newy);
7881
7882       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7883         return;
7884
7885       MovDir[x][y] = old_move_dir;
7886     }
7887   }
7888   else if (move_pattern == MV_WHEN_PUSHED ||
7889            move_pattern == MV_WHEN_DROPPED)
7890   {
7891     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7892       MovDir[x][y] = MV_NONE;
7893
7894     MovDelay[x][y] = 0;
7895   }
7896   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7897   {
7898     static int test_xy[7][2] =
7899     {
7900       { 0, -1 },
7901       { -1, 0 },
7902       { +1, 0 },
7903       { 0, +1 },
7904       { 0, -1 },
7905       { -1, 0 },
7906       { +1, 0 },
7907     };
7908     static int test_dir[7] =
7909     {
7910       MV_UP,
7911       MV_LEFT,
7912       MV_RIGHT,
7913       MV_DOWN,
7914       MV_UP,
7915       MV_LEFT,
7916       MV_RIGHT,
7917     };
7918     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7919     int move_preference = -1000000;     /* start with very low preference */
7920     int new_move_dir = MV_NONE;
7921     int start_test = RND(4);
7922     int i;
7923
7924     for (i = 0; i < NUM_DIRECTIONS; i++)
7925     {
7926       int move_dir = test_dir[start_test + i];
7927       int move_dir_preference;
7928
7929       xx = x + test_xy[start_test + i][0];
7930       yy = y + test_xy[start_test + i][1];
7931
7932       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7933           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7934       {
7935         new_move_dir = move_dir;
7936
7937         break;
7938       }
7939
7940       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7941         continue;
7942
7943       move_dir_preference = -1 * RunnerVisit[xx][yy];
7944       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7945         move_dir_preference = PlayerVisit[xx][yy];
7946
7947       if (move_dir_preference > move_preference)
7948       {
7949         /* prefer field that has not been visited for the longest time */
7950         move_preference = move_dir_preference;
7951         new_move_dir = move_dir;
7952       }
7953       else if (move_dir_preference == move_preference &&
7954                move_dir == old_move_dir)
7955       {
7956         /* prefer last direction when all directions are preferred equally */
7957         move_preference = move_dir_preference;
7958         new_move_dir = move_dir;
7959       }
7960     }
7961
7962     MovDir[x][y] = new_move_dir;
7963     if (old_move_dir != new_move_dir)
7964       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7965   }
7966 }
7967
7968 static void TurnRound(int x, int y)
7969 {
7970   int direction = MovDir[x][y];
7971
7972   TurnRoundExt(x, y);
7973
7974   GfxDir[x][y] = MovDir[x][y];
7975
7976   if (direction != MovDir[x][y])
7977     GfxFrame[x][y] = 0;
7978
7979   if (MovDelay[x][y])
7980     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7981
7982   ResetGfxFrame(x, y, FALSE);
7983 }
7984
7985 static boolean JustBeingPushed(int x, int y)
7986 {
7987   int i;
7988
7989   for (i = 0; i < MAX_PLAYERS; i++)
7990   {
7991     struct PlayerInfo *player = &stored_player[i];
7992
7993     if (player->active && player->is_pushing && player->MovPos)
7994     {
7995       int next_jx = player->jx + (player->jx - player->last_jx);
7996       int next_jy = player->jy + (player->jy - player->last_jy);
7997
7998       if (x == next_jx && y == next_jy)
7999         return TRUE;
8000     }
8001   }
8002
8003   return FALSE;
8004 }
8005
8006 void StartMoving(int x, int y)
8007 {
8008   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
8009   int element = Feld[x][y];
8010
8011   if (Stop[x][y])
8012     return;
8013
8014   if (MovDelay[x][y] == 0)
8015     GfxAction[x][y] = ACTION_DEFAULT;
8016
8017   if (CAN_FALL(element) && y < lev_fieldy - 1)
8018   {
8019     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8020         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8021       if (JustBeingPushed(x, y))
8022         return;
8023
8024     if (element == EL_QUICKSAND_FULL)
8025     {
8026       if (IS_FREE(x, y + 1))
8027       {
8028         InitMovingField(x, y, MV_DOWN);
8029         started_moving = TRUE;
8030
8031         Feld[x][y] = EL_QUICKSAND_EMPTYING;
8032 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8033         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8034           Store[x][y] = EL_ROCK;
8035 #else
8036         Store[x][y] = EL_ROCK;
8037 #endif
8038
8039         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8040       }
8041       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8042       {
8043         if (!MovDelay[x][y])
8044         {
8045           MovDelay[x][y] = TILEY + 1;
8046
8047           ResetGfxAnimation(x, y);
8048           ResetGfxAnimation(x, y + 1);
8049         }
8050
8051         if (MovDelay[x][y])
8052         {
8053           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8054           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8055
8056           MovDelay[x][y]--;
8057           if (MovDelay[x][y])
8058             return;
8059         }
8060
8061         Feld[x][y] = EL_QUICKSAND_EMPTY;
8062         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8063         Store[x][y + 1] = Store[x][y];
8064         Store[x][y] = 0;
8065
8066         PlayLevelSoundAction(x, y, ACTION_FILLING);
8067       }
8068       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8069       {
8070         if (!MovDelay[x][y])
8071         {
8072           MovDelay[x][y] = TILEY + 1;
8073
8074           ResetGfxAnimation(x, y);
8075           ResetGfxAnimation(x, y + 1);
8076         }
8077
8078         if (MovDelay[x][y])
8079         {
8080           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8081           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8082
8083           MovDelay[x][y]--;
8084           if (MovDelay[x][y])
8085             return;
8086         }
8087
8088         Feld[x][y] = EL_QUICKSAND_EMPTY;
8089         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8090         Store[x][y + 1] = Store[x][y];
8091         Store[x][y] = 0;
8092
8093         PlayLevelSoundAction(x, y, ACTION_FILLING);
8094       }
8095     }
8096     else if (element == EL_QUICKSAND_FAST_FULL)
8097     {
8098       if (IS_FREE(x, y + 1))
8099       {
8100         InitMovingField(x, y, MV_DOWN);
8101         started_moving = TRUE;
8102
8103         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8104 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8105         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8106           Store[x][y] = EL_ROCK;
8107 #else
8108         Store[x][y] = EL_ROCK;
8109 #endif
8110
8111         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8112       }
8113       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8114       {
8115         if (!MovDelay[x][y])
8116         {
8117           MovDelay[x][y] = TILEY + 1;
8118
8119           ResetGfxAnimation(x, y);
8120           ResetGfxAnimation(x, y + 1);
8121         }
8122
8123         if (MovDelay[x][y])
8124         {
8125           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8126           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8127
8128           MovDelay[x][y]--;
8129           if (MovDelay[x][y])
8130             return;
8131         }
8132
8133         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8134         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8135         Store[x][y + 1] = Store[x][y];
8136         Store[x][y] = 0;
8137
8138         PlayLevelSoundAction(x, y, ACTION_FILLING);
8139       }
8140       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8141       {
8142         if (!MovDelay[x][y])
8143         {
8144           MovDelay[x][y] = TILEY + 1;
8145
8146           ResetGfxAnimation(x, y);
8147           ResetGfxAnimation(x, y + 1);
8148         }
8149
8150         if (MovDelay[x][y])
8151         {
8152           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8153           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8154
8155           MovDelay[x][y]--;
8156           if (MovDelay[x][y])
8157             return;
8158         }
8159
8160         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8161         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8162         Store[x][y + 1] = Store[x][y];
8163         Store[x][y] = 0;
8164
8165         PlayLevelSoundAction(x, y, ACTION_FILLING);
8166       }
8167     }
8168     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8169              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8170     {
8171       InitMovingField(x, y, MV_DOWN);
8172       started_moving = TRUE;
8173
8174       Feld[x][y] = EL_QUICKSAND_FILLING;
8175       Store[x][y] = element;
8176
8177       PlayLevelSoundAction(x, y, ACTION_FILLING);
8178     }
8179     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8180              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8181     {
8182       InitMovingField(x, y, MV_DOWN);
8183       started_moving = TRUE;
8184
8185       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8186       Store[x][y] = element;
8187
8188       PlayLevelSoundAction(x, y, ACTION_FILLING);
8189     }
8190     else if (element == EL_MAGIC_WALL_FULL)
8191     {
8192       if (IS_FREE(x, y + 1))
8193       {
8194         InitMovingField(x, y, MV_DOWN);
8195         started_moving = TRUE;
8196
8197         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8198         Store[x][y] = EL_CHANGED(Store[x][y]);
8199       }
8200       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8201       {
8202         if (!MovDelay[x][y])
8203           MovDelay[x][y] = TILEY / 4 + 1;
8204
8205         if (MovDelay[x][y])
8206         {
8207           MovDelay[x][y]--;
8208           if (MovDelay[x][y])
8209             return;
8210         }
8211
8212         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8213         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8214         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8215         Store[x][y] = 0;
8216       }
8217     }
8218     else if (element == EL_BD_MAGIC_WALL_FULL)
8219     {
8220       if (IS_FREE(x, y + 1))
8221       {
8222         InitMovingField(x, y, MV_DOWN);
8223         started_moving = TRUE;
8224
8225         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8226         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8227       }
8228       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8229       {
8230         if (!MovDelay[x][y])
8231           MovDelay[x][y] = TILEY / 4 + 1;
8232
8233         if (MovDelay[x][y])
8234         {
8235           MovDelay[x][y]--;
8236           if (MovDelay[x][y])
8237             return;
8238         }
8239
8240         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8241         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8242         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8243         Store[x][y] = 0;
8244       }
8245     }
8246     else if (element == EL_DC_MAGIC_WALL_FULL)
8247     {
8248       if (IS_FREE(x, y + 1))
8249       {
8250         InitMovingField(x, y, MV_DOWN);
8251         started_moving = TRUE;
8252
8253         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8254         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8255       }
8256       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8257       {
8258         if (!MovDelay[x][y])
8259           MovDelay[x][y] = TILEY / 4 + 1;
8260
8261         if (MovDelay[x][y])
8262         {
8263           MovDelay[x][y]--;
8264           if (MovDelay[x][y])
8265             return;
8266         }
8267
8268         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8269         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8270         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8271         Store[x][y] = 0;
8272       }
8273     }
8274     else if ((CAN_PASS_MAGIC_WALL(element) &&
8275               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8276                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8277              (CAN_PASS_DC_MAGIC_WALL(element) &&
8278               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8279
8280     {
8281       InitMovingField(x, y, MV_DOWN);
8282       started_moving = TRUE;
8283
8284       Feld[x][y] =
8285         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8286          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8287          EL_DC_MAGIC_WALL_FILLING);
8288       Store[x][y] = element;
8289     }
8290     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8291     {
8292       SplashAcid(x, y + 1);
8293
8294       InitMovingField(x, y, MV_DOWN);
8295       started_moving = TRUE;
8296
8297       Store[x][y] = EL_ACID;
8298     }
8299     else if (
8300 #if USE_FIX_IMPACT_COLLISION
8301              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8302               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8303 #else
8304              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8305               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8306 #endif
8307              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8308               CAN_FALL(element) && WasJustFalling[x][y] &&
8309               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8310
8311              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8312               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8313               (Feld[x][y + 1] == EL_BLOCKED)))
8314     {
8315       /* this is needed for a special case not covered by calling "Impact()"
8316          from "ContinueMoving()": if an element moves to a tile directly below
8317          another element which was just falling on that tile (which was empty
8318          in the previous frame), the falling element above would just stop
8319          instead of smashing the element below (in previous version, the above
8320          element was just checked for "moving" instead of "falling", resulting
8321          in incorrect smashes caused by horizontal movement of the above
8322          element; also, the case of the player being the element to smash was
8323          simply not covered here... :-/ ) */
8324
8325       CheckCollision[x][y] = 0;
8326       CheckImpact[x][y] = 0;
8327
8328       Impact(x, y);
8329     }
8330     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8331     {
8332       if (MovDir[x][y] == MV_NONE)
8333       {
8334         InitMovingField(x, y, MV_DOWN);
8335         started_moving = TRUE;
8336       }
8337     }
8338     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8339     {
8340       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8341         MovDir[x][y] = MV_DOWN;
8342
8343       InitMovingField(x, y, MV_DOWN);
8344       started_moving = TRUE;
8345     }
8346     else if (element == EL_AMOEBA_DROP)
8347     {
8348       Feld[x][y] = EL_AMOEBA_GROWING;
8349       Store[x][y] = EL_AMOEBA_WET;
8350     }
8351     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8352               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8353              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8354              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8355     {
8356       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8357                                 (IS_FREE(x - 1, y + 1) ||
8358                                  Feld[x - 1][y + 1] == EL_ACID));
8359       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8360                                 (IS_FREE(x + 1, y + 1) ||
8361                                  Feld[x + 1][y + 1] == EL_ACID));
8362       boolean can_fall_any  = (can_fall_left || can_fall_right);
8363       boolean can_fall_both = (can_fall_left && can_fall_right);
8364       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8365
8366 #if USE_NEW_ALL_SLIPPERY
8367       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8368       {
8369         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8370           can_fall_right = FALSE;
8371         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8372           can_fall_left = FALSE;
8373         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8374           can_fall_right = FALSE;
8375         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8376           can_fall_left = FALSE;
8377
8378         can_fall_any  = (can_fall_left || can_fall_right);
8379         can_fall_both = FALSE;
8380       }
8381 #else
8382       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8383       {
8384         if (slippery_type == SLIPPERY_ONLY_LEFT)
8385           can_fall_right = FALSE;
8386         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8387           can_fall_left = FALSE;
8388         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8389           can_fall_right = FALSE;
8390         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8391           can_fall_left = FALSE;
8392
8393         can_fall_any  = (can_fall_left || can_fall_right);
8394         can_fall_both = (can_fall_left && can_fall_right);
8395       }
8396 #endif
8397
8398 #if USE_NEW_ALL_SLIPPERY
8399 #else
8400 #if USE_NEW_SP_SLIPPERY
8401       /* !!! better use the same properties as for custom elements here !!! */
8402       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8403                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8404       {
8405         can_fall_right = FALSE;         /* slip down on left side */
8406         can_fall_both = FALSE;
8407       }
8408 #endif
8409 #endif
8410
8411 #if USE_NEW_ALL_SLIPPERY
8412       if (can_fall_both)
8413       {
8414         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8415           can_fall_right = FALSE;       /* slip down on left side */
8416         else
8417           can_fall_left = !(can_fall_right = RND(2));
8418
8419         can_fall_both = FALSE;
8420       }
8421 #else
8422       if (can_fall_both)
8423       {
8424         if (game.emulation == EMU_BOULDERDASH ||
8425             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8426           can_fall_right = FALSE;       /* slip down on left side */
8427         else
8428           can_fall_left = !(can_fall_right = RND(2));
8429
8430         can_fall_both = FALSE;
8431       }
8432 #endif
8433
8434       if (can_fall_any)
8435       {
8436         /* if not determined otherwise, prefer left side for slipping down */
8437         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8438         started_moving = TRUE;
8439       }
8440     }
8441 #if 0
8442     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8443 #else
8444     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8445 #endif
8446     {
8447       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8448       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8449       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8450       int belt_dir = game.belt_dir[belt_nr];
8451
8452       if ((belt_dir == MV_LEFT  && left_is_free) ||
8453           (belt_dir == MV_RIGHT && right_is_free))
8454       {
8455         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8456
8457         InitMovingField(x, y, belt_dir);
8458         started_moving = TRUE;
8459
8460         Pushed[x][y] = TRUE;
8461         Pushed[nextx][y] = TRUE;
8462
8463         GfxAction[x][y] = ACTION_DEFAULT;
8464       }
8465       else
8466       {
8467         MovDir[x][y] = 0;       /* if element was moving, stop it */
8468       }
8469     }
8470   }
8471
8472   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8473 #if 0
8474   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8475 #else
8476   if (CAN_MOVE(element) && !started_moving)
8477 #endif
8478   {
8479     int move_pattern = element_info[element].move_pattern;
8480     int newx, newy;
8481
8482 #if 0
8483 #if DEBUG
8484     if (MovDir[x][y] == MV_NONE)
8485     {
8486       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8487              x, y, element, element_info[element].token_name);
8488       printf("StartMoving(): This should never happen!\n");
8489     }
8490 #endif
8491 #endif
8492
8493     Moving2Blocked(x, y, &newx, &newy);
8494
8495     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8496       return;
8497
8498     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8499         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8500     {
8501       WasJustMoving[x][y] = 0;
8502       CheckCollision[x][y] = 0;
8503
8504       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8505
8506       if (Feld[x][y] != element)        /* element has changed */
8507         return;
8508     }
8509
8510     if (!MovDelay[x][y])        /* start new movement phase */
8511     {
8512       /* all objects that can change their move direction after each step
8513          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8514
8515       if (element != EL_YAMYAM &&
8516           element != EL_DARK_YAMYAM &&
8517           element != EL_PACMAN &&
8518           !(move_pattern & MV_ANY_DIRECTION) &&
8519           move_pattern != MV_TURNING_LEFT &&
8520           move_pattern != MV_TURNING_RIGHT &&
8521           move_pattern != MV_TURNING_LEFT_RIGHT &&
8522           move_pattern != MV_TURNING_RIGHT_LEFT &&
8523           move_pattern != MV_TURNING_RANDOM)
8524       {
8525         TurnRound(x, y);
8526
8527         if (MovDelay[x][y] && (element == EL_BUG ||
8528                                element == EL_SPACESHIP ||
8529                                element == EL_SP_SNIKSNAK ||
8530                                element == EL_SP_ELECTRON ||
8531                                element == EL_MOLE))
8532           TEST_DrawLevelField(x, y);
8533       }
8534     }
8535
8536     if (MovDelay[x][y])         /* wait some time before next movement */
8537     {
8538       MovDelay[x][y]--;
8539
8540       if (element == EL_ROBOT ||
8541           element == EL_YAMYAM ||
8542           element == EL_DARK_YAMYAM)
8543       {
8544         DrawLevelElementAnimationIfNeeded(x, y, element);
8545         PlayLevelSoundAction(x, y, ACTION_WAITING);
8546       }
8547       else if (element == EL_SP_ELECTRON)
8548         DrawLevelElementAnimationIfNeeded(x, y, element);
8549       else if (element == EL_DRAGON)
8550       {
8551         int i;
8552         int dir = MovDir[x][y];
8553         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8554         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8555         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8556                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8557                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8558                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8559         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8560
8561         GfxAction[x][y] = ACTION_ATTACKING;
8562
8563         if (IS_PLAYER(x, y))
8564           DrawPlayerField(x, y);
8565         else
8566           TEST_DrawLevelField(x, y);
8567
8568         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8569
8570         for (i = 1; i <= 3; i++)
8571         {
8572           int xx = x + i * dx;
8573           int yy = y + i * dy;
8574           int sx = SCREENX(xx);
8575           int sy = SCREENY(yy);
8576           int flame_graphic = graphic + (i - 1);
8577
8578           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8579             break;
8580
8581           if (MovDelay[x][y])
8582           {
8583             int flamed = MovingOrBlocked2Element(xx, yy);
8584
8585             /* !!! */
8586 #if 0
8587             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8588               Bang(xx, yy);
8589             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8590               RemoveMovingField(xx, yy);
8591             else
8592               RemoveField(xx, yy);
8593 #else
8594             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8595               Bang(xx, yy);
8596             else
8597               RemoveMovingField(xx, yy);
8598 #endif
8599
8600             ChangeDelay[xx][yy] = 0;
8601
8602             Feld[xx][yy] = EL_FLAMES;
8603
8604             if (IN_SCR_FIELD(sx, sy))
8605             {
8606               TEST_DrawLevelFieldCrumbled(xx, yy);
8607               DrawGraphic(sx, sy, flame_graphic, frame);
8608             }
8609           }
8610           else
8611           {
8612             if (Feld[xx][yy] == EL_FLAMES)
8613               Feld[xx][yy] = EL_EMPTY;
8614             TEST_DrawLevelField(xx, yy);
8615           }
8616         }
8617       }
8618
8619       if (MovDelay[x][y])       /* element still has to wait some time */
8620       {
8621         PlayLevelSoundAction(x, y, ACTION_WAITING);
8622
8623         return;
8624       }
8625     }
8626
8627     /* now make next step */
8628
8629     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8630
8631     if (DONT_COLLIDE_WITH(element) &&
8632         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8633         !PLAYER_ENEMY_PROTECTED(newx, newy))
8634     {
8635       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8636
8637       return;
8638     }
8639
8640     else if (CAN_MOVE_INTO_ACID(element) &&
8641              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8642              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8643              (MovDir[x][y] == MV_DOWN ||
8644               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8645     {
8646       SplashAcid(newx, newy);
8647       Store[x][y] = EL_ACID;
8648     }
8649     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8650     {
8651       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8652           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8653           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8654           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8655       {
8656         RemoveField(x, y);
8657         TEST_DrawLevelField(x, y);
8658
8659         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8660         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8661           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8662
8663         local_player->friends_still_needed--;
8664         if (!local_player->friends_still_needed &&
8665             !local_player->GameOver && AllPlayersGone)
8666           PlayerWins(local_player);
8667
8668         return;
8669       }
8670       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8671       {
8672         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8673           TEST_DrawLevelField(newx, newy);
8674         else
8675           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8676       }
8677       else if (!IS_FREE(newx, newy))
8678       {
8679         GfxAction[x][y] = ACTION_WAITING;
8680
8681         if (IS_PLAYER(x, y))
8682           DrawPlayerField(x, y);
8683         else
8684           TEST_DrawLevelField(x, y);
8685
8686         return;
8687       }
8688     }
8689     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8690     {
8691       if (IS_FOOD_PIG(Feld[newx][newy]))
8692       {
8693         if (IS_MOVING(newx, newy))
8694           RemoveMovingField(newx, newy);
8695         else
8696         {
8697           Feld[newx][newy] = EL_EMPTY;
8698           TEST_DrawLevelField(newx, newy);
8699         }
8700
8701         PlayLevelSound(x, y, SND_PIG_DIGGING);
8702       }
8703       else if (!IS_FREE(newx, newy))
8704       {
8705         if (IS_PLAYER(x, y))
8706           DrawPlayerField(x, y);
8707         else
8708           TEST_DrawLevelField(x, y);
8709
8710         return;
8711       }
8712     }
8713     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8714     {
8715       if (Store[x][y] != EL_EMPTY)
8716       {
8717         boolean can_clone = FALSE;
8718         int xx, yy;
8719
8720         /* check if element to clone is still there */
8721         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8722         {
8723           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8724           {
8725             can_clone = TRUE;
8726
8727             break;
8728           }
8729         }
8730
8731         /* cannot clone or target field not free anymore -- do not clone */
8732         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8733           Store[x][y] = EL_EMPTY;
8734       }
8735
8736       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8737       {
8738         if (IS_MV_DIAGONAL(MovDir[x][y]))
8739         {
8740           int diagonal_move_dir = MovDir[x][y];
8741           int stored = Store[x][y];
8742           int change_delay = 8;
8743           int graphic;
8744
8745           /* android is moving diagonally */
8746
8747           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8748
8749           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8750           GfxElement[x][y] = EL_EMC_ANDROID;
8751           GfxAction[x][y] = ACTION_SHRINKING;
8752           GfxDir[x][y] = diagonal_move_dir;
8753           ChangeDelay[x][y] = change_delay;
8754
8755           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8756                                    GfxDir[x][y]);
8757
8758           DrawLevelGraphicAnimation(x, y, graphic);
8759           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8760
8761           if (Feld[newx][newy] == EL_ACID)
8762           {
8763             SplashAcid(newx, newy);
8764
8765             return;
8766           }
8767
8768           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8769
8770           Store[newx][newy] = EL_EMC_ANDROID;
8771           GfxElement[newx][newy] = EL_EMC_ANDROID;
8772           GfxAction[newx][newy] = ACTION_GROWING;
8773           GfxDir[newx][newy] = diagonal_move_dir;
8774           ChangeDelay[newx][newy] = change_delay;
8775
8776           graphic = el_act_dir2img(GfxElement[newx][newy],
8777                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8778
8779           DrawLevelGraphicAnimation(newx, newy, graphic);
8780           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8781
8782           return;
8783         }
8784         else
8785         {
8786           Feld[newx][newy] = EL_EMPTY;
8787           TEST_DrawLevelField(newx, newy);
8788
8789           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8790         }
8791       }
8792       else if (!IS_FREE(newx, newy))
8793       {
8794 #if 0
8795         if (IS_PLAYER(x, y))
8796           DrawPlayerField(x, y);
8797         else
8798           TEST_DrawLevelField(x, y);
8799 #endif
8800
8801         return;
8802       }
8803     }
8804     else if (IS_CUSTOM_ELEMENT(element) &&
8805              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8806     {
8807 #if 1
8808       if (!DigFieldByCE(newx, newy, element))
8809         return;
8810 #else
8811       int new_element = Feld[newx][newy];
8812
8813       if (!IS_FREE(newx, newy))
8814       {
8815         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8816                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8817                       ACTION_BREAKING);
8818
8819         /* no element can dig solid indestructible elements */
8820         if (IS_INDESTRUCTIBLE(new_element) &&
8821             !IS_DIGGABLE(new_element) &&
8822             !IS_COLLECTIBLE(new_element))
8823           return;
8824
8825         if (AmoebaNr[newx][newy] &&
8826             (new_element == EL_AMOEBA_FULL ||
8827              new_element == EL_BD_AMOEBA ||
8828              new_element == EL_AMOEBA_GROWING))
8829         {
8830           AmoebaCnt[AmoebaNr[newx][newy]]--;
8831           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8832         }
8833
8834         if (IS_MOVING(newx, newy))
8835           RemoveMovingField(newx, newy);
8836         else
8837         {
8838           RemoveField(newx, newy);
8839           TEST_DrawLevelField(newx, newy);
8840         }
8841
8842         /* if digged element was about to explode, prevent the explosion */
8843         ExplodeField[newx][newy] = EX_TYPE_NONE;
8844
8845         PlayLevelSoundAction(x, y, action);
8846       }
8847
8848       Store[newx][newy] = EL_EMPTY;
8849
8850 #if 1
8851       /* this makes it possible to leave the removed element again */
8852       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8853         Store[newx][newy] = new_element;
8854 #else
8855       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8856       {
8857         int move_leave_element = element_info[element].move_leave_element;
8858
8859         /* this makes it possible to leave the removed element again */
8860         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8861                              new_element : move_leave_element);
8862       }
8863 #endif
8864
8865 #endif
8866
8867       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8868       {
8869         RunnerVisit[x][y] = FrameCounter;
8870         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8871       }
8872     }
8873     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8874     {
8875       if (!IS_FREE(newx, newy))
8876       {
8877         if (IS_PLAYER(x, y))
8878           DrawPlayerField(x, y);
8879         else
8880           TEST_DrawLevelField(x, y);
8881
8882         return;
8883       }
8884       else
8885       {
8886         boolean wanna_flame = !RND(10);
8887         int dx = newx - x, dy = newy - y;
8888         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8889         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8890         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8891                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8892         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8893                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8894
8895         if ((wanna_flame ||
8896              IS_CLASSIC_ENEMY(element1) ||
8897              IS_CLASSIC_ENEMY(element2)) &&
8898             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8899             element1 != EL_FLAMES && element2 != EL_FLAMES)
8900         {
8901           ResetGfxAnimation(x, y);
8902           GfxAction[x][y] = ACTION_ATTACKING;
8903
8904           if (IS_PLAYER(x, y))
8905             DrawPlayerField(x, y);
8906           else
8907             TEST_DrawLevelField(x, y);
8908
8909           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8910
8911           MovDelay[x][y] = 50;
8912
8913           /* !!! */
8914 #if 0
8915           RemoveField(newx, newy);
8916 #endif
8917           Feld[newx][newy] = EL_FLAMES;
8918           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8919           {
8920 #if 0
8921             RemoveField(newx1, newy1);
8922 #endif
8923             Feld[newx1][newy1] = EL_FLAMES;
8924           }
8925           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8926           {
8927 #if 0
8928             RemoveField(newx2, newy2);
8929 #endif
8930             Feld[newx2][newy2] = EL_FLAMES;
8931           }
8932
8933           return;
8934         }
8935       }
8936     }
8937     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8938              Feld[newx][newy] == EL_DIAMOND)
8939     {
8940       if (IS_MOVING(newx, newy))
8941         RemoveMovingField(newx, newy);
8942       else
8943       {
8944         Feld[newx][newy] = EL_EMPTY;
8945         TEST_DrawLevelField(newx, newy);
8946       }
8947
8948       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8949     }
8950     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8951              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8952     {
8953       if (AmoebaNr[newx][newy])
8954       {
8955         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8956         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8957             Feld[newx][newy] == EL_BD_AMOEBA)
8958           AmoebaCnt[AmoebaNr[newx][newy]]--;
8959       }
8960
8961 #if 0
8962       /* !!! test !!! */
8963       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8964       {
8965         RemoveMovingField(newx, newy);
8966       }
8967 #else
8968       if (IS_MOVING(newx, newy))
8969       {
8970         RemoveMovingField(newx, newy);
8971       }
8972 #endif
8973       else
8974       {
8975         Feld[newx][newy] = EL_EMPTY;
8976         TEST_DrawLevelField(newx, newy);
8977       }
8978
8979       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8980     }
8981     else if ((element == EL_PACMAN || element == EL_MOLE)
8982              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8983     {
8984       if (AmoebaNr[newx][newy])
8985       {
8986         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8987         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8988             Feld[newx][newy] == EL_BD_AMOEBA)
8989           AmoebaCnt[AmoebaNr[newx][newy]]--;
8990       }
8991
8992       if (element == EL_MOLE)
8993       {
8994         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8995         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8996
8997         ResetGfxAnimation(x, y);
8998         GfxAction[x][y] = ACTION_DIGGING;
8999         TEST_DrawLevelField(x, y);
9000
9001         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
9002
9003         return;                         /* wait for shrinking amoeba */
9004       }
9005       else      /* element == EL_PACMAN */
9006       {
9007         Feld[newx][newy] = EL_EMPTY;
9008         TEST_DrawLevelField(newx, newy);
9009         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9010       }
9011     }
9012     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9013              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9014               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9015     {
9016       /* wait for shrinking amoeba to completely disappear */
9017       return;
9018     }
9019     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9020     {
9021       /* object was running against a wall */
9022
9023       TurnRound(x, y);
9024
9025 #if 0
9026       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9027       if (move_pattern & MV_ANY_DIRECTION &&
9028           move_pattern == MovDir[x][y])
9029       {
9030         int blocking_element =
9031           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9032
9033         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9034                                  MovDir[x][y]);
9035
9036         element = Feld[x][y];   /* element might have changed */
9037       }
9038 #endif
9039
9040       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9041         DrawLevelElementAnimation(x, y, element);
9042
9043       if (DONT_TOUCH(element))
9044         TestIfBadThingTouchesPlayer(x, y);
9045
9046       return;
9047     }
9048
9049     InitMovingField(x, y, MovDir[x][y]);
9050
9051     PlayLevelSoundAction(x, y, ACTION_MOVING);
9052   }
9053
9054   if (MovDir[x][y])
9055     ContinueMoving(x, y);
9056 }
9057
9058 void ContinueMoving(int x, int y)
9059 {
9060   int element = Feld[x][y];
9061   struct ElementInfo *ei = &element_info[element];
9062   int direction = MovDir[x][y];
9063   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9064   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9065   int newx = x + dx, newy = y + dy;
9066   int stored = Store[x][y];
9067   int stored_new = Store[newx][newy];
9068   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9069   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9070   boolean last_line = (newy == lev_fieldy - 1);
9071
9072   MovPos[x][y] += getElementMoveStepsize(x, y);
9073
9074   if (pushed_by_player) /* special case: moving object pushed by player */
9075     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9076
9077   if (ABS(MovPos[x][y]) < TILEX)
9078   {
9079 #if 0
9080     int ee = Feld[x][y];
9081     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9082     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9083
9084     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9085            x, y, ABS(MovPos[x][y]),
9086            ee, gg, ff,
9087            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9088 #endif
9089
9090     TEST_DrawLevelField(x, y);
9091
9092     return;     /* element is still moving */
9093   }
9094
9095   /* element reached destination field */
9096
9097   Feld[x][y] = EL_EMPTY;
9098   Feld[newx][newy] = element;
9099   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9100
9101   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9102   {
9103     element = Feld[newx][newy] = EL_ACID;
9104   }
9105   else if (element == EL_MOLE)
9106   {
9107     Feld[x][y] = EL_SAND;
9108
9109     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9110   }
9111   else if (element == EL_QUICKSAND_FILLING)
9112   {
9113     element = Feld[newx][newy] = get_next_element(element);
9114     Store[newx][newy] = Store[x][y];
9115   }
9116   else if (element == EL_QUICKSAND_EMPTYING)
9117   {
9118     Feld[x][y] = get_next_element(element);
9119     element = Feld[newx][newy] = Store[x][y];
9120   }
9121   else if (element == EL_QUICKSAND_FAST_FILLING)
9122   {
9123     element = Feld[newx][newy] = get_next_element(element);
9124     Store[newx][newy] = Store[x][y];
9125   }
9126   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9127   {
9128     Feld[x][y] = get_next_element(element);
9129     element = Feld[newx][newy] = Store[x][y];
9130   }
9131   else if (element == EL_MAGIC_WALL_FILLING)
9132   {
9133     element = Feld[newx][newy] = get_next_element(element);
9134     if (!game.magic_wall_active)
9135       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9136     Store[newx][newy] = Store[x][y];
9137   }
9138   else if (element == EL_MAGIC_WALL_EMPTYING)
9139   {
9140     Feld[x][y] = get_next_element(element);
9141     if (!game.magic_wall_active)
9142       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9143     element = Feld[newx][newy] = Store[x][y];
9144
9145 #if USE_NEW_CUSTOM_VALUE
9146     InitField(newx, newy, FALSE);
9147 #endif
9148   }
9149   else if (element == EL_BD_MAGIC_WALL_FILLING)
9150   {
9151     element = Feld[newx][newy] = get_next_element(element);
9152     if (!game.magic_wall_active)
9153       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9154     Store[newx][newy] = Store[x][y];
9155   }
9156   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9157   {
9158     Feld[x][y] = get_next_element(element);
9159     if (!game.magic_wall_active)
9160       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9161     element = Feld[newx][newy] = Store[x][y];
9162
9163 #if USE_NEW_CUSTOM_VALUE
9164     InitField(newx, newy, FALSE);
9165 #endif
9166   }
9167   else if (element == EL_DC_MAGIC_WALL_FILLING)
9168   {
9169     element = Feld[newx][newy] = get_next_element(element);
9170     if (!game.magic_wall_active)
9171       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9172     Store[newx][newy] = Store[x][y];
9173   }
9174   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9175   {
9176     Feld[x][y] = get_next_element(element);
9177     if (!game.magic_wall_active)
9178       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9179     element = Feld[newx][newy] = Store[x][y];
9180
9181 #if USE_NEW_CUSTOM_VALUE
9182     InitField(newx, newy, FALSE);
9183 #endif
9184   }
9185   else if (element == EL_AMOEBA_DROPPING)
9186   {
9187     Feld[x][y] = get_next_element(element);
9188     element = Feld[newx][newy] = Store[x][y];
9189   }
9190   else if (element == EL_SOKOBAN_OBJECT)
9191   {
9192     if (Back[x][y])
9193       Feld[x][y] = Back[x][y];
9194
9195     if (Back[newx][newy])
9196       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9197
9198     Back[x][y] = Back[newx][newy] = 0;
9199   }
9200
9201   Store[x][y] = EL_EMPTY;
9202   MovPos[x][y] = 0;
9203   MovDir[x][y] = 0;
9204   MovDelay[x][y] = 0;
9205
9206   MovDelay[newx][newy] = 0;
9207
9208   if (CAN_CHANGE_OR_HAS_ACTION(element))
9209   {
9210     /* copy element change control values to new field */
9211     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9212     ChangePage[newx][newy]  = ChangePage[x][y];
9213     ChangeCount[newx][newy] = ChangeCount[x][y];
9214     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9215   }
9216
9217 #if USE_NEW_CUSTOM_VALUE
9218   CustomValue[newx][newy] = CustomValue[x][y];
9219 #endif
9220
9221   ChangeDelay[x][y] = 0;
9222   ChangePage[x][y] = -1;
9223   ChangeCount[x][y] = 0;
9224   ChangeEvent[x][y] = -1;
9225
9226 #if USE_NEW_CUSTOM_VALUE
9227   CustomValue[x][y] = 0;
9228 #endif
9229
9230   /* copy animation control values to new field */
9231   GfxFrame[newx][newy]  = GfxFrame[x][y];
9232   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9233   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9234   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9235
9236   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9237
9238   /* some elements can leave other elements behind after moving */
9239 #if 1
9240   if (ei->move_leave_element != EL_EMPTY &&
9241       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9242       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9243 #else
9244   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9245       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9246       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9247 #endif
9248   {
9249     int move_leave_element = ei->move_leave_element;
9250
9251 #if 1
9252 #if 1
9253     /* this makes it possible to leave the removed element again */
9254     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9255       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9256 #else
9257     /* this makes it possible to leave the removed element again */
9258     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9259       move_leave_element = stored;
9260 #endif
9261 #else
9262     /* this makes it possible to leave the removed element again */
9263     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9264         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9265       move_leave_element = stored;
9266 #endif
9267
9268     Feld[x][y] = move_leave_element;
9269
9270     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9271       MovDir[x][y] = direction;
9272
9273     InitField(x, y, FALSE);
9274
9275     if (GFX_CRUMBLED(Feld[x][y]))
9276       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9277
9278     if (ELEM_IS_PLAYER(move_leave_element))
9279       RelocatePlayer(x, y, move_leave_element);
9280   }
9281
9282   /* do this after checking for left-behind element */
9283   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9284
9285   if (!CAN_MOVE(element) ||
9286       (CAN_FALL(element) && direction == MV_DOWN &&
9287        (element == EL_SPRING ||
9288         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9289         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9290     GfxDir[x][y] = MovDir[newx][newy] = 0;
9291
9292   TEST_DrawLevelField(x, y);
9293   TEST_DrawLevelField(newx, newy);
9294
9295   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9296
9297   /* prevent pushed element from moving on in pushed direction */
9298   if (pushed_by_player && CAN_MOVE(element) &&
9299       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9300       !(element_info[element].move_pattern & direction))
9301     TurnRound(newx, newy);
9302
9303   /* prevent elements on conveyor belt from moving on in last direction */
9304   if (pushed_by_conveyor && CAN_FALL(element) &&
9305       direction & MV_HORIZONTAL)
9306     MovDir[newx][newy] = 0;
9307
9308   if (!pushed_by_player)
9309   {
9310     int nextx = newx + dx, nexty = newy + dy;
9311     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9312
9313     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9314
9315     if (CAN_FALL(element) && direction == MV_DOWN)
9316       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9317
9318     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9319       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9320
9321 #if USE_FIX_IMPACT_COLLISION
9322     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9323       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9324 #endif
9325   }
9326
9327   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9328   {
9329     TestIfBadThingTouchesPlayer(newx, newy);
9330     TestIfBadThingTouchesFriend(newx, newy);
9331
9332     if (!IS_CUSTOM_ELEMENT(element))
9333       TestIfBadThingTouchesOtherBadThing(newx, newy);
9334   }
9335   else if (element == EL_PENGUIN)
9336     TestIfFriendTouchesBadThing(newx, newy);
9337
9338   if (DONT_GET_HIT_BY(element))
9339   {
9340     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9341   }
9342
9343   /* give the player one last chance (one more frame) to move away */
9344   if (CAN_FALL(element) && direction == MV_DOWN &&
9345       (last_line || (!IS_FREE(x, newy + 1) &&
9346                      (!IS_PLAYER(x, newy + 1) ||
9347                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9348     Impact(x, newy);
9349
9350   if (pushed_by_player && !game.use_change_when_pushing_bug)
9351   {
9352     int push_side = MV_DIR_OPPOSITE(direction);
9353     struct PlayerInfo *player = PLAYERINFO(x, y);
9354
9355     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9356                                player->index_bit, push_side);
9357     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9358                                         player->index_bit, push_side);
9359   }
9360
9361   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9362     MovDelay[newx][newy] = 1;
9363
9364   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9365
9366   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9367
9368 #if 0
9369   if (ChangePage[newx][newy] != -1)             /* delayed change */
9370   {
9371     int page = ChangePage[newx][newy];
9372     struct ElementChangeInfo *change = &ei->change_page[page];
9373
9374     ChangePage[newx][newy] = -1;
9375
9376     if (change->can_change)
9377     {
9378       if (ChangeElement(newx, newy, element, page))
9379       {
9380         if (change->post_change_function)
9381           change->post_change_function(newx, newy);
9382       }
9383     }
9384
9385     if (change->has_action)
9386       ExecuteCustomElementAction(newx, newy, element, page);
9387   }
9388 #endif
9389
9390   TestIfElementHitsCustomElement(newx, newy, direction);
9391   TestIfPlayerTouchesCustomElement(newx, newy);
9392   TestIfElementTouchesCustomElement(newx, newy);
9393
9394   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9395       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9396     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9397                              MV_DIR_OPPOSITE(direction));
9398 }
9399
9400 int AmoebeNachbarNr(int ax, int ay)
9401 {
9402   int i;
9403   int element = Feld[ax][ay];
9404   int group_nr = 0;
9405   static int xy[4][2] =
9406   {
9407     { 0, -1 },
9408     { -1, 0 },
9409     { +1, 0 },
9410     { 0, +1 }
9411   };
9412
9413   for (i = 0; i < NUM_DIRECTIONS; i++)
9414   {
9415     int x = ax + xy[i][0];
9416     int y = ay + xy[i][1];
9417
9418     if (!IN_LEV_FIELD(x, y))
9419       continue;
9420
9421     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9422       group_nr = AmoebaNr[x][y];
9423   }
9424
9425   return group_nr;
9426 }
9427
9428 void AmoebenVereinigen(int ax, int ay)
9429 {
9430   int i, x, y, xx, yy;
9431   int new_group_nr = AmoebaNr[ax][ay];
9432   static int xy[4][2] =
9433   {
9434     { 0, -1 },
9435     { -1, 0 },
9436     { +1, 0 },
9437     { 0, +1 }
9438   };
9439
9440   if (new_group_nr == 0)
9441     return;
9442
9443   for (i = 0; i < NUM_DIRECTIONS; i++)
9444   {
9445     x = ax + xy[i][0];
9446     y = ay + xy[i][1];
9447
9448     if (!IN_LEV_FIELD(x, y))
9449       continue;
9450
9451     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9452          Feld[x][y] == EL_BD_AMOEBA ||
9453          Feld[x][y] == EL_AMOEBA_DEAD) &&
9454         AmoebaNr[x][y] != new_group_nr)
9455     {
9456       int old_group_nr = AmoebaNr[x][y];
9457
9458       if (old_group_nr == 0)
9459         return;
9460
9461       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9462       AmoebaCnt[old_group_nr] = 0;
9463       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9464       AmoebaCnt2[old_group_nr] = 0;
9465
9466       SCAN_PLAYFIELD(xx, yy)
9467       {
9468         if (AmoebaNr[xx][yy] == old_group_nr)
9469           AmoebaNr[xx][yy] = new_group_nr;
9470       }
9471     }
9472   }
9473 }
9474
9475 void AmoebeUmwandeln(int ax, int ay)
9476 {
9477   int i, x, y;
9478
9479   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9480   {
9481     int group_nr = AmoebaNr[ax][ay];
9482
9483 #ifdef DEBUG
9484     if (group_nr == 0)
9485     {
9486       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9487       printf("AmoebeUmwandeln(): This should never happen!\n");
9488       return;
9489     }
9490 #endif
9491
9492     SCAN_PLAYFIELD(x, y)
9493     {
9494       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9495       {
9496         AmoebaNr[x][y] = 0;
9497         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9498       }
9499     }
9500
9501     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9502                             SND_AMOEBA_TURNING_TO_GEM :
9503                             SND_AMOEBA_TURNING_TO_ROCK));
9504     Bang(ax, ay);
9505   }
9506   else
9507   {
9508     static int xy[4][2] =
9509     {
9510       { 0, -1 },
9511       { -1, 0 },
9512       { +1, 0 },
9513       { 0, +1 }
9514     };
9515
9516     for (i = 0; i < NUM_DIRECTIONS; i++)
9517     {
9518       x = ax + xy[i][0];
9519       y = ay + xy[i][1];
9520
9521       if (!IN_LEV_FIELD(x, y))
9522         continue;
9523
9524       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9525       {
9526         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9527                               SND_AMOEBA_TURNING_TO_GEM :
9528                               SND_AMOEBA_TURNING_TO_ROCK));
9529         Bang(x, y);
9530       }
9531     }
9532   }
9533 }
9534
9535 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9536 {
9537   int x, y;
9538   int group_nr = AmoebaNr[ax][ay];
9539   boolean done = FALSE;
9540
9541 #ifdef DEBUG
9542   if (group_nr == 0)
9543   {
9544     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9545     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9546     return;
9547   }
9548 #endif
9549
9550   SCAN_PLAYFIELD(x, y)
9551   {
9552     if (AmoebaNr[x][y] == group_nr &&
9553         (Feld[x][y] == EL_AMOEBA_DEAD ||
9554          Feld[x][y] == EL_BD_AMOEBA ||
9555          Feld[x][y] == EL_AMOEBA_GROWING))
9556     {
9557       AmoebaNr[x][y] = 0;
9558       Feld[x][y] = new_element;
9559       InitField(x, y, FALSE);
9560       TEST_DrawLevelField(x, y);
9561       done = TRUE;
9562     }
9563   }
9564
9565   if (done)
9566     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9567                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9568                             SND_BD_AMOEBA_TURNING_TO_GEM));
9569 }
9570
9571 void AmoebeWaechst(int x, int y)
9572 {
9573   static unsigned int sound_delay = 0;
9574   static unsigned int sound_delay_value = 0;
9575
9576   if (!MovDelay[x][y])          /* start new growing cycle */
9577   {
9578     MovDelay[x][y] = 7;
9579
9580     if (DelayReached(&sound_delay, sound_delay_value))
9581     {
9582       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9583       sound_delay_value = 30;
9584     }
9585   }
9586
9587   if (MovDelay[x][y])           /* wait some time before growing bigger */
9588   {
9589     MovDelay[x][y]--;
9590     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9591     {
9592       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9593                                            6 - MovDelay[x][y]);
9594
9595       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9596     }
9597
9598     if (!MovDelay[x][y])
9599     {
9600       Feld[x][y] = Store[x][y];
9601       Store[x][y] = 0;
9602       TEST_DrawLevelField(x, y);
9603     }
9604   }
9605 }
9606
9607 void AmoebaDisappearing(int x, int y)
9608 {
9609   static unsigned int sound_delay = 0;
9610   static unsigned int sound_delay_value = 0;
9611
9612   if (!MovDelay[x][y])          /* start new shrinking cycle */
9613   {
9614     MovDelay[x][y] = 7;
9615
9616     if (DelayReached(&sound_delay, sound_delay_value))
9617       sound_delay_value = 30;
9618   }
9619
9620   if (MovDelay[x][y])           /* wait some time before shrinking */
9621   {
9622     MovDelay[x][y]--;
9623     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9624     {
9625       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9626                                            6 - MovDelay[x][y]);
9627
9628       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9629     }
9630
9631     if (!MovDelay[x][y])
9632     {
9633       Feld[x][y] = EL_EMPTY;
9634       TEST_DrawLevelField(x, y);
9635
9636       /* don't let mole enter this field in this cycle;
9637          (give priority to objects falling to this field from above) */
9638       Stop[x][y] = TRUE;
9639     }
9640   }
9641 }
9642
9643 void AmoebeAbleger(int ax, int ay)
9644 {
9645   int i;
9646   int element = Feld[ax][ay];
9647   int graphic = el2img(element);
9648   int newax = ax, neway = ay;
9649   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9650   static int xy[4][2] =
9651   {
9652     { 0, -1 },
9653     { -1, 0 },
9654     { +1, 0 },
9655     { 0, +1 }
9656   };
9657
9658   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9659   {
9660     Feld[ax][ay] = EL_AMOEBA_DEAD;
9661     TEST_DrawLevelField(ax, ay);
9662     return;
9663   }
9664
9665   if (IS_ANIMATED(graphic))
9666     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9667
9668   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9669     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9670
9671   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9672   {
9673     MovDelay[ax][ay]--;
9674     if (MovDelay[ax][ay])
9675       return;
9676   }
9677
9678   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9679   {
9680     int start = RND(4);
9681     int x = ax + xy[start][0];
9682     int y = ay + xy[start][1];
9683
9684     if (!IN_LEV_FIELD(x, y))
9685       return;
9686
9687     if (IS_FREE(x, y) ||
9688         CAN_GROW_INTO(Feld[x][y]) ||
9689         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9690         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9691     {
9692       newax = x;
9693       neway = y;
9694     }
9695
9696     if (newax == ax && neway == ay)
9697       return;
9698   }
9699   else                          /* normal or "filled" (BD style) amoeba */
9700   {
9701     int start = RND(4);
9702     boolean waiting_for_player = FALSE;
9703
9704     for (i = 0; i < NUM_DIRECTIONS; i++)
9705     {
9706       int j = (start + i) % 4;
9707       int x = ax + xy[j][0];
9708       int y = ay + xy[j][1];
9709
9710       if (!IN_LEV_FIELD(x, y))
9711         continue;
9712
9713       if (IS_FREE(x, y) ||
9714           CAN_GROW_INTO(Feld[x][y]) ||
9715           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9716           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9717       {
9718         newax = x;
9719         neway = y;
9720         break;
9721       }
9722       else if (IS_PLAYER(x, y))
9723         waiting_for_player = TRUE;
9724     }
9725
9726     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9727     {
9728       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9729       {
9730         Feld[ax][ay] = EL_AMOEBA_DEAD;
9731         TEST_DrawLevelField(ax, ay);
9732         AmoebaCnt[AmoebaNr[ax][ay]]--;
9733
9734         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9735         {
9736           if (element == EL_AMOEBA_FULL)
9737             AmoebeUmwandeln(ax, ay);
9738           else if (element == EL_BD_AMOEBA)
9739             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9740         }
9741       }
9742       return;
9743     }
9744     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9745     {
9746       /* amoeba gets larger by growing in some direction */
9747
9748       int new_group_nr = AmoebaNr[ax][ay];
9749
9750 #ifdef DEBUG
9751   if (new_group_nr == 0)
9752   {
9753     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9754     printf("AmoebeAbleger(): This should never happen!\n");
9755     return;
9756   }
9757 #endif
9758
9759       AmoebaNr[newax][neway] = new_group_nr;
9760       AmoebaCnt[new_group_nr]++;
9761       AmoebaCnt2[new_group_nr]++;
9762
9763       /* if amoeba touches other amoeba(s) after growing, unify them */
9764       AmoebenVereinigen(newax, neway);
9765
9766       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9767       {
9768         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9769         return;
9770       }
9771     }
9772   }
9773
9774   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9775       (neway == lev_fieldy - 1 && newax != ax))
9776   {
9777     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9778     Store[newax][neway] = element;
9779   }
9780   else if (neway == ay || element == EL_EMC_DRIPPER)
9781   {
9782     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9783
9784     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9785   }
9786   else
9787   {
9788     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9789     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9790     Store[ax][ay] = EL_AMOEBA_DROP;
9791     ContinueMoving(ax, ay);
9792     return;
9793   }
9794
9795   TEST_DrawLevelField(newax, neway);
9796 }
9797
9798 void Life(int ax, int ay)
9799 {
9800   int x1, y1, x2, y2;
9801   int life_time = 40;
9802   int element = Feld[ax][ay];
9803   int graphic = el2img(element);
9804   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9805                          level.biomaze);
9806   boolean changed = FALSE;
9807
9808   if (IS_ANIMATED(graphic))
9809     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9810
9811   if (Stop[ax][ay])
9812     return;
9813
9814   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9815     MovDelay[ax][ay] = life_time;
9816
9817   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9818   {
9819     MovDelay[ax][ay]--;
9820     if (MovDelay[ax][ay])
9821       return;
9822   }
9823
9824   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9825   {
9826     int xx = ax+x1, yy = ay+y1;
9827     int nachbarn = 0;
9828
9829     if (!IN_LEV_FIELD(xx, yy))
9830       continue;
9831
9832     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9833     {
9834       int x = xx+x2, y = yy+y2;
9835
9836       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9837         continue;
9838
9839       if (((Feld[x][y] == element ||
9840             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9841            !Stop[x][y]) ||
9842           (IS_FREE(x, y) && Stop[x][y]))
9843         nachbarn++;
9844     }
9845
9846     if (xx == ax && yy == ay)           /* field in the middle */
9847     {
9848       if (nachbarn < life_parameter[0] ||
9849           nachbarn > life_parameter[1])
9850       {
9851         Feld[xx][yy] = EL_EMPTY;
9852         if (!Stop[xx][yy])
9853           TEST_DrawLevelField(xx, yy);
9854         Stop[xx][yy] = TRUE;
9855         changed = TRUE;
9856       }
9857     }
9858     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9859     {                                   /* free border field */
9860       if (nachbarn >= life_parameter[2] &&
9861           nachbarn <= life_parameter[3])
9862       {
9863         Feld[xx][yy] = element;
9864         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9865         if (!Stop[xx][yy])
9866           TEST_DrawLevelField(xx, yy);
9867         Stop[xx][yy] = TRUE;
9868         changed = TRUE;
9869       }
9870     }
9871   }
9872
9873   if (changed)
9874     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9875                    SND_GAME_OF_LIFE_GROWING);
9876 }
9877
9878 static void InitRobotWheel(int x, int y)
9879 {
9880   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9881 }
9882
9883 static void RunRobotWheel(int x, int y)
9884 {
9885   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9886 }
9887
9888 static void StopRobotWheel(int x, int y)
9889 {
9890   if (ZX == x && ZY == y)
9891   {
9892     ZX = ZY = -1;
9893
9894     game.robot_wheel_active = FALSE;
9895   }
9896 }
9897
9898 static void InitTimegateWheel(int x, int y)
9899 {
9900   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9901 }
9902
9903 static void RunTimegateWheel(int x, int y)
9904 {
9905   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9906 }
9907
9908 static void InitMagicBallDelay(int x, int y)
9909 {
9910 #if 1
9911   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9912 #else
9913   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9914 #endif
9915 }
9916
9917 static void ActivateMagicBall(int bx, int by)
9918 {
9919   int x, y;
9920
9921   if (level.ball_random)
9922   {
9923     int pos_border = RND(8);    /* select one of the eight border elements */
9924     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9925     int xx = pos_content % 3;
9926     int yy = pos_content / 3;
9927
9928     x = bx - 1 + xx;
9929     y = by - 1 + yy;
9930
9931     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9932       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9933   }
9934   else
9935   {
9936     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9937     {
9938       int xx = x - bx + 1;
9939       int yy = y - by + 1;
9940
9941       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9942         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9943     }
9944   }
9945
9946   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9947 }
9948
9949 void CheckExit(int x, int y)
9950 {
9951   if (local_player->gems_still_needed > 0 ||
9952       local_player->sokobanfields_still_needed > 0 ||
9953       local_player->lights_still_needed > 0)
9954   {
9955     int element = Feld[x][y];
9956     int graphic = el2img(element);
9957
9958     if (IS_ANIMATED(graphic))
9959       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9960
9961     return;
9962   }
9963
9964   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9965     return;
9966
9967   Feld[x][y] = EL_EXIT_OPENING;
9968
9969   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9970 }
9971
9972 void CheckExitEM(int x, int y)
9973 {
9974   if (local_player->gems_still_needed > 0 ||
9975       local_player->sokobanfields_still_needed > 0 ||
9976       local_player->lights_still_needed > 0)
9977   {
9978     int element = Feld[x][y];
9979     int graphic = el2img(element);
9980
9981     if (IS_ANIMATED(graphic))
9982       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9983
9984     return;
9985   }
9986
9987   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9988     return;
9989
9990   Feld[x][y] = EL_EM_EXIT_OPENING;
9991
9992   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9993 }
9994
9995 void CheckExitSteel(int x, int y)
9996 {
9997   if (local_player->gems_still_needed > 0 ||
9998       local_player->sokobanfields_still_needed > 0 ||
9999       local_player->lights_still_needed > 0)
10000   {
10001     int element = Feld[x][y];
10002     int graphic = el2img(element);
10003
10004     if (IS_ANIMATED(graphic))
10005       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10006
10007     return;
10008   }
10009
10010   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10011     return;
10012
10013   Feld[x][y] = EL_STEEL_EXIT_OPENING;
10014
10015   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10016 }
10017
10018 void CheckExitSteelEM(int x, int y)
10019 {
10020   if (local_player->gems_still_needed > 0 ||
10021       local_player->sokobanfields_still_needed > 0 ||
10022       local_player->lights_still_needed > 0)
10023   {
10024     int element = Feld[x][y];
10025     int graphic = el2img(element);
10026
10027     if (IS_ANIMATED(graphic))
10028       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10029
10030     return;
10031   }
10032
10033   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10034     return;
10035
10036   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10037
10038   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10039 }
10040
10041 void CheckExitSP(int x, int y)
10042 {
10043   if (local_player->gems_still_needed > 0)
10044   {
10045     int element = Feld[x][y];
10046     int graphic = el2img(element);
10047
10048     if (IS_ANIMATED(graphic))
10049       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10050
10051     return;
10052   }
10053
10054   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10055     return;
10056
10057   Feld[x][y] = EL_SP_EXIT_OPENING;
10058
10059   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10060 }
10061
10062 static void CloseAllOpenTimegates()
10063 {
10064   int x, y;
10065
10066   SCAN_PLAYFIELD(x, y)
10067   {
10068     int element = Feld[x][y];
10069
10070     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10071     {
10072       Feld[x][y] = EL_TIMEGATE_CLOSING;
10073
10074       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10075     }
10076   }
10077 }
10078
10079 void DrawTwinkleOnField(int x, int y)
10080 {
10081   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10082     return;
10083
10084   if (Feld[x][y] == EL_BD_DIAMOND)
10085     return;
10086
10087   if (MovDelay[x][y] == 0)      /* next animation frame */
10088     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10089
10090   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10091   {
10092     MovDelay[x][y]--;
10093
10094     DrawLevelElementAnimation(x, y, Feld[x][y]);
10095
10096     if (MovDelay[x][y] != 0)
10097     {
10098       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10099                                            10 - MovDelay[x][y]);
10100
10101       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10102     }
10103   }
10104 }
10105
10106 void MauerWaechst(int x, int y)
10107 {
10108   int delay = 6;
10109
10110   if (!MovDelay[x][y])          /* next animation frame */
10111     MovDelay[x][y] = 3 * delay;
10112
10113   if (MovDelay[x][y])           /* wait some time before next frame */
10114   {
10115     MovDelay[x][y]--;
10116
10117     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10118     {
10119       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10120       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10121
10122       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10123     }
10124
10125     if (!MovDelay[x][y])
10126     {
10127       if (MovDir[x][y] == MV_LEFT)
10128       {
10129         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10130           TEST_DrawLevelField(x - 1, y);
10131       }
10132       else if (MovDir[x][y] == MV_RIGHT)
10133       {
10134         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10135           TEST_DrawLevelField(x + 1, y);
10136       }
10137       else if (MovDir[x][y] == MV_UP)
10138       {
10139         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10140           TEST_DrawLevelField(x, y - 1);
10141       }
10142       else
10143       {
10144         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10145           TEST_DrawLevelField(x, y + 1);
10146       }
10147
10148       Feld[x][y] = Store[x][y];
10149       Store[x][y] = 0;
10150       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10151       TEST_DrawLevelField(x, y);
10152     }
10153   }
10154 }
10155
10156 void MauerAbleger(int ax, int ay)
10157 {
10158   int element = Feld[ax][ay];
10159   int graphic = el2img(element);
10160   boolean oben_frei = FALSE, unten_frei = FALSE;
10161   boolean links_frei = FALSE, rechts_frei = FALSE;
10162   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10163   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10164   boolean new_wall = FALSE;
10165
10166   if (IS_ANIMATED(graphic))
10167     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10168
10169   if (!MovDelay[ax][ay])        /* start building new wall */
10170     MovDelay[ax][ay] = 6;
10171
10172   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10173   {
10174     MovDelay[ax][ay]--;
10175     if (MovDelay[ax][ay])
10176       return;
10177   }
10178
10179   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10180     oben_frei = TRUE;
10181   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10182     unten_frei = TRUE;
10183   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10184     links_frei = TRUE;
10185   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10186     rechts_frei = TRUE;
10187
10188   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10189       element == EL_EXPANDABLE_WALL_ANY)
10190   {
10191     if (oben_frei)
10192     {
10193       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10194       Store[ax][ay-1] = element;
10195       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10196       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10197         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10198                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10199       new_wall = TRUE;
10200     }
10201     if (unten_frei)
10202     {
10203       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10204       Store[ax][ay+1] = element;
10205       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10206       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10207         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10208                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10209       new_wall = TRUE;
10210     }
10211   }
10212
10213   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10214       element == EL_EXPANDABLE_WALL_ANY ||
10215       element == EL_EXPANDABLE_WALL ||
10216       element == EL_BD_EXPANDABLE_WALL)
10217   {
10218     if (links_frei)
10219     {
10220       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10221       Store[ax-1][ay] = element;
10222       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10223       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10224         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10225                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10226       new_wall = TRUE;
10227     }
10228
10229     if (rechts_frei)
10230     {
10231       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10232       Store[ax+1][ay] = element;
10233       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10234       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10235         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10236                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10237       new_wall = TRUE;
10238     }
10239   }
10240
10241   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10242     TEST_DrawLevelField(ax, ay);
10243
10244   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10245     oben_massiv = TRUE;
10246   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10247     unten_massiv = TRUE;
10248   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10249     links_massiv = TRUE;
10250   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10251     rechts_massiv = TRUE;
10252
10253   if (((oben_massiv && unten_massiv) ||
10254        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10255        element == EL_EXPANDABLE_WALL) &&
10256       ((links_massiv && rechts_massiv) ||
10257        element == EL_EXPANDABLE_WALL_VERTICAL))
10258     Feld[ax][ay] = EL_WALL;
10259
10260   if (new_wall)
10261     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10262 }
10263
10264 void MauerAblegerStahl(int ax, int ay)
10265 {
10266   int element = Feld[ax][ay];
10267   int graphic = el2img(element);
10268   boolean oben_frei = FALSE, unten_frei = FALSE;
10269   boolean links_frei = FALSE, rechts_frei = FALSE;
10270   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10271   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10272   boolean new_wall = FALSE;
10273
10274   if (IS_ANIMATED(graphic))
10275     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10276
10277   if (!MovDelay[ax][ay])        /* start building new wall */
10278     MovDelay[ax][ay] = 6;
10279
10280   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10281   {
10282     MovDelay[ax][ay]--;
10283     if (MovDelay[ax][ay])
10284       return;
10285   }
10286
10287   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10288     oben_frei = TRUE;
10289   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10290     unten_frei = TRUE;
10291   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10292     links_frei = TRUE;
10293   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10294     rechts_frei = TRUE;
10295
10296   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10297       element == EL_EXPANDABLE_STEELWALL_ANY)
10298   {
10299     if (oben_frei)
10300     {
10301       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10302       Store[ax][ay-1] = element;
10303       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10304       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10305         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10306                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10307       new_wall = TRUE;
10308     }
10309     if (unten_frei)
10310     {
10311       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10312       Store[ax][ay+1] = element;
10313       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10314       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10315         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10316                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10317       new_wall = TRUE;
10318     }
10319   }
10320
10321   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10322       element == EL_EXPANDABLE_STEELWALL_ANY)
10323   {
10324     if (links_frei)
10325     {
10326       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10327       Store[ax-1][ay] = element;
10328       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10329       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10330         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10331                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10332       new_wall = TRUE;
10333     }
10334
10335     if (rechts_frei)
10336     {
10337       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10338       Store[ax+1][ay] = element;
10339       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10340       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10341         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10342                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10343       new_wall = TRUE;
10344     }
10345   }
10346
10347   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10348     oben_massiv = TRUE;
10349   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10350     unten_massiv = TRUE;
10351   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10352     links_massiv = TRUE;
10353   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10354     rechts_massiv = TRUE;
10355
10356   if (((oben_massiv && unten_massiv) ||
10357        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10358       ((links_massiv && rechts_massiv) ||
10359        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10360     Feld[ax][ay] = EL_STEELWALL;
10361
10362   if (new_wall)
10363     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10364 }
10365
10366 void CheckForDragon(int x, int y)
10367 {
10368   int i, j;
10369   boolean dragon_found = FALSE;
10370   static int xy[4][2] =
10371   {
10372     { 0, -1 },
10373     { -1, 0 },
10374     { +1, 0 },
10375     { 0, +1 }
10376   };
10377
10378   for (i = 0; i < NUM_DIRECTIONS; i++)
10379   {
10380     for (j = 0; j < 4; j++)
10381     {
10382       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10383
10384       if (IN_LEV_FIELD(xx, yy) &&
10385           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10386       {
10387         if (Feld[xx][yy] == EL_DRAGON)
10388           dragon_found = TRUE;
10389       }
10390       else
10391         break;
10392     }
10393   }
10394
10395   if (!dragon_found)
10396   {
10397     for (i = 0; i < NUM_DIRECTIONS; i++)
10398     {
10399       for (j = 0; j < 3; j++)
10400       {
10401         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10402   
10403         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10404         {
10405           Feld[xx][yy] = EL_EMPTY;
10406           TEST_DrawLevelField(xx, yy);
10407         }
10408         else
10409           break;
10410       }
10411     }
10412   }
10413 }
10414
10415 static void InitBuggyBase(int x, int y)
10416 {
10417   int element = Feld[x][y];
10418   int activating_delay = FRAMES_PER_SECOND / 4;
10419
10420   ChangeDelay[x][y] =
10421     (element == EL_SP_BUGGY_BASE ?
10422      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10423      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10424      activating_delay :
10425      element == EL_SP_BUGGY_BASE_ACTIVE ?
10426      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10427 }
10428
10429 static void WarnBuggyBase(int x, int y)
10430 {
10431   int i;
10432   static int xy[4][2] =
10433   {
10434     { 0, -1 },
10435     { -1, 0 },
10436     { +1, 0 },
10437     { 0, +1 }
10438   };
10439
10440   for (i = 0; i < NUM_DIRECTIONS; i++)
10441   {
10442     int xx = x + xy[i][0];
10443     int yy = y + xy[i][1];
10444
10445     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10446     {
10447       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10448
10449       break;
10450     }
10451   }
10452 }
10453
10454 static void InitTrap(int x, int y)
10455 {
10456   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10457 }
10458
10459 static void ActivateTrap(int x, int y)
10460 {
10461   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10462 }
10463
10464 static void ChangeActiveTrap(int x, int y)
10465 {
10466   int graphic = IMG_TRAP_ACTIVE;
10467
10468   /* if new animation frame was drawn, correct crumbled sand border */
10469   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10470     TEST_DrawLevelFieldCrumbled(x, y);
10471 }
10472
10473 static int getSpecialActionElement(int element, int number, int base_element)
10474 {
10475   return (element != EL_EMPTY ? element :
10476           number != -1 ? base_element + number - 1 :
10477           EL_EMPTY);
10478 }
10479
10480 static int getModifiedActionNumber(int value_old, int operator, int operand,
10481                                    int value_min, int value_max)
10482 {
10483   int value_new = (operator == CA_MODE_SET      ? operand :
10484                    operator == CA_MODE_ADD      ? value_old + operand :
10485                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10486                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10487                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10488                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10489                    value_old);
10490
10491   return (value_new < value_min ? value_min :
10492           value_new > value_max ? value_max :
10493           value_new);
10494 }
10495
10496 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10497 {
10498   struct ElementInfo *ei = &element_info[element];
10499   struct ElementChangeInfo *change = &ei->change_page[page];
10500   int target_element = change->target_element;
10501   int action_type = change->action_type;
10502   int action_mode = change->action_mode;
10503   int action_arg = change->action_arg;
10504   int action_element = change->action_element;
10505   int i;
10506
10507   if (!change->has_action)
10508     return;
10509
10510   /* ---------- determine action paramater values -------------------------- */
10511
10512   int level_time_value =
10513     (level.time > 0 ? TimeLeft :
10514      TimePlayed);
10515
10516   int action_arg_element_raw =
10517     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10518      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10519      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10520      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10521      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10522      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10523      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10524      EL_EMPTY);
10525   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10526
10527 #if 0
10528   if (action_arg_element_raw == EL_GROUP_START)
10529     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10530 #endif
10531
10532   int action_arg_direction =
10533     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10534      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10535      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10536      change->actual_trigger_side :
10537      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10538      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10539      MV_NONE);
10540
10541   int action_arg_number_min =
10542     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10543      CA_ARG_MIN);
10544
10545   int action_arg_number_max =
10546     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10547      action_type == CA_SET_LEVEL_GEMS ? 999 :
10548      action_type == CA_SET_LEVEL_TIME ? 9999 :
10549      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10550      action_type == CA_SET_CE_VALUE ? 9999 :
10551      action_type == CA_SET_CE_SCORE ? 9999 :
10552      CA_ARG_MAX);
10553
10554   int action_arg_number_reset =
10555     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10556      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10557      action_type == CA_SET_LEVEL_TIME ? level.time :
10558      action_type == CA_SET_LEVEL_SCORE ? 0 :
10559 #if USE_NEW_CUSTOM_VALUE
10560      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10561 #else
10562      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10563 #endif
10564      action_type == CA_SET_CE_SCORE ? 0 :
10565      0);
10566
10567   int action_arg_number =
10568     (action_arg <= CA_ARG_MAX ? action_arg :
10569      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10570      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10571      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10572      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10573      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10574      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10575 #if USE_NEW_CUSTOM_VALUE
10576      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10577 #else
10578      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10579 #endif
10580      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10581      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10582      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10583      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10584      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10585      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10586      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10587      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10588      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10589      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10590      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10591      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10592      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10593      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10594      -1);
10595
10596   int action_arg_number_old =
10597     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10598      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10599      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10600      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10601      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10602      0);
10603
10604   int action_arg_number_new =
10605     getModifiedActionNumber(action_arg_number_old,
10606                             action_mode, action_arg_number,
10607                             action_arg_number_min, action_arg_number_max);
10608
10609 #if 1
10610   int trigger_player_bits =
10611     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10612      change->actual_trigger_player_bits : change->trigger_player);
10613 #else
10614   int trigger_player_bits =
10615     (change->actual_trigger_player >= EL_PLAYER_1 &&
10616      change->actual_trigger_player <= EL_PLAYER_4 ?
10617      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10618      PLAYER_BITS_ANY);
10619 #endif
10620
10621   int action_arg_player_bits =
10622     (action_arg >= CA_ARG_PLAYER_1 &&
10623      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10624      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10625      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10626      PLAYER_BITS_ANY);
10627
10628   /* ---------- execute action  -------------------------------------------- */
10629
10630   switch (action_type)
10631   {
10632     case CA_NO_ACTION:
10633     {
10634       return;
10635     }
10636
10637     /* ---------- level actions  ------------------------------------------- */
10638
10639     case CA_RESTART_LEVEL:
10640     {
10641       game.restart_level = TRUE;
10642
10643       break;
10644     }
10645
10646     case CA_SHOW_ENVELOPE:
10647     {
10648       int element = getSpecialActionElement(action_arg_element,
10649                                             action_arg_number, EL_ENVELOPE_1);
10650
10651       if (IS_ENVELOPE(element))
10652         local_player->show_envelope = element;
10653
10654       break;
10655     }
10656
10657     case CA_SET_LEVEL_TIME:
10658     {
10659       if (level.time > 0)       /* only modify limited time value */
10660       {
10661         TimeLeft = action_arg_number_new;
10662
10663 #if 1
10664         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10665
10666         DisplayGameControlValues();
10667 #else
10668         DrawGameValue_Time(TimeLeft);
10669 #endif
10670
10671         if (!TimeLeft && setup.time_limit)
10672           for (i = 0; i < MAX_PLAYERS; i++)
10673             KillPlayer(&stored_player[i]);
10674       }
10675
10676       break;
10677     }
10678
10679     case CA_SET_LEVEL_SCORE:
10680     {
10681       local_player->score = action_arg_number_new;
10682
10683 #if 1
10684       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10685
10686       DisplayGameControlValues();
10687 #else
10688       DrawGameValue_Score(local_player->score);
10689 #endif
10690
10691       break;
10692     }
10693
10694     case CA_SET_LEVEL_GEMS:
10695     {
10696       local_player->gems_still_needed = action_arg_number_new;
10697
10698 #if 1
10699       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10700
10701       DisplayGameControlValues();
10702 #else
10703       DrawGameValue_Emeralds(local_player->gems_still_needed);
10704 #endif
10705
10706       break;
10707     }
10708
10709 #if !USE_PLAYER_GRAVITY
10710     case CA_SET_LEVEL_GRAVITY:
10711     {
10712       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10713                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10714                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10715                       game.gravity);
10716       break;
10717     }
10718 #endif
10719
10720     case CA_SET_LEVEL_WIND:
10721     {
10722       game.wind_direction = action_arg_direction;
10723
10724       break;
10725     }
10726
10727     case CA_SET_LEVEL_RANDOM_SEED:
10728     {
10729 #if 1
10730       /* ensure that setting a new random seed while playing is predictable */
10731       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10732 #else
10733       InitRND(action_arg_number_new);
10734 #endif
10735
10736 #if 0
10737       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10738 #endif
10739
10740 #if 0
10741       {
10742         int i;
10743
10744         printf("::: ");
10745         for (i = 0; i < 9; i++)
10746           printf("%d, ", RND(2));
10747         printf("\n");
10748       }
10749 #endif
10750
10751       break;
10752     }
10753
10754     /* ---------- player actions  ------------------------------------------ */
10755
10756     case CA_MOVE_PLAYER:
10757     {
10758       /* automatically move to the next field in specified direction */
10759       for (i = 0; i < MAX_PLAYERS; i++)
10760         if (trigger_player_bits & (1 << i))
10761           stored_player[i].programmed_action = action_arg_direction;
10762
10763       break;
10764     }
10765
10766     case CA_EXIT_PLAYER:
10767     {
10768       for (i = 0; i < MAX_PLAYERS; i++)
10769         if (action_arg_player_bits & (1 << i))
10770           PlayerWins(&stored_player[i]);
10771
10772       break;
10773     }
10774
10775     case CA_KILL_PLAYER:
10776     {
10777       for (i = 0; i < MAX_PLAYERS; i++)
10778         if (action_arg_player_bits & (1 << i))
10779           KillPlayer(&stored_player[i]);
10780
10781       break;
10782     }
10783
10784     case CA_SET_PLAYER_KEYS:
10785     {
10786       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10787       int element = getSpecialActionElement(action_arg_element,
10788                                             action_arg_number, EL_KEY_1);
10789
10790       if (IS_KEY(element))
10791       {
10792         for (i = 0; i < MAX_PLAYERS; i++)
10793         {
10794           if (trigger_player_bits & (1 << i))
10795           {
10796             stored_player[i].key[KEY_NR(element)] = key_state;
10797
10798             DrawGameDoorValues();
10799           }
10800         }
10801       }
10802
10803       break;
10804     }
10805
10806     case CA_SET_PLAYER_SPEED:
10807     {
10808 #if 0
10809       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10810 #endif
10811
10812       for (i = 0; i < MAX_PLAYERS; i++)
10813       {
10814         if (trigger_player_bits & (1 << i))
10815         {
10816           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10817
10818           if (action_arg == CA_ARG_SPEED_FASTER &&
10819               stored_player[i].cannot_move)
10820           {
10821             action_arg_number = STEPSIZE_VERY_SLOW;
10822           }
10823           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10824                    action_arg == CA_ARG_SPEED_FASTER)
10825           {
10826             action_arg_number = 2;
10827             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10828                            CA_MODE_MULTIPLY);
10829           }
10830           else if (action_arg == CA_ARG_NUMBER_RESET)
10831           {
10832             action_arg_number = level.initial_player_stepsize[i];
10833           }
10834
10835           move_stepsize =
10836             getModifiedActionNumber(move_stepsize,
10837                                     action_mode,
10838                                     action_arg_number,
10839                                     action_arg_number_min,
10840                                     action_arg_number_max);
10841
10842           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10843         }
10844       }
10845
10846       break;
10847     }
10848
10849     case CA_SET_PLAYER_SHIELD:
10850     {
10851       for (i = 0; i < MAX_PLAYERS; i++)
10852       {
10853         if (trigger_player_bits & (1 << i))
10854         {
10855           if (action_arg == CA_ARG_SHIELD_OFF)
10856           {
10857             stored_player[i].shield_normal_time_left = 0;
10858             stored_player[i].shield_deadly_time_left = 0;
10859           }
10860           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10861           {
10862             stored_player[i].shield_normal_time_left = 999999;
10863           }
10864           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10865           {
10866             stored_player[i].shield_normal_time_left = 999999;
10867             stored_player[i].shield_deadly_time_left = 999999;
10868           }
10869         }
10870       }
10871
10872       break;
10873     }
10874
10875 #if USE_PLAYER_GRAVITY
10876     case CA_SET_PLAYER_GRAVITY:
10877     {
10878       for (i = 0; i < MAX_PLAYERS; i++)
10879       {
10880         if (trigger_player_bits & (1 << i))
10881         {
10882           stored_player[i].gravity =
10883             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10884              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10885              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10886              stored_player[i].gravity);
10887         }
10888       }
10889
10890       break;
10891     }
10892 #endif
10893
10894     case CA_SET_PLAYER_ARTWORK:
10895     {
10896       for (i = 0; i < MAX_PLAYERS; i++)
10897       {
10898         if (trigger_player_bits & (1 << i))
10899         {
10900           int artwork_element = action_arg_element;
10901
10902           if (action_arg == CA_ARG_ELEMENT_RESET)
10903             artwork_element =
10904               (level.use_artwork_element[i] ? level.artwork_element[i] :
10905                stored_player[i].element_nr);
10906
10907 #if USE_GFX_RESET_PLAYER_ARTWORK
10908           if (stored_player[i].artwork_element != artwork_element)
10909             stored_player[i].Frame = 0;
10910 #endif
10911
10912           stored_player[i].artwork_element = artwork_element;
10913
10914           SetPlayerWaiting(&stored_player[i], FALSE);
10915
10916           /* set number of special actions for bored and sleeping animation */
10917           stored_player[i].num_special_action_bored =
10918             get_num_special_action(artwork_element,
10919                                    ACTION_BORING_1, ACTION_BORING_LAST);
10920           stored_player[i].num_special_action_sleeping =
10921             get_num_special_action(artwork_element,
10922                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10923         }
10924       }
10925
10926       break;
10927     }
10928
10929     case CA_SET_PLAYER_INVENTORY:
10930     {
10931       for (i = 0; i < MAX_PLAYERS; i++)
10932       {
10933         struct PlayerInfo *player = &stored_player[i];
10934         int j, k;
10935
10936         if (trigger_player_bits & (1 << i))
10937         {
10938           int inventory_element = action_arg_element;
10939
10940           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10941               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10942               action_arg == CA_ARG_ELEMENT_ACTION)
10943           {
10944             int element = inventory_element;
10945             int collect_count = element_info[element].collect_count_initial;
10946
10947             if (!IS_CUSTOM_ELEMENT(element))
10948               collect_count = 1;
10949
10950             if (collect_count == 0)
10951               player->inventory_infinite_element = element;
10952             else
10953               for (k = 0; k < collect_count; k++)
10954                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10955                   player->inventory_element[player->inventory_size++] =
10956                     element;
10957           }
10958           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10959                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10960                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10961           {
10962             if (player->inventory_infinite_element != EL_UNDEFINED &&
10963                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10964                                      action_arg_element_raw))
10965               player->inventory_infinite_element = EL_UNDEFINED;
10966
10967             for (k = 0, j = 0; j < player->inventory_size; j++)
10968             {
10969               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10970                                         action_arg_element_raw))
10971                 player->inventory_element[k++] = player->inventory_element[j];
10972             }
10973
10974             player->inventory_size = k;
10975           }
10976           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10977           {
10978             if (player->inventory_size > 0)
10979             {
10980               for (j = 0; j < player->inventory_size - 1; j++)
10981                 player->inventory_element[j] = player->inventory_element[j + 1];
10982
10983               player->inventory_size--;
10984             }
10985           }
10986           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10987           {
10988             if (player->inventory_size > 0)
10989               player->inventory_size--;
10990           }
10991           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10992           {
10993             player->inventory_infinite_element = EL_UNDEFINED;
10994             player->inventory_size = 0;
10995           }
10996           else if (action_arg == CA_ARG_INVENTORY_RESET)
10997           {
10998             player->inventory_infinite_element = EL_UNDEFINED;
10999             player->inventory_size = 0;
11000
11001             if (level.use_initial_inventory[i])
11002             {
11003               for (j = 0; j < level.initial_inventory_size[i]; j++)
11004               {
11005                 int element = level.initial_inventory_content[i][j];
11006                 int collect_count = element_info[element].collect_count_initial;
11007
11008                 if (!IS_CUSTOM_ELEMENT(element))
11009                   collect_count = 1;
11010
11011                 if (collect_count == 0)
11012                   player->inventory_infinite_element = element;
11013                 else
11014                   for (k = 0; k < collect_count; k++)
11015                     if (player->inventory_size < MAX_INVENTORY_SIZE)
11016                       player->inventory_element[player->inventory_size++] =
11017                         element;
11018               }
11019             }
11020           }
11021         }
11022       }
11023
11024       break;
11025     }
11026
11027     /* ---------- CE actions  ---------------------------------------------- */
11028
11029     case CA_SET_CE_VALUE:
11030     {
11031 #if USE_NEW_CUSTOM_VALUE
11032       int last_ce_value = CustomValue[x][y];
11033
11034       CustomValue[x][y] = action_arg_number_new;
11035
11036       if (CustomValue[x][y] != last_ce_value)
11037       {
11038         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11039         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11040
11041         if (CustomValue[x][y] == 0)
11042         {
11043           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11044           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11045         }
11046       }
11047 #endif
11048
11049       break;
11050     }
11051
11052     case CA_SET_CE_SCORE:
11053     {
11054 #if USE_NEW_CUSTOM_VALUE
11055       int last_ce_score = ei->collect_score;
11056
11057       ei->collect_score = action_arg_number_new;
11058
11059       if (ei->collect_score != last_ce_score)
11060       {
11061         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11062         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11063
11064         if (ei->collect_score == 0)
11065         {
11066           int xx, yy;
11067
11068           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11069           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11070
11071           /*
11072             This is a very special case that seems to be a mixture between
11073             CheckElementChange() and CheckTriggeredElementChange(): while
11074             the first one only affects single elements that are triggered
11075             directly, the second one affects multiple elements in the playfield
11076             that are triggered indirectly by another element. This is a third
11077             case: Changing the CE score always affects multiple identical CEs,
11078             so every affected CE must be checked, not only the single CE for
11079             which the CE score was changed in the first place (as every instance
11080             of that CE shares the same CE score, and therefore also can change)!
11081           */
11082           SCAN_PLAYFIELD(xx, yy)
11083           {
11084             if (Feld[xx][yy] == element)
11085               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11086                                  CE_SCORE_GETS_ZERO);
11087           }
11088         }
11089       }
11090 #endif
11091
11092       break;
11093     }
11094
11095     case CA_SET_CE_ARTWORK:
11096     {
11097       int artwork_element = action_arg_element;
11098       boolean reset_frame = FALSE;
11099       int xx, yy;
11100
11101       if (action_arg == CA_ARG_ELEMENT_RESET)
11102         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11103                            element);
11104
11105       if (ei->gfx_element != artwork_element)
11106         reset_frame = TRUE;
11107
11108       ei->gfx_element = artwork_element;
11109
11110       SCAN_PLAYFIELD(xx, yy)
11111       {
11112         if (Feld[xx][yy] == element)
11113         {
11114           if (reset_frame)
11115           {
11116             ResetGfxAnimation(xx, yy);
11117             ResetRandomAnimationValue(xx, yy);
11118           }
11119
11120           TEST_DrawLevelField(xx, yy);
11121         }
11122       }
11123
11124       break;
11125     }
11126
11127     /* ---------- engine actions  ------------------------------------------ */
11128
11129     case CA_SET_ENGINE_SCAN_MODE:
11130     {
11131       InitPlayfieldScanMode(action_arg);
11132
11133       break;
11134     }
11135
11136     default:
11137       break;
11138   }
11139 }
11140
11141 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11142 {
11143   int old_element = Feld[x][y];
11144   int new_element = GetElementFromGroupElement(element);
11145   int previous_move_direction = MovDir[x][y];
11146 #if USE_NEW_CUSTOM_VALUE
11147   int last_ce_value = CustomValue[x][y];
11148 #endif
11149   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11150   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11151   boolean add_player_onto_element = (new_element_is_player &&
11152 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11153                                      /* this breaks SnakeBite when a snake is
11154                                         halfway through a door that closes */
11155                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11156                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11157 #endif
11158                                      IS_WALKABLE(old_element));
11159
11160 #if 0
11161   /* check if element under the player changes from accessible to unaccessible
11162      (needed for special case of dropping element which then changes) */
11163   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11164       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11165   {
11166     Bang(x, y);
11167
11168     return;
11169   }
11170 #endif
11171
11172   if (!add_player_onto_element)
11173   {
11174     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11175       RemoveMovingField(x, y);
11176     else
11177       RemoveField(x, y);
11178
11179     Feld[x][y] = new_element;
11180
11181 #if !USE_GFX_RESET_GFX_ANIMATION
11182     ResetGfxAnimation(x, y);
11183     ResetRandomAnimationValue(x, y);
11184 #endif
11185
11186     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11187       MovDir[x][y] = previous_move_direction;
11188
11189 #if USE_NEW_CUSTOM_VALUE
11190     if (element_info[new_element].use_last_ce_value)
11191       CustomValue[x][y] = last_ce_value;
11192 #endif
11193
11194     InitField_WithBug1(x, y, FALSE);
11195
11196     new_element = Feld[x][y];   /* element may have changed */
11197
11198 #if USE_GFX_RESET_GFX_ANIMATION
11199     ResetGfxAnimation(x, y);
11200     ResetRandomAnimationValue(x, y);
11201 #endif
11202
11203     TEST_DrawLevelField(x, y);
11204
11205     if (GFX_CRUMBLED(new_element))
11206       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11207   }
11208
11209 #if 1
11210   /* check if element under the player changes from accessible to unaccessible
11211      (needed for special case of dropping element which then changes) */
11212   /* (must be checked after creating new element for walkable group elements) */
11213 #if USE_FIX_KILLED_BY_NON_WALKABLE
11214   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11215       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11216   {
11217     Bang(x, y);
11218
11219     return;
11220   }
11221 #else
11222   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11223       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11224   {
11225     Bang(x, y);
11226
11227     return;
11228   }
11229 #endif
11230 #endif
11231
11232   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11233   if (new_element_is_player)
11234     RelocatePlayer(x, y, new_element);
11235
11236   if (is_change)
11237     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11238
11239   TestIfBadThingTouchesPlayer(x, y);
11240   TestIfPlayerTouchesCustomElement(x, y);
11241   TestIfElementTouchesCustomElement(x, y);
11242 }
11243
11244 static void CreateField(int x, int y, int element)
11245 {
11246   CreateFieldExt(x, y, element, FALSE);
11247 }
11248
11249 static void CreateElementFromChange(int x, int y, int element)
11250 {
11251   element = GET_VALID_RUNTIME_ELEMENT(element);
11252
11253 #if USE_STOP_CHANGED_ELEMENTS
11254   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11255   {
11256     int old_element = Feld[x][y];
11257
11258     /* prevent changed element from moving in same engine frame
11259        unless both old and new element can either fall or move */
11260     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11261         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11262       Stop[x][y] = TRUE;
11263   }
11264 #endif
11265
11266   CreateFieldExt(x, y, element, TRUE);
11267 }
11268
11269 static boolean ChangeElement(int x, int y, int element, int page)
11270 {
11271   struct ElementInfo *ei = &element_info[element];
11272   struct ElementChangeInfo *change = &ei->change_page[page];
11273   int ce_value = CustomValue[x][y];
11274   int ce_score = ei->collect_score;
11275   int target_element;
11276   int old_element = Feld[x][y];
11277
11278   /* always use default change event to prevent running into a loop */
11279   if (ChangeEvent[x][y] == -1)
11280     ChangeEvent[x][y] = CE_DELAY;
11281
11282   if (ChangeEvent[x][y] == CE_DELAY)
11283   {
11284     /* reset actual trigger element, trigger player and action element */
11285     change->actual_trigger_element = EL_EMPTY;
11286     change->actual_trigger_player = EL_EMPTY;
11287     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11288     change->actual_trigger_side = CH_SIDE_NONE;
11289     change->actual_trigger_ce_value = 0;
11290     change->actual_trigger_ce_score = 0;
11291   }
11292
11293   /* do not change elements more than a specified maximum number of changes */
11294   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11295     return FALSE;
11296
11297   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11298
11299   if (change->explode)
11300   {
11301     Bang(x, y);
11302
11303     return TRUE;
11304   }
11305
11306   if (change->use_target_content)
11307   {
11308     boolean complete_replace = TRUE;
11309     boolean can_replace[3][3];
11310     int xx, yy;
11311
11312     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11313     {
11314       boolean is_empty;
11315       boolean is_walkable;
11316       boolean is_diggable;
11317       boolean is_collectible;
11318       boolean is_removable;
11319       boolean is_destructible;
11320       int ex = x + xx - 1;
11321       int ey = y + yy - 1;
11322       int content_element = change->target_content.e[xx][yy];
11323       int e;
11324
11325       can_replace[xx][yy] = TRUE;
11326
11327       if (ex == x && ey == y)   /* do not check changing element itself */
11328         continue;
11329
11330       if (content_element == EL_EMPTY_SPACE)
11331       {
11332         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11333
11334         continue;
11335       }
11336
11337       if (!IN_LEV_FIELD(ex, ey))
11338       {
11339         can_replace[xx][yy] = FALSE;
11340         complete_replace = FALSE;
11341
11342         continue;
11343       }
11344
11345       e = Feld[ex][ey];
11346
11347       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11348         e = MovingOrBlocked2Element(ex, ey);
11349
11350       is_empty = (IS_FREE(ex, ey) ||
11351                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11352
11353       is_walkable     = (is_empty || IS_WALKABLE(e));
11354       is_diggable     = (is_empty || IS_DIGGABLE(e));
11355       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11356       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11357       is_removable    = (is_diggable || is_collectible);
11358
11359       can_replace[xx][yy] =
11360         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11361           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11362           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11363           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11364           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11365           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11366          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11367
11368       if (!can_replace[xx][yy])
11369         complete_replace = FALSE;
11370     }
11371
11372     if (!change->only_if_complete || complete_replace)
11373     {
11374       boolean something_has_changed = FALSE;
11375
11376       if (change->only_if_complete && change->use_random_replace &&
11377           RND(100) < change->random_percentage)
11378         return FALSE;
11379
11380       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11381       {
11382         int ex = x + xx - 1;
11383         int ey = y + yy - 1;
11384         int content_element;
11385
11386         if (can_replace[xx][yy] && (!change->use_random_replace ||
11387                                     RND(100) < change->random_percentage))
11388         {
11389           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11390             RemoveMovingField(ex, ey);
11391
11392           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11393
11394           content_element = change->target_content.e[xx][yy];
11395           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11396                                               ce_value, ce_score);
11397
11398           CreateElementFromChange(ex, ey, target_element);
11399
11400           something_has_changed = TRUE;
11401
11402           /* for symmetry reasons, freeze newly created border elements */
11403           if (ex != x || ey != y)
11404             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11405         }
11406       }
11407
11408       if (something_has_changed)
11409       {
11410         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11411         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11412       }
11413     }
11414   }
11415   else
11416   {
11417     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11418                                         ce_value, ce_score);
11419
11420     if (element == EL_DIAGONAL_GROWING ||
11421         element == EL_DIAGONAL_SHRINKING)
11422     {
11423       target_element = Store[x][y];
11424
11425       Store[x][y] = EL_EMPTY;
11426     }
11427
11428     CreateElementFromChange(x, y, target_element);
11429
11430     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11431     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11432   }
11433
11434   /* this uses direct change before indirect change */
11435   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11436
11437   return TRUE;
11438 }
11439
11440 #if USE_NEW_DELAYED_ACTION
11441
11442 static void HandleElementChange(int x, int y, int page)
11443 {
11444   int element = MovingOrBlocked2Element(x, y);
11445   struct ElementInfo *ei = &element_info[element];
11446   struct ElementChangeInfo *change = &ei->change_page[page];
11447   boolean handle_action_before_change = FALSE;
11448
11449 #ifdef DEBUG
11450   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11451       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11452   {
11453     printf("\n\n");
11454     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11455            x, y, element, element_info[element].token_name);
11456     printf("HandleElementChange(): This should never happen!\n");
11457     printf("\n\n");
11458   }
11459 #endif
11460
11461   /* this can happen with classic bombs on walkable, changing elements */
11462   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11463   {
11464 #if 0
11465     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11466       ChangeDelay[x][y] = 0;
11467 #endif
11468
11469     return;
11470   }
11471
11472   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11473   {
11474     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11475
11476     if (change->can_change)
11477     {
11478 #if 1
11479       /* !!! not clear why graphic animation should be reset at all here !!! */
11480       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11481 #if USE_GFX_RESET_WHEN_NOT_MOVING
11482       /* when a custom element is about to change (for example by change delay),
11483          do not reset graphic animation when the custom element is moving */
11484       if (!IS_MOVING(x, y))
11485 #endif
11486       {
11487         ResetGfxAnimation(x, y);
11488         ResetRandomAnimationValue(x, y);
11489       }
11490 #endif
11491
11492       if (change->pre_change_function)
11493         change->pre_change_function(x, y);
11494     }
11495   }
11496
11497   ChangeDelay[x][y]--;
11498
11499   if (ChangeDelay[x][y] != 0)           /* continue element change */
11500   {
11501     if (change->can_change)
11502     {
11503       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11504
11505       if (IS_ANIMATED(graphic))
11506         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11507
11508       if (change->change_function)
11509         change->change_function(x, y);
11510     }
11511   }
11512   else                                  /* finish element change */
11513   {
11514     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11515     {
11516       page = ChangePage[x][y];
11517       ChangePage[x][y] = -1;
11518
11519       change = &ei->change_page[page];
11520     }
11521
11522     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11523     {
11524       ChangeDelay[x][y] = 1;            /* try change after next move step */
11525       ChangePage[x][y] = page;          /* remember page to use for change */
11526
11527       return;
11528     }
11529
11530 #if 1
11531     /* special case: set new level random seed before changing element */
11532     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11533       handle_action_before_change = TRUE;
11534
11535     if (change->has_action && handle_action_before_change)
11536       ExecuteCustomElementAction(x, y, element, page);
11537 #endif
11538
11539     if (change->can_change)
11540     {
11541       if (ChangeElement(x, y, element, page))
11542       {
11543         if (change->post_change_function)
11544           change->post_change_function(x, y);
11545       }
11546     }
11547
11548     if (change->has_action && !handle_action_before_change)
11549       ExecuteCustomElementAction(x, y, element, page);
11550   }
11551 }
11552
11553 #else
11554
11555 static void HandleElementChange(int x, int y, int page)
11556 {
11557   int element = MovingOrBlocked2Element(x, y);
11558   struct ElementInfo *ei = &element_info[element];
11559   struct ElementChangeInfo *change = &ei->change_page[page];
11560
11561 #ifdef DEBUG
11562   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11563   {
11564     printf("\n\n");
11565     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11566            x, y, element, element_info[element].token_name);
11567     printf("HandleElementChange(): This should never happen!\n");
11568     printf("\n\n");
11569   }
11570 #endif
11571
11572   /* this can happen with classic bombs on walkable, changing elements */
11573   if (!CAN_CHANGE(element))
11574   {
11575 #if 0
11576     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11577       ChangeDelay[x][y] = 0;
11578 #endif
11579
11580     return;
11581   }
11582
11583   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11584   {
11585     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11586
11587     ResetGfxAnimation(x, y);
11588     ResetRandomAnimationValue(x, y);
11589
11590     if (change->pre_change_function)
11591       change->pre_change_function(x, y);
11592   }
11593
11594   ChangeDelay[x][y]--;
11595
11596   if (ChangeDelay[x][y] != 0)           /* continue element change */
11597   {
11598     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11599
11600     if (IS_ANIMATED(graphic))
11601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11602
11603     if (change->change_function)
11604       change->change_function(x, y);
11605   }
11606   else                                  /* finish element change */
11607   {
11608     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11609     {
11610       page = ChangePage[x][y];
11611       ChangePage[x][y] = -1;
11612
11613       change = &ei->change_page[page];
11614     }
11615
11616     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11617     {
11618       ChangeDelay[x][y] = 1;            /* try change after next move step */
11619       ChangePage[x][y] = page;          /* remember page to use for change */
11620
11621       return;
11622     }
11623
11624     if (ChangeElement(x, y, element, page))
11625     {
11626       if (change->post_change_function)
11627         change->post_change_function(x, y);
11628     }
11629   }
11630 }
11631
11632 #endif
11633
11634 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11635                                               int trigger_element,
11636                                               int trigger_event,
11637                                               int trigger_player,
11638                                               int trigger_side,
11639                                               int trigger_page)
11640 {
11641   boolean change_done_any = FALSE;
11642   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11643   int i;
11644
11645   if (!(trigger_events[trigger_element][trigger_event]))
11646     return FALSE;
11647
11648 #if 0
11649   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11650          trigger_event, recursion_loop_depth, recursion_loop_detected,
11651          recursion_loop_element, EL_NAME(recursion_loop_element));
11652 #endif
11653
11654   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11655
11656   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11657   {
11658     int element = EL_CUSTOM_START + i;
11659     boolean change_done = FALSE;
11660     int p;
11661
11662     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11663         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11664       continue;
11665
11666     for (p = 0; p < element_info[element].num_change_pages; p++)
11667     {
11668       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11669
11670       if (change->can_change_or_has_action &&
11671           change->has_event[trigger_event] &&
11672           change->trigger_side & trigger_side &&
11673           change->trigger_player & trigger_player &&
11674           change->trigger_page & trigger_page_bits &&
11675           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11676       {
11677         change->actual_trigger_element = trigger_element;
11678         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11679         change->actual_trigger_player_bits = trigger_player;
11680         change->actual_trigger_side = trigger_side;
11681         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11682         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11683
11684 #if 0
11685         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11686                element, EL_NAME(element), p);
11687 #endif
11688
11689         if ((change->can_change && !change_done) || change->has_action)
11690         {
11691           int x, y;
11692
11693           SCAN_PLAYFIELD(x, y)
11694           {
11695             if (Feld[x][y] == element)
11696             {
11697               if (change->can_change && !change_done)
11698               {
11699 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11700                 /* if element already changed in this frame, not only prevent
11701                    another element change (checked in ChangeElement()), but
11702                    also prevent additional element actions for this element */
11703
11704                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11705                     !level.use_action_after_change_bug)
11706                   continue;
11707 #endif
11708
11709 #if 0
11710                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11711                        element, EL_NAME(element), p);
11712 #endif
11713
11714                 ChangeDelay[x][y] = 1;
11715                 ChangeEvent[x][y] = trigger_event;
11716
11717                 HandleElementChange(x, y, p);
11718               }
11719 #if USE_NEW_DELAYED_ACTION
11720               else if (change->has_action)
11721               {
11722 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11723                 /* if element already changed in this frame, not only prevent
11724                    another element change (checked in ChangeElement()), but
11725                    also prevent additional element actions for this element */
11726
11727                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11728                     !level.use_action_after_change_bug)
11729                   continue;
11730 #endif
11731
11732
11733 #if 0
11734                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11735                        element, EL_NAME(element), p);
11736 #endif
11737
11738                 ExecuteCustomElementAction(x, y, element, p);
11739                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11740               }
11741 #else
11742               if (change->has_action)
11743               {
11744                 ExecuteCustomElementAction(x, y, element, p);
11745                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11746               }
11747 #endif
11748             }
11749           }
11750
11751           if (change->can_change)
11752           {
11753             change_done = TRUE;
11754             change_done_any = TRUE;
11755
11756 #if 0
11757             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11758                    element, EL_NAME(element), p);
11759 #endif
11760
11761           }
11762         }
11763       }
11764     }
11765   }
11766
11767   RECURSION_LOOP_DETECTION_END();
11768
11769   return change_done_any;
11770 }
11771
11772 static boolean CheckElementChangeExt(int x, int y,
11773                                      int element,
11774                                      int trigger_element,
11775                                      int trigger_event,
11776                                      int trigger_player,
11777                                      int trigger_side)
11778 {
11779   boolean change_done = FALSE;
11780   int p;
11781
11782   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11783       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11784     return FALSE;
11785
11786   if (Feld[x][y] == EL_BLOCKED)
11787   {
11788     Blocked2Moving(x, y, &x, &y);
11789     element = Feld[x][y];
11790   }
11791
11792 #if 0
11793   /* check if element has already changed */
11794   if (Feld[x][y] != element)
11795     return FALSE;
11796 #else
11797   /* check if element has already changed or is about to change after moving */
11798   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11799        Feld[x][y] != element) ||
11800
11801       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11802        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11803         ChangePage[x][y] != -1)))
11804     return FALSE;
11805 #endif
11806
11807 #if 0
11808   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11809          trigger_event, recursion_loop_depth, recursion_loop_detected,
11810          recursion_loop_element, EL_NAME(recursion_loop_element));
11811 #endif
11812
11813   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11814
11815 #if 0
11816   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11817 #endif
11818
11819   for (p = 0; p < element_info[element].num_change_pages; p++)
11820   {
11821     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11822
11823     /* check trigger element for all events where the element that is checked
11824        for changing interacts with a directly adjacent element -- this is
11825        different to element changes that affect other elements to change on the
11826        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11827     boolean check_trigger_element =
11828       (trigger_event == CE_TOUCHING_X ||
11829        trigger_event == CE_HITTING_X ||
11830        trigger_event == CE_HIT_BY_X ||
11831 #if 1
11832        /* this one was forgotten until 3.2.3 */
11833        trigger_event == CE_DIGGING_X);
11834 #endif
11835
11836     if (change->can_change_or_has_action &&
11837         change->has_event[trigger_event] &&
11838         change->trigger_side & trigger_side &&
11839         change->trigger_player & trigger_player &&
11840         (!check_trigger_element ||
11841          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11842     {
11843       change->actual_trigger_element = trigger_element;
11844       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11845       change->actual_trigger_player_bits = trigger_player;
11846       change->actual_trigger_side = trigger_side;
11847       change->actual_trigger_ce_value = CustomValue[x][y];
11848       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11849
11850       /* special case: trigger element not at (x,y) position for some events */
11851       if (check_trigger_element)
11852       {
11853         static struct
11854         {
11855           int dx, dy;
11856         } move_xy[] =
11857           {
11858             {  0,  0 },
11859             { -1,  0 },
11860             { +1,  0 },
11861             {  0,  0 },
11862             {  0, -1 },
11863             {  0,  0 }, { 0, 0 }, { 0, 0 },
11864             {  0, +1 }
11865           };
11866
11867         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11868         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11869
11870         change->actual_trigger_ce_value = CustomValue[xx][yy];
11871         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11872       }
11873
11874       if (change->can_change && !change_done)
11875       {
11876         ChangeDelay[x][y] = 1;
11877         ChangeEvent[x][y] = trigger_event;
11878
11879         HandleElementChange(x, y, p);
11880
11881         change_done = TRUE;
11882       }
11883 #if USE_NEW_DELAYED_ACTION
11884       else if (change->has_action)
11885       {
11886         ExecuteCustomElementAction(x, y, element, p);
11887         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11888       }
11889 #else
11890       if (change->has_action)
11891       {
11892         ExecuteCustomElementAction(x, y, element, p);
11893         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11894       }
11895 #endif
11896     }
11897   }
11898
11899   RECURSION_LOOP_DETECTION_END();
11900
11901   return change_done;
11902 }
11903
11904 static void PlayPlayerSound(struct PlayerInfo *player)
11905 {
11906   int jx = player->jx, jy = player->jy;
11907   int sound_element = player->artwork_element;
11908   int last_action = player->last_action_waiting;
11909   int action = player->action_waiting;
11910
11911   if (player->is_waiting)
11912   {
11913     if (action != last_action)
11914       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11915     else
11916       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11917   }
11918   else
11919   {
11920     if (action != last_action)
11921       StopSound(element_info[sound_element].sound[last_action]);
11922
11923     if (last_action == ACTION_SLEEPING)
11924       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11925   }
11926 }
11927
11928 static void PlayAllPlayersSound()
11929 {
11930   int i;
11931
11932   for (i = 0; i < MAX_PLAYERS; i++)
11933     if (stored_player[i].active)
11934       PlayPlayerSound(&stored_player[i]);
11935 }
11936
11937 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11938 {
11939   boolean last_waiting = player->is_waiting;
11940   int move_dir = player->MovDir;
11941
11942   player->dir_waiting = move_dir;
11943   player->last_action_waiting = player->action_waiting;
11944
11945   if (is_waiting)
11946   {
11947     if (!last_waiting)          /* not waiting -> waiting */
11948     {
11949       player->is_waiting = TRUE;
11950
11951       player->frame_counter_bored =
11952         FrameCounter +
11953         game.player_boring_delay_fixed +
11954         GetSimpleRandom(game.player_boring_delay_random);
11955       player->frame_counter_sleeping =
11956         FrameCounter +
11957         game.player_sleeping_delay_fixed +
11958         GetSimpleRandom(game.player_sleeping_delay_random);
11959
11960       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11961     }
11962
11963     if (game.player_sleeping_delay_fixed +
11964         game.player_sleeping_delay_random > 0 &&
11965         player->anim_delay_counter == 0 &&
11966         player->post_delay_counter == 0 &&
11967         FrameCounter >= player->frame_counter_sleeping)
11968       player->is_sleeping = TRUE;
11969     else if (game.player_boring_delay_fixed +
11970              game.player_boring_delay_random > 0 &&
11971              FrameCounter >= player->frame_counter_bored)
11972       player->is_bored = TRUE;
11973
11974     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11975                               player->is_bored ? ACTION_BORING :
11976                               ACTION_WAITING);
11977
11978     if (player->is_sleeping && player->use_murphy)
11979     {
11980       /* special case for sleeping Murphy when leaning against non-free tile */
11981
11982       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11983           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11984            !IS_MOVING(player->jx - 1, player->jy)))
11985         move_dir = MV_LEFT;
11986       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11987                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11988                 !IS_MOVING(player->jx + 1, player->jy)))
11989         move_dir = MV_RIGHT;
11990       else
11991         player->is_sleeping = FALSE;
11992
11993       player->dir_waiting = move_dir;
11994     }
11995
11996     if (player->is_sleeping)
11997     {
11998       if (player->num_special_action_sleeping > 0)
11999       {
12000         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12001         {
12002           int last_special_action = player->special_action_sleeping;
12003           int num_special_action = player->num_special_action_sleeping;
12004           int special_action =
12005             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12006              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12007              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12008              last_special_action + 1 : ACTION_SLEEPING);
12009           int special_graphic =
12010             el_act_dir2img(player->artwork_element, special_action, move_dir);
12011
12012           player->anim_delay_counter =
12013             graphic_info[special_graphic].anim_delay_fixed +
12014             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12015           player->post_delay_counter =
12016             graphic_info[special_graphic].post_delay_fixed +
12017             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12018
12019           player->special_action_sleeping = special_action;
12020         }
12021
12022         if (player->anim_delay_counter > 0)
12023         {
12024           player->action_waiting = player->special_action_sleeping;
12025           player->anim_delay_counter--;
12026         }
12027         else if (player->post_delay_counter > 0)
12028         {
12029           player->post_delay_counter--;
12030         }
12031       }
12032     }
12033     else if (player->is_bored)
12034     {
12035       if (player->num_special_action_bored > 0)
12036       {
12037         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12038         {
12039           int special_action =
12040             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12041           int special_graphic =
12042             el_act_dir2img(player->artwork_element, special_action, move_dir);
12043
12044           player->anim_delay_counter =
12045             graphic_info[special_graphic].anim_delay_fixed +
12046             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12047           player->post_delay_counter =
12048             graphic_info[special_graphic].post_delay_fixed +
12049             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12050
12051           player->special_action_bored = special_action;
12052         }
12053
12054         if (player->anim_delay_counter > 0)
12055         {
12056           player->action_waiting = player->special_action_bored;
12057           player->anim_delay_counter--;
12058         }
12059         else if (player->post_delay_counter > 0)
12060         {
12061           player->post_delay_counter--;
12062         }
12063       }
12064     }
12065   }
12066   else if (last_waiting)        /* waiting -> not waiting */
12067   {
12068     player->is_waiting = FALSE;
12069     player->is_bored = FALSE;
12070     player->is_sleeping = FALSE;
12071
12072     player->frame_counter_bored = -1;
12073     player->frame_counter_sleeping = -1;
12074
12075     player->anim_delay_counter = 0;
12076     player->post_delay_counter = 0;
12077
12078     player->dir_waiting = player->MovDir;
12079     player->action_waiting = ACTION_DEFAULT;
12080
12081     player->special_action_bored = ACTION_DEFAULT;
12082     player->special_action_sleeping = ACTION_DEFAULT;
12083   }
12084 }
12085
12086 static void CheckSingleStepMode(struct PlayerInfo *player)
12087 {
12088   if (tape.single_step && tape.recording && !tape.pausing)
12089   {
12090     /* as it is called "single step mode", just return to pause mode when the
12091        player stopped moving after one tile (or never starts moving at all) */
12092     if (!player->is_moving && !player->is_pushing)
12093     {
12094       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12095       SnapField(player, 0, 0);                  /* stop snapping */
12096     }
12097   }
12098 }
12099
12100 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12101 {
12102   int left      = player_action & JOY_LEFT;
12103   int right     = player_action & JOY_RIGHT;
12104   int up        = player_action & JOY_UP;
12105   int down      = player_action & JOY_DOWN;
12106   int button1   = player_action & JOY_BUTTON_1;
12107   int button2   = player_action & JOY_BUTTON_2;
12108   int dx        = (left ? -1 : right ? 1 : 0);
12109   int dy        = (up   ? -1 : down  ? 1 : 0);
12110
12111   if (!player->active || tape.pausing)
12112     return 0;
12113
12114   if (player_action)
12115   {
12116     if (button1)
12117       SnapField(player, dx, dy);
12118     else
12119     {
12120       if (button2)
12121         DropElement(player);
12122
12123       MovePlayer(player, dx, dy);
12124     }
12125
12126     CheckSingleStepMode(player);
12127
12128     SetPlayerWaiting(player, FALSE);
12129
12130     return player_action;
12131   }
12132   else
12133   {
12134     /* no actions for this player (no input at player's configured device) */
12135
12136     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12137     SnapField(player, 0, 0);
12138     CheckGravityMovementWhenNotMoving(player);
12139
12140     if (player->MovPos == 0)
12141       SetPlayerWaiting(player, TRUE);
12142
12143     if (player->MovPos == 0)    /* needed for tape.playing */
12144       player->is_moving = FALSE;
12145
12146     player->is_dropping = FALSE;
12147     player->is_dropping_pressed = FALSE;
12148     player->drop_pressed_delay = 0;
12149
12150     CheckSingleStepMode(player);
12151
12152     return 0;
12153   }
12154 }
12155
12156 static void CheckLevelTime()
12157 {
12158   int i;
12159
12160   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12161   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12162   {
12163     if (level.native_em_level->lev->home == 0)  /* all players at home */
12164     {
12165       PlayerWins(local_player);
12166
12167       AllPlayersGone = TRUE;
12168
12169       level.native_em_level->lev->home = -1;
12170     }
12171
12172     if (level.native_em_level->ply[0]->alive == 0 &&
12173         level.native_em_level->ply[1]->alive == 0 &&
12174         level.native_em_level->ply[2]->alive == 0 &&
12175         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12176       AllPlayersGone = TRUE;
12177   }
12178   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12179   {
12180     if (game_sp.LevelSolved &&
12181         !game_sp.GameOver)                              /* game won */
12182     {
12183       PlayerWins(local_player);
12184
12185       game_sp.GameOver = TRUE;
12186
12187       AllPlayersGone = TRUE;
12188     }
12189
12190     if (game_sp.GameOver)                               /* game lost */
12191       AllPlayersGone = TRUE;
12192   }
12193
12194   if (TimeFrames >= FRAMES_PER_SECOND)
12195   {
12196     TimeFrames = 0;
12197     TapeTime++;
12198
12199     for (i = 0; i < MAX_PLAYERS; i++)
12200     {
12201       struct PlayerInfo *player = &stored_player[i];
12202
12203       if (SHIELD_ON(player))
12204       {
12205         player->shield_normal_time_left--;
12206
12207         if (player->shield_deadly_time_left > 0)
12208           player->shield_deadly_time_left--;
12209       }
12210     }
12211
12212     if (!local_player->LevelSolved && !level.use_step_counter)
12213     {
12214       TimePlayed++;
12215
12216       if (TimeLeft > 0)
12217       {
12218         TimeLeft--;
12219
12220         if (TimeLeft <= 10 && setup.time_limit)
12221           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12222
12223 #if 1
12224         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12225            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12226
12227         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12228
12229         /* (already called by UpdateAndDisplayGameControlValues() below) */
12230         // DisplayGameControlValues();
12231 #else
12232         DrawGameValue_Time(TimeLeft);
12233 #endif
12234
12235         if (!TimeLeft && setup.time_limit)
12236         {
12237           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12238             level.native_em_level->lev->killed_out_of_time = TRUE;
12239           else
12240             for (i = 0; i < MAX_PLAYERS; i++)
12241               KillPlayer(&stored_player[i]);
12242         }
12243       }
12244 #if 1
12245       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12246       {
12247         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12248
12249         /* (already called by UpdateAndDisplayGameControlValues() below) */
12250         // DisplayGameControlValues();
12251       }
12252 #else
12253       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12254         DrawGameValue_Time(TimePlayed);
12255 #endif
12256
12257       level.native_em_level->lev->time =
12258         (game.no_time_limit ? TimePlayed : TimeLeft);
12259     }
12260
12261     if (tape.recording || tape.playing)
12262       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12263   }
12264
12265 #if 1
12266   UpdateAndDisplayGameControlValues();
12267 #else
12268   UpdateGameDoorValues();
12269   DrawGameDoorValues();
12270 #endif
12271 }
12272
12273 void AdvanceFrameAndPlayerCounters(int player_nr)
12274 {
12275   int i;
12276
12277   /* advance frame counters (global frame counter and time frame counter) */
12278   FrameCounter++;
12279   TimeFrames++;
12280
12281   /* advance player counters (counters for move delay, move animation etc.) */
12282   for (i = 0; i < MAX_PLAYERS; i++)
12283   {
12284     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12285     int move_delay_value = stored_player[i].move_delay_value;
12286     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12287
12288     if (!advance_player_counters)       /* not all players may be affected */
12289       continue;
12290
12291 #if USE_NEW_PLAYER_ANIM
12292     if (move_frames == 0)       /* less than one move per game frame */
12293     {
12294       int stepsize = TILEX / move_delay_value;
12295       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12296       int count = (stored_player[i].is_moving ?
12297                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12298
12299       if (count % delay == 0)
12300         move_frames = 1;
12301     }
12302 #endif
12303
12304     stored_player[i].Frame += move_frames;
12305
12306     if (stored_player[i].MovPos != 0)
12307       stored_player[i].StepFrame += move_frames;
12308
12309     if (stored_player[i].move_delay > 0)
12310       stored_player[i].move_delay--;
12311
12312     /* due to bugs in previous versions, counter must count up, not down */
12313     if (stored_player[i].push_delay != -1)
12314       stored_player[i].push_delay++;
12315
12316     if (stored_player[i].drop_delay > 0)
12317       stored_player[i].drop_delay--;
12318
12319     if (stored_player[i].is_dropping_pressed)
12320       stored_player[i].drop_pressed_delay++;
12321   }
12322 }
12323
12324 void StartGameActions(boolean init_network_game, boolean record_tape,
12325                       int random_seed)
12326 {
12327   unsigned int new_random_seed = InitRND(random_seed);
12328
12329   if (record_tape)
12330     TapeStartRecording(new_random_seed);
12331
12332 #if defined(NETWORK_AVALIABLE)
12333   if (init_network_game)
12334   {
12335     SendToServer_StartPlaying();
12336
12337     return;
12338   }
12339 #endif
12340
12341   InitGame();
12342 }
12343
12344 void GameActions()
12345 {
12346   static unsigned int game_frame_delay = 0;
12347   unsigned int game_frame_delay_value;
12348   byte *recorded_player_action;
12349   byte summarized_player_action = 0;
12350   byte tape_action[MAX_PLAYERS];
12351   int i;
12352
12353   /* detect endless loops, caused by custom element programming */
12354   if (recursion_loop_detected && recursion_loop_depth == 0)
12355   {
12356     char *message = getStringCat3("Internal Error! Element ",
12357                                   EL_NAME(recursion_loop_element),
12358                                   " caused endless loop! Quit the game?");
12359
12360     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12361           EL_NAME(recursion_loop_element));
12362
12363     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12364
12365     recursion_loop_detected = FALSE;    /* if game should be continued */
12366
12367     free(message);
12368
12369     return;
12370   }
12371
12372   if (game.restart_level)
12373     StartGameActions(options.network, setup.autorecord, level.random_seed);
12374
12375   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12376   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12377   {
12378     if (level.native_em_level->lev->home == 0)  /* all players at home */
12379     {
12380       PlayerWins(local_player);
12381
12382       AllPlayersGone = TRUE;
12383
12384       level.native_em_level->lev->home = -1;
12385     }
12386
12387     if (level.native_em_level->ply[0]->alive == 0 &&
12388         level.native_em_level->ply[1]->alive == 0 &&
12389         level.native_em_level->ply[2]->alive == 0 &&
12390         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12391       AllPlayersGone = TRUE;
12392   }
12393   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12394   {
12395     if (game_sp.LevelSolved &&
12396         !game_sp.GameOver)                              /* game won */
12397     {
12398       PlayerWins(local_player);
12399
12400       game_sp.GameOver = TRUE;
12401
12402       AllPlayersGone = TRUE;
12403     }
12404
12405     if (game_sp.GameOver)                               /* game lost */
12406       AllPlayersGone = TRUE;
12407   }
12408
12409   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12410     GameWon();
12411
12412   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12413     TapeStop();
12414
12415   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12416     return;
12417
12418   game_frame_delay_value =
12419     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12420
12421   if (tape.playing && tape.warp_forward && !tape.pausing)
12422     game_frame_delay_value = 0;
12423
12424   /* ---------- main game synchronization point ---------- */
12425
12426   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12427
12428   if (network_playing && !network_player_action_received)
12429   {
12430     /* try to get network player actions in time */
12431
12432 #if defined(NETWORK_AVALIABLE)
12433     /* last chance to get network player actions without main loop delay */
12434     HandleNetworking();
12435 #endif
12436
12437     /* game was quit by network peer */
12438     if (game_status != GAME_MODE_PLAYING)
12439       return;
12440
12441     if (!network_player_action_received)
12442       return;           /* failed to get network player actions in time */
12443
12444     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12445   }
12446
12447   if (tape.pausing)
12448     return;
12449
12450   /* at this point we know that we really continue executing the game */
12451
12452   network_player_action_received = FALSE;
12453
12454   /* when playing tape, read previously recorded player input from tape data */
12455   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12456
12457 #if 1
12458   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12459   if (tape.pausing)
12460     return;
12461 #endif
12462
12463   if (tape.set_centered_player)
12464   {
12465     game.centered_player_nr_next = tape.centered_player_nr_next;
12466     game.set_centered_player = TRUE;
12467   }
12468
12469   for (i = 0; i < MAX_PLAYERS; i++)
12470   {
12471     summarized_player_action |= stored_player[i].action;
12472
12473 #if 1
12474     if (!network_playing && (game.team_mode || tape.playing))
12475       stored_player[i].effective_action = stored_player[i].action;
12476 #else
12477     if (!network_playing)
12478       stored_player[i].effective_action = stored_player[i].action;
12479 #endif
12480   }
12481
12482 #if defined(NETWORK_AVALIABLE)
12483   if (network_playing)
12484     SendToServer_MovePlayer(summarized_player_action);
12485 #endif
12486
12487   if (!options.network && !game.team_mode)
12488     local_player->effective_action = summarized_player_action;
12489
12490   if (tape.recording &&
12491       setup.team_mode &&
12492       setup.input_on_focus &&
12493       game.centered_player_nr != -1)
12494   {
12495     for (i = 0; i < MAX_PLAYERS; i++)
12496       stored_player[i].effective_action =
12497         (i == game.centered_player_nr ? summarized_player_action : 0);
12498   }
12499
12500   if (recorded_player_action != NULL)
12501     for (i = 0; i < MAX_PLAYERS; i++)
12502       stored_player[i].effective_action = recorded_player_action[i];
12503
12504   for (i = 0; i < MAX_PLAYERS; i++)
12505   {
12506     tape_action[i] = stored_player[i].effective_action;
12507
12508 #if 1
12509     /* (this may happen in the RND game engine if a player was not present on
12510        the playfield on level start, but appeared later from a custom element */
12511     if (tape.recording &&
12512         setup.team_mode &&
12513         tape_action[i] &&
12514         !tape.player_participates[i])
12515       tape.player_participates[i] = TRUE;
12516 #else
12517     /* (this can only happen in the R'n'D game engine) */
12518     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12519       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12520 #endif
12521   }
12522
12523   /* only record actions from input devices, but not programmed actions */
12524   if (tape.recording)
12525     TapeRecordAction(tape_action);
12526
12527 #if USE_NEW_PLAYER_ASSIGNMENTS
12528 #if 1
12529   if (game.team_mode)
12530 #endif
12531   {
12532     byte mapped_action[MAX_PLAYERS];
12533
12534 #if DEBUG_PLAYER_ACTIONS
12535     printf(":::");
12536     for (i = 0; i < MAX_PLAYERS; i++)
12537       printf(" %d, ", stored_player[i].effective_action);
12538 #endif
12539
12540     for (i = 0; i < MAX_PLAYERS; i++)
12541       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12542
12543     for (i = 0; i < MAX_PLAYERS; i++)
12544       stored_player[i].effective_action = mapped_action[i];
12545
12546 #if DEBUG_PLAYER_ACTIONS
12547     printf(" =>");
12548     for (i = 0; i < MAX_PLAYERS; i++)
12549       printf(" %d, ", stored_player[i].effective_action);
12550     printf("\n");
12551 #endif
12552   }
12553 #if DEBUG_PLAYER_ACTIONS
12554   else
12555   {
12556     printf(":::");
12557     for (i = 0; i < MAX_PLAYERS; i++)
12558       printf(" %d, ", stored_player[i].effective_action);
12559     printf("\n");
12560   }
12561 #endif
12562 #endif
12563
12564 #if 0
12565   printf("::: summarized_player_action == %d\n",
12566          local_player->effective_action);
12567 #endif
12568
12569
12570
12571
12572 #if 0
12573 #if DEBUG_INIT_PLAYER
12574     if (options.debug)
12575     {
12576       printf("Player status (final):\n");
12577
12578       for (i = 0; i < MAX_PLAYERS; i++)
12579       {
12580         struct PlayerInfo *player = &stored_player[i];
12581
12582         printf("- player %d: present == %d, connected == %d, active == %d",
12583                i + 1,
12584                player->present,
12585                player->connected,
12586                player->active);
12587
12588         if (local_player == player)
12589           printf(" (local player)");
12590
12591         printf("\n");
12592       }
12593     }
12594 #endif
12595 #endif
12596
12597
12598
12599   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12600   {
12601     GameActions_EM_Main();
12602   }
12603   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12604   {
12605     GameActions_SP_Main();
12606   }
12607   else
12608   {
12609     GameActions_RND();
12610   }
12611 }
12612
12613 void GameActions_EM_Main()
12614 {
12615   byte effective_action[MAX_PLAYERS];
12616   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12617   int i;
12618
12619   for (i = 0; i < MAX_PLAYERS; i++)
12620     effective_action[i] = stored_player[i].effective_action;
12621
12622   GameActions_EM(effective_action, warp_mode);
12623
12624   CheckLevelTime();
12625
12626   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12627 }
12628
12629 void GameActions_SP_Main()
12630 {
12631   byte effective_action[MAX_PLAYERS];
12632   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12633   int i;
12634
12635   for (i = 0; i < MAX_PLAYERS; i++)
12636     effective_action[i] = stored_player[i].effective_action;
12637
12638   GameActions_SP(effective_action, warp_mode);
12639
12640   CheckLevelTime();
12641
12642   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12643 }
12644
12645 void GameActions_RND()
12646 {
12647   int magic_wall_x = 0, magic_wall_y = 0;
12648   int i, x, y, element, graphic;
12649
12650   InitPlayfieldScanModeVars();
12651
12652 #if USE_ONE_MORE_CHANGE_PER_FRAME
12653   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12654   {
12655     SCAN_PLAYFIELD(x, y)
12656     {
12657       ChangeCount[x][y] = 0;
12658       ChangeEvent[x][y] = -1;
12659     }
12660   }
12661 #endif
12662
12663   if (game.set_centered_player)
12664   {
12665     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12666
12667     /* switching to "all players" only possible if all players fit to screen */
12668     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12669     {
12670       game.centered_player_nr_next = game.centered_player_nr;
12671       game.set_centered_player = FALSE;
12672     }
12673
12674     /* do not switch focus to non-existing (or non-active) player */
12675     if (game.centered_player_nr_next >= 0 &&
12676         !stored_player[game.centered_player_nr_next].active)
12677     {
12678       game.centered_player_nr_next = game.centered_player_nr;
12679       game.set_centered_player = FALSE;
12680     }
12681   }
12682
12683   if (game.set_centered_player &&
12684       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12685   {
12686     int sx, sy;
12687
12688     if (game.centered_player_nr_next == -1)
12689     {
12690       setScreenCenteredToAllPlayers(&sx, &sy);
12691     }
12692     else
12693     {
12694       sx = stored_player[game.centered_player_nr_next].jx;
12695       sy = stored_player[game.centered_player_nr_next].jy;
12696     }
12697
12698     game.centered_player_nr = game.centered_player_nr_next;
12699     game.set_centered_player = FALSE;
12700
12701     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12702     DrawGameDoorValues();
12703   }
12704
12705   for (i = 0; i < MAX_PLAYERS; i++)
12706   {
12707     int actual_player_action = stored_player[i].effective_action;
12708
12709 #if 1
12710     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12711        - rnd_equinox_tetrachloride 048
12712        - rnd_equinox_tetrachloride_ii 096
12713        - rnd_emanuel_schmieg 002
12714        - doctor_sloan_ww 001, 020
12715     */
12716     if (stored_player[i].MovPos == 0)
12717       CheckGravityMovement(&stored_player[i]);
12718 #endif
12719
12720     /* overwrite programmed action with tape action */
12721     if (stored_player[i].programmed_action)
12722       actual_player_action = stored_player[i].programmed_action;
12723
12724     PlayerActions(&stored_player[i], actual_player_action);
12725
12726     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12727   }
12728
12729   ScrollScreen(NULL, SCROLL_GO_ON);
12730
12731   /* for backwards compatibility, the following code emulates a fixed bug that
12732      occured when pushing elements (causing elements that just made their last
12733      pushing step to already (if possible) make their first falling step in the
12734      same game frame, which is bad); this code is also needed to use the famous
12735      "spring push bug" which is used in older levels and might be wanted to be
12736      used also in newer levels, but in this case the buggy pushing code is only
12737      affecting the "spring" element and no other elements */
12738
12739   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12740   {
12741     for (i = 0; i < MAX_PLAYERS; i++)
12742     {
12743       struct PlayerInfo *player = &stored_player[i];
12744       int x = player->jx;
12745       int y = player->jy;
12746
12747       if (player->active && player->is_pushing && player->is_moving &&
12748           IS_MOVING(x, y) &&
12749           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12750            Feld[x][y] == EL_SPRING))
12751       {
12752         ContinueMoving(x, y);
12753
12754         /* continue moving after pushing (this is actually a bug) */
12755         if (!IS_MOVING(x, y))
12756           Stop[x][y] = FALSE;
12757       }
12758     }
12759   }
12760
12761 #if 0
12762   debug_print_timestamp(0, "start main loop profiling");
12763 #endif
12764
12765   SCAN_PLAYFIELD(x, y)
12766   {
12767     ChangeCount[x][y] = 0;
12768     ChangeEvent[x][y] = -1;
12769
12770     /* this must be handled before main playfield loop */
12771     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12772     {
12773       MovDelay[x][y]--;
12774       if (MovDelay[x][y] <= 0)
12775         RemoveField(x, y);
12776     }
12777
12778 #if USE_NEW_SNAP_DELAY
12779     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12780     {
12781       MovDelay[x][y]--;
12782       if (MovDelay[x][y] <= 0)
12783       {
12784         RemoveField(x, y);
12785         TEST_DrawLevelField(x, y);
12786
12787         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12788       }
12789     }
12790 #endif
12791
12792 #if DEBUG
12793     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12794     {
12795       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12796       printf("GameActions(): This should never happen!\n");
12797
12798       ChangePage[x][y] = -1;
12799     }
12800 #endif
12801
12802     Stop[x][y] = FALSE;
12803     if (WasJustMoving[x][y] > 0)
12804       WasJustMoving[x][y]--;
12805     if (WasJustFalling[x][y] > 0)
12806       WasJustFalling[x][y]--;
12807     if (CheckCollision[x][y] > 0)
12808       CheckCollision[x][y]--;
12809     if (CheckImpact[x][y] > 0)
12810       CheckImpact[x][y]--;
12811
12812     GfxFrame[x][y]++;
12813
12814     /* reset finished pushing action (not done in ContinueMoving() to allow
12815        continuous pushing animation for elements with zero push delay) */
12816     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12817     {
12818       ResetGfxAnimation(x, y);
12819       TEST_DrawLevelField(x, y);
12820     }
12821
12822 #if DEBUG
12823     if (IS_BLOCKED(x, y))
12824     {
12825       int oldx, oldy;
12826
12827       Blocked2Moving(x, y, &oldx, &oldy);
12828       if (!IS_MOVING(oldx, oldy))
12829       {
12830         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12831         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12832         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12833         printf("GameActions(): This should never happen!\n");
12834       }
12835     }
12836 #endif
12837   }
12838
12839 #if 0
12840   debug_print_timestamp(0, "- time for pre-main loop:");
12841 #endif
12842
12843 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12844   SCAN_PLAYFIELD(x, y)
12845   {
12846     element = Feld[x][y];
12847     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12848
12849 #if 1
12850     {
12851 #if 1
12852       int element2 = element;
12853       int graphic2 = graphic;
12854 #else
12855       int element2 = Feld[x][y];
12856       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12857 #endif
12858       int last_gfx_frame = GfxFrame[x][y];
12859
12860       if (graphic_info[graphic2].anim_global_sync)
12861         GfxFrame[x][y] = FrameCounter;
12862       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12863         GfxFrame[x][y] = CustomValue[x][y];
12864       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12865         GfxFrame[x][y] = element_info[element2].collect_score;
12866       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12867         GfxFrame[x][y] = ChangeDelay[x][y];
12868
12869       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12870         DrawLevelGraphicAnimation(x, y, graphic2);
12871     }
12872 #else
12873     ResetGfxFrame(x, y, TRUE);
12874 #endif
12875
12876 #if 1
12877     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12878         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12879       ResetRandomAnimationValue(x, y);
12880 #endif
12881
12882 #if 1
12883     SetRandomAnimationValue(x, y);
12884 #endif
12885
12886 #if 1
12887     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12888 #endif
12889   }
12890 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12891
12892 #if 0
12893   debug_print_timestamp(0, "- time for TEST loop:     -->");
12894 #endif
12895
12896   SCAN_PLAYFIELD(x, y)
12897   {
12898     element = Feld[x][y];
12899     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12900
12901     ResetGfxFrame(x, y, TRUE);
12902
12903     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12904         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12905       ResetRandomAnimationValue(x, y);
12906
12907     SetRandomAnimationValue(x, y);
12908
12909     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12910
12911     if (IS_INACTIVE(element))
12912     {
12913       if (IS_ANIMATED(graphic))
12914         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12915
12916       continue;
12917     }
12918
12919     /* this may take place after moving, so 'element' may have changed */
12920     if (IS_CHANGING(x, y) &&
12921         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12922     {
12923       int page = element_info[element].event_page_nr[CE_DELAY];
12924
12925 #if 1
12926       HandleElementChange(x, y, page);
12927 #else
12928       if (CAN_CHANGE(element))
12929         HandleElementChange(x, y, page);
12930
12931       if (HAS_ACTION(element))
12932         ExecuteCustomElementAction(x, y, element, page);
12933 #endif
12934
12935       element = Feld[x][y];
12936       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12937     }
12938
12939 #if 0   // ---------------------------------------------------------------------
12940
12941     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12942     {
12943       StartMoving(x, y);
12944
12945       element = Feld[x][y];
12946       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12947
12948       if (IS_ANIMATED(graphic) &&
12949           !IS_MOVING(x, y) &&
12950           !Stop[x][y])
12951         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12952
12953       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12954         TEST_DrawTwinkleOnField(x, y);
12955     }
12956     else if (IS_MOVING(x, y))
12957       ContinueMoving(x, y);
12958     else
12959     {
12960       switch (element)
12961       {
12962         case EL_ACID:
12963         case EL_EXIT_OPEN:
12964         case EL_EM_EXIT_OPEN:
12965         case EL_SP_EXIT_OPEN:
12966         case EL_STEEL_EXIT_OPEN:
12967         case EL_EM_STEEL_EXIT_OPEN:
12968         case EL_SP_TERMINAL:
12969         case EL_SP_TERMINAL_ACTIVE:
12970         case EL_EXTRA_TIME:
12971         case EL_SHIELD_NORMAL:
12972         case EL_SHIELD_DEADLY:
12973           if (IS_ANIMATED(graphic))
12974             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12975           break;
12976
12977         case EL_DYNAMITE_ACTIVE:
12978         case EL_EM_DYNAMITE_ACTIVE:
12979         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12980         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12981         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12982         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12983         case EL_SP_DISK_RED_ACTIVE:
12984           CheckDynamite(x, y);
12985           break;
12986
12987         case EL_AMOEBA_GROWING:
12988           AmoebeWaechst(x, y);
12989           break;
12990
12991         case EL_AMOEBA_SHRINKING:
12992           AmoebaDisappearing(x, y);
12993           break;
12994
12995 #if !USE_NEW_AMOEBA_CODE
12996         case EL_AMOEBA_WET:
12997         case EL_AMOEBA_DRY:
12998         case EL_AMOEBA_FULL:
12999         case EL_BD_AMOEBA:
13000         case EL_EMC_DRIPPER:
13001           AmoebeAbleger(x, y);
13002           break;
13003 #endif
13004
13005         case EL_GAME_OF_LIFE:
13006         case EL_BIOMAZE:
13007           Life(x, y);
13008           break;
13009
13010         case EL_EXIT_CLOSED:
13011           CheckExit(x, y);
13012           break;
13013
13014         case EL_EM_EXIT_CLOSED:
13015           CheckExitEM(x, y);
13016           break;
13017
13018         case EL_STEEL_EXIT_CLOSED:
13019           CheckExitSteel(x, y);
13020           break;
13021
13022         case EL_EM_STEEL_EXIT_CLOSED:
13023           CheckExitSteelEM(x, y);
13024           break;
13025
13026         case EL_SP_EXIT_CLOSED:
13027           CheckExitSP(x, y);
13028           break;
13029
13030         case EL_EXPANDABLE_WALL_GROWING:
13031         case EL_EXPANDABLE_STEELWALL_GROWING:
13032           MauerWaechst(x, y);
13033           break;
13034
13035         case EL_EXPANDABLE_WALL:
13036         case EL_EXPANDABLE_WALL_HORIZONTAL:
13037         case EL_EXPANDABLE_WALL_VERTICAL:
13038         case EL_EXPANDABLE_WALL_ANY:
13039         case EL_BD_EXPANDABLE_WALL:
13040           MauerAbleger(x, y);
13041           break;
13042
13043         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13044         case EL_EXPANDABLE_STEELWALL_VERTICAL:
13045         case EL_EXPANDABLE_STEELWALL_ANY:
13046           MauerAblegerStahl(x, y);
13047           break;
13048
13049         case EL_FLAMES:
13050           CheckForDragon(x, y);
13051           break;
13052
13053         case EL_EXPLOSION:
13054           break;
13055
13056         case EL_ELEMENT_SNAPPING:
13057         case EL_DIAGONAL_SHRINKING:
13058         case EL_DIAGONAL_GROWING:
13059         {
13060           graphic =
13061             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13062
13063           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13064           break;
13065         }
13066
13067         default:
13068           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13069             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13070           break;
13071       }
13072     }
13073
13074 #else   // ---------------------------------------------------------------------
13075
13076     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13077     {
13078       StartMoving(x, y);
13079
13080       element = Feld[x][y];
13081       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13082
13083       if (IS_ANIMATED(graphic) &&
13084           !IS_MOVING(x, y) &&
13085           !Stop[x][y])
13086         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13087
13088       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13089         TEST_DrawTwinkleOnField(x, y);
13090     }
13091     else if ((element == EL_ACID ||
13092               element == EL_EXIT_OPEN ||
13093               element == EL_EM_EXIT_OPEN ||
13094               element == EL_SP_EXIT_OPEN ||
13095               element == EL_STEEL_EXIT_OPEN ||
13096               element == EL_EM_STEEL_EXIT_OPEN ||
13097               element == EL_SP_TERMINAL ||
13098               element == EL_SP_TERMINAL_ACTIVE ||
13099               element == EL_EXTRA_TIME ||
13100               element == EL_SHIELD_NORMAL ||
13101               element == EL_SHIELD_DEADLY) &&
13102              IS_ANIMATED(graphic))
13103       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13104     else if (IS_MOVING(x, y))
13105       ContinueMoving(x, y);
13106     else if (IS_ACTIVE_BOMB(element))
13107       CheckDynamite(x, y);
13108     else if (element == EL_AMOEBA_GROWING)
13109       AmoebeWaechst(x, y);
13110     else if (element == EL_AMOEBA_SHRINKING)
13111       AmoebaDisappearing(x, y);
13112
13113 #if !USE_NEW_AMOEBA_CODE
13114     else if (IS_AMOEBALIVE(element))
13115       AmoebeAbleger(x, y);
13116 #endif
13117
13118     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13119       Life(x, y);
13120     else if (element == EL_EXIT_CLOSED)
13121       CheckExit(x, y);
13122     else if (element == EL_EM_EXIT_CLOSED)
13123       CheckExitEM(x, y);
13124     else if (element == EL_STEEL_EXIT_CLOSED)
13125       CheckExitSteel(x, y);
13126     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13127       CheckExitSteelEM(x, y);
13128     else if (element == EL_SP_EXIT_CLOSED)
13129       CheckExitSP(x, y);
13130     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13131              element == EL_EXPANDABLE_STEELWALL_GROWING)
13132       MauerWaechst(x, y);
13133     else if (element == EL_EXPANDABLE_WALL ||
13134              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13135              element == EL_EXPANDABLE_WALL_VERTICAL ||
13136              element == EL_EXPANDABLE_WALL_ANY ||
13137              element == EL_BD_EXPANDABLE_WALL)
13138       MauerAbleger(x, y);
13139     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13140              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13141              element == EL_EXPANDABLE_STEELWALL_ANY)
13142       MauerAblegerStahl(x, y);
13143     else if (element == EL_FLAMES)
13144       CheckForDragon(x, y);
13145     else if (element == EL_EXPLOSION)
13146       ; /* drawing of correct explosion animation is handled separately */
13147     else if (element == EL_ELEMENT_SNAPPING ||
13148              element == EL_DIAGONAL_SHRINKING ||
13149              element == EL_DIAGONAL_GROWING)
13150     {
13151       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13152
13153       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13154     }
13155     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13156       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13157
13158 #endif  // ---------------------------------------------------------------------
13159
13160     if (IS_BELT_ACTIVE(element))
13161       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13162
13163     if (game.magic_wall_active)
13164     {
13165       int jx = local_player->jx, jy = local_player->jy;
13166
13167       /* play the element sound at the position nearest to the player */
13168       if ((element == EL_MAGIC_WALL_FULL ||
13169            element == EL_MAGIC_WALL_ACTIVE ||
13170            element == EL_MAGIC_WALL_EMPTYING ||
13171            element == EL_BD_MAGIC_WALL_FULL ||
13172            element == EL_BD_MAGIC_WALL_ACTIVE ||
13173            element == EL_BD_MAGIC_WALL_EMPTYING ||
13174            element == EL_DC_MAGIC_WALL_FULL ||
13175            element == EL_DC_MAGIC_WALL_ACTIVE ||
13176            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13177           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13178       {
13179         magic_wall_x = x;
13180         magic_wall_y = y;
13181       }
13182     }
13183   }
13184
13185 #if 0
13186   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13187 #endif
13188
13189 #if USE_NEW_AMOEBA_CODE
13190   /* new experimental amoeba growth stuff */
13191   if (!(FrameCounter % 8))
13192   {
13193     static unsigned int random = 1684108901;
13194
13195     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13196     {
13197       x = RND(lev_fieldx);
13198       y = RND(lev_fieldy);
13199       element = Feld[x][y];
13200
13201       if (!IS_PLAYER(x,y) &&
13202           (element == EL_EMPTY ||
13203            CAN_GROW_INTO(element) ||
13204            element == EL_QUICKSAND_EMPTY ||
13205            element == EL_QUICKSAND_FAST_EMPTY ||
13206            element == EL_ACID_SPLASH_LEFT ||
13207            element == EL_ACID_SPLASH_RIGHT))
13208       {
13209         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13210             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13211             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13212             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13213           Feld[x][y] = EL_AMOEBA_DROP;
13214       }
13215
13216       random = random * 129 + 1;
13217     }
13218   }
13219 #endif
13220
13221 #if 0
13222   if (game.explosions_delayed)
13223 #endif
13224   {
13225     game.explosions_delayed = FALSE;
13226
13227     SCAN_PLAYFIELD(x, y)
13228     {
13229       element = Feld[x][y];
13230
13231       if (ExplodeField[x][y])
13232         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13233       else if (element == EL_EXPLOSION)
13234         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13235
13236       ExplodeField[x][y] = EX_TYPE_NONE;
13237     }
13238
13239     game.explosions_delayed = TRUE;
13240   }
13241
13242   if (game.magic_wall_active)
13243   {
13244     if (!(game.magic_wall_time_left % 4))
13245     {
13246       int element = Feld[magic_wall_x][magic_wall_y];
13247
13248       if (element == EL_BD_MAGIC_WALL_FULL ||
13249           element == EL_BD_MAGIC_WALL_ACTIVE ||
13250           element == EL_BD_MAGIC_WALL_EMPTYING)
13251         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13252       else if (element == EL_DC_MAGIC_WALL_FULL ||
13253                element == EL_DC_MAGIC_WALL_ACTIVE ||
13254                element == EL_DC_MAGIC_WALL_EMPTYING)
13255         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13256       else
13257         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13258     }
13259
13260     if (game.magic_wall_time_left > 0)
13261     {
13262       game.magic_wall_time_left--;
13263
13264       if (!game.magic_wall_time_left)
13265       {
13266         SCAN_PLAYFIELD(x, y)
13267         {
13268           element = Feld[x][y];
13269
13270           if (element == EL_MAGIC_WALL_ACTIVE ||
13271               element == EL_MAGIC_WALL_FULL)
13272           {
13273             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13274             TEST_DrawLevelField(x, y);
13275           }
13276           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13277                    element == EL_BD_MAGIC_WALL_FULL)
13278           {
13279             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13280             TEST_DrawLevelField(x, y);
13281           }
13282           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13283                    element == EL_DC_MAGIC_WALL_FULL)
13284           {
13285             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13286             TEST_DrawLevelField(x, y);
13287           }
13288         }
13289
13290         game.magic_wall_active = FALSE;
13291       }
13292     }
13293   }
13294
13295   if (game.light_time_left > 0)
13296   {
13297     game.light_time_left--;
13298
13299     if (game.light_time_left == 0)
13300       RedrawAllLightSwitchesAndInvisibleElements();
13301   }
13302
13303   if (game.timegate_time_left > 0)
13304   {
13305     game.timegate_time_left--;
13306
13307     if (game.timegate_time_left == 0)
13308       CloseAllOpenTimegates();
13309   }
13310
13311   if (game.lenses_time_left > 0)
13312   {
13313     game.lenses_time_left--;
13314
13315     if (game.lenses_time_left == 0)
13316       RedrawAllInvisibleElementsForLenses();
13317   }
13318
13319   if (game.magnify_time_left > 0)
13320   {
13321     game.magnify_time_left--;
13322
13323     if (game.magnify_time_left == 0)
13324       RedrawAllInvisibleElementsForMagnifier();
13325   }
13326
13327   for (i = 0; i < MAX_PLAYERS; i++)
13328   {
13329     struct PlayerInfo *player = &stored_player[i];
13330
13331     if (SHIELD_ON(player))
13332     {
13333       if (player->shield_deadly_time_left)
13334         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13335       else if (player->shield_normal_time_left)
13336         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13337     }
13338   }
13339
13340 #if USE_DELAYED_GFX_REDRAW
13341   SCAN_PLAYFIELD(x, y)
13342   {
13343 #if 1
13344     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13345 #else
13346     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13347         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13348 #endif
13349     {
13350       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13351          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13352
13353       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13354         DrawLevelField(x, y);
13355
13356       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13357         DrawLevelFieldCrumbled(x, y);
13358
13359       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13360         DrawLevelFieldCrumbledNeighbours(x, y);
13361
13362       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13363         DrawTwinkleOnField(x, y);
13364     }
13365
13366     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13367   }
13368 #endif
13369
13370   CheckLevelTime();
13371
13372   DrawAllPlayers();
13373   PlayAllPlayersSound();
13374
13375   if (options.debug)                    /* calculate frames per second */
13376   {
13377     static unsigned int fps_counter = 0;
13378     static int fps_frames = 0;
13379     unsigned int fps_delay_ms = Counter() - fps_counter;
13380
13381     fps_frames++;
13382
13383     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13384     {
13385       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13386
13387       fps_frames = 0;
13388       fps_counter = Counter();
13389     }
13390
13391     redraw_mask |= REDRAW_FPS;
13392   }
13393
13394   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13395
13396   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13397   {
13398     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13399
13400     local_player->show_envelope = 0;
13401   }
13402
13403 #if 0
13404   debug_print_timestamp(0, "stop main loop profiling ");
13405   printf("----------------------------------------------------------\n");
13406 #endif
13407
13408   /* use random number generator in every frame to make it less predictable */
13409   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13410     RND(1);
13411 }
13412
13413 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13414 {
13415   int min_x = x, min_y = y, max_x = x, max_y = y;
13416   int i;
13417
13418   for (i = 0; i < MAX_PLAYERS; i++)
13419   {
13420     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13421
13422     if (!stored_player[i].active || &stored_player[i] == player)
13423       continue;
13424
13425     min_x = MIN(min_x, jx);
13426     min_y = MIN(min_y, jy);
13427     max_x = MAX(max_x, jx);
13428     max_y = MAX(max_y, jy);
13429   }
13430
13431   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13432 }
13433
13434 static boolean AllPlayersInVisibleScreen()
13435 {
13436   int i;
13437
13438   for (i = 0; i < MAX_PLAYERS; i++)
13439   {
13440     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13441
13442     if (!stored_player[i].active)
13443       continue;
13444
13445     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13446       return FALSE;
13447   }
13448
13449   return TRUE;
13450 }
13451
13452 void ScrollLevel(int dx, int dy)
13453 {
13454 #if 0
13455   /* (directly solved in BlitBitmap() now) */
13456   static Bitmap *bitmap_db_field2 = NULL;
13457   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13458   int x, y;
13459 #else
13460   int x, y;
13461 #endif
13462
13463 #if 0
13464   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13465   /* only horizontal XOR vertical scroll direction allowed */
13466   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13467     return;
13468 #endif
13469
13470 #if 0
13471   /* (directly solved in BlitBitmap() now) */
13472   if (bitmap_db_field2 == NULL)
13473     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13474
13475   /* needed when blitting directly to same bitmap -- should not be needed with
13476      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13477   BlitBitmap(drawto_field, bitmap_db_field2,
13478              FX + TILEX * (dx == -1) - softscroll_offset,
13479              FY + TILEY * (dy == -1) - softscroll_offset,
13480              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13481              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13482              FX + TILEX * (dx == 1) - softscroll_offset,
13483              FY + TILEY * (dy == 1) - softscroll_offset);
13484   BlitBitmap(bitmap_db_field2, drawto_field,
13485              FX + TILEX * (dx == 1) - softscroll_offset,
13486              FY + TILEY * (dy == 1) - softscroll_offset,
13487              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13488              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13489              FX + TILEX * (dx == 1) - softscroll_offset,
13490              FY + TILEY * (dy == 1) - softscroll_offset);
13491
13492 #else
13493
13494 #if 0
13495   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13496   int xsize = (BX2 - BX1 + 1);
13497   int ysize = (BY2 - BY1 + 1);
13498   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13499   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13500   int step  = (start < end ? +1 : -1);
13501
13502   for (i = start; i != end; i += step)
13503   {
13504     BlitBitmap(drawto_field, drawto_field,
13505                FX + TILEX * (dx != 0 ? i + step : 0),
13506                FY + TILEY * (dy != 0 ? i + step : 0),
13507                TILEX * (dx != 0 ? 1 : xsize),
13508                TILEY * (dy != 0 ? 1 : ysize),
13509                FX + TILEX * (dx != 0 ? i : 0),
13510                FY + TILEY * (dy != 0 ? i : 0));
13511   }
13512
13513 #else
13514
13515 #if NEW_TILESIZE
13516 #if NEW_SCROLL
13517   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13518 #else
13519   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13520 #endif
13521 #else
13522 #if NEW_SCROLL
13523   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13524 #else
13525   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13526 #endif
13527 #endif
13528
13529 #if NEW_TILESIZE
13530   BlitBitmap(drawto_field, drawto_field,
13531              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13532              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13533              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13534              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13535              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13536              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13537 #else
13538   BlitBitmap(drawto_field, drawto_field,
13539              FX + TILEX * (dx == -1) - softscroll_offset,
13540              FY + TILEY * (dy == -1) - softscroll_offset,
13541              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13542              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13543              FX + TILEX * (dx == 1) - softscroll_offset,
13544              FY + TILEY * (dy == 1) - softscroll_offset);
13545 #endif
13546
13547 #endif
13548 #endif
13549
13550   if (dx != 0)
13551   {
13552     x = (dx == 1 ? BX1 : BX2);
13553     for (y = BY1; y <= BY2; y++)
13554       DrawScreenField(x, y);
13555   }
13556
13557   if (dy != 0)
13558   {
13559     y = (dy == 1 ? BY1 : BY2);
13560     for (x = BX1; x <= BX2; x++)
13561       DrawScreenField(x, y);
13562   }
13563
13564   redraw_mask |= REDRAW_FIELD;
13565 }
13566
13567 static boolean canFallDown(struct PlayerInfo *player)
13568 {
13569   int jx = player->jx, jy = player->jy;
13570
13571   return (IN_LEV_FIELD(jx, jy + 1) &&
13572           (IS_FREE(jx, jy + 1) ||
13573            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13574           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13575           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13576 }
13577
13578 static boolean canPassField(int x, int y, int move_dir)
13579 {
13580   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13581   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13582   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13583   int nextx = x + dx;
13584   int nexty = y + dy;
13585   int element = Feld[x][y];
13586
13587   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13588           !CAN_MOVE(element) &&
13589           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13590           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13591           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13592 }
13593
13594 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13595 {
13596   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13597   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13598   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13599   int newx = x + dx;
13600   int newy = y + dy;
13601
13602   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13603           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13604           (IS_DIGGABLE(Feld[newx][newy]) ||
13605            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13606            canPassField(newx, newy, move_dir)));
13607 }
13608
13609 static void CheckGravityMovement(struct PlayerInfo *player)
13610 {
13611 #if USE_PLAYER_GRAVITY
13612   if (player->gravity && !player->programmed_action)
13613 #else
13614   if (game.gravity && !player->programmed_action)
13615 #endif
13616   {
13617     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13618     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13619     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13620     int jx = player->jx, jy = player->jy;
13621     boolean player_is_moving_to_valid_field =
13622       (!player_is_snapping &&
13623        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13624         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13625     boolean player_can_fall_down = canFallDown(player);
13626
13627     if (player_can_fall_down &&
13628         !player_is_moving_to_valid_field)
13629       player->programmed_action = MV_DOWN;
13630   }
13631 }
13632
13633 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13634 {
13635   return CheckGravityMovement(player);
13636
13637 #if USE_PLAYER_GRAVITY
13638   if (player->gravity && !player->programmed_action)
13639 #else
13640   if (game.gravity && !player->programmed_action)
13641 #endif
13642   {
13643     int jx = player->jx, jy = player->jy;
13644     boolean field_under_player_is_free =
13645       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13646     boolean player_is_standing_on_valid_field =
13647       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13648        (IS_WALKABLE(Feld[jx][jy]) &&
13649         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13650
13651     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13652       player->programmed_action = MV_DOWN;
13653   }
13654 }
13655
13656 /*
13657   MovePlayerOneStep()
13658   -----------------------------------------------------------------------------
13659   dx, dy:               direction (non-diagonal) to try to move the player to
13660   real_dx, real_dy:     direction as read from input device (can be diagonal)
13661 */
13662
13663 boolean MovePlayerOneStep(struct PlayerInfo *player,
13664                           int dx, int dy, int real_dx, int real_dy)
13665 {
13666   int jx = player->jx, jy = player->jy;
13667   int new_jx = jx + dx, new_jy = jy + dy;
13668 #if !USE_FIXED_DONT_RUN_INTO
13669   int element;
13670 #endif
13671   int can_move;
13672   boolean player_can_move = !player->cannot_move;
13673
13674   if (!player->active || (!dx && !dy))
13675     return MP_NO_ACTION;
13676
13677   player->MovDir = (dx < 0 ? MV_LEFT :
13678                     dx > 0 ? MV_RIGHT :
13679                     dy < 0 ? MV_UP :
13680                     dy > 0 ? MV_DOWN :  MV_NONE);
13681
13682   if (!IN_LEV_FIELD(new_jx, new_jy))
13683     return MP_NO_ACTION;
13684
13685   if (!player_can_move)
13686   {
13687     if (player->MovPos == 0)
13688     {
13689       player->is_moving = FALSE;
13690       player->is_digging = FALSE;
13691       player->is_collecting = FALSE;
13692       player->is_snapping = FALSE;
13693       player->is_pushing = FALSE;
13694     }
13695   }
13696
13697 #if 1
13698   if (!options.network && game.centered_player_nr == -1 &&
13699       !AllPlayersInSight(player, new_jx, new_jy))
13700     return MP_NO_ACTION;
13701 #else
13702   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13703     return MP_NO_ACTION;
13704 #endif
13705
13706 #if !USE_FIXED_DONT_RUN_INTO
13707   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13708
13709   /* (moved to DigField()) */
13710   if (player_can_move && DONT_RUN_INTO(element))
13711   {
13712     if (element == EL_ACID && dx == 0 && dy == 1)
13713     {
13714       SplashAcid(new_jx, new_jy);
13715       Feld[jx][jy] = EL_PLAYER_1;
13716       InitMovingField(jx, jy, MV_DOWN);
13717       Store[jx][jy] = EL_ACID;
13718       ContinueMoving(jx, jy);
13719       BuryPlayer(player);
13720     }
13721     else
13722       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13723
13724     return MP_MOVING;
13725   }
13726 #endif
13727
13728   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13729   if (can_move != MP_MOVING)
13730     return can_move;
13731
13732   /* check if DigField() has caused relocation of the player */
13733   if (player->jx != jx || player->jy != jy)
13734     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13735
13736   StorePlayer[jx][jy] = 0;
13737   player->last_jx = jx;
13738   player->last_jy = jy;
13739   player->jx = new_jx;
13740   player->jy = new_jy;
13741   StorePlayer[new_jx][new_jy] = player->element_nr;
13742
13743   if (player->move_delay_value_next != -1)
13744   {
13745     player->move_delay_value = player->move_delay_value_next;
13746     player->move_delay_value_next = -1;
13747   }
13748
13749   player->MovPos =
13750     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13751
13752   player->step_counter++;
13753
13754   PlayerVisit[jx][jy] = FrameCounter;
13755
13756 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13757   player->is_moving = TRUE;
13758 #endif
13759
13760 #if 1
13761   /* should better be called in MovePlayer(), but this breaks some tapes */
13762   ScrollPlayer(player, SCROLL_INIT);
13763 #endif
13764
13765   return MP_MOVING;
13766 }
13767
13768 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13769 {
13770   int jx = player->jx, jy = player->jy;
13771   int old_jx = jx, old_jy = jy;
13772   int moved = MP_NO_ACTION;
13773
13774   if (!player->active)
13775     return FALSE;
13776
13777   if (!dx && !dy)
13778   {
13779     if (player->MovPos == 0)
13780     {
13781       player->is_moving = FALSE;
13782       player->is_digging = FALSE;
13783       player->is_collecting = FALSE;
13784       player->is_snapping = FALSE;
13785       player->is_pushing = FALSE;
13786     }
13787
13788     return FALSE;
13789   }
13790
13791   if (player->move_delay > 0)
13792     return FALSE;
13793
13794   player->move_delay = -1;              /* set to "uninitialized" value */
13795
13796   /* store if player is automatically moved to next field */
13797   player->is_auto_moving = (player->programmed_action != MV_NONE);
13798
13799   /* remove the last programmed player action */
13800   player->programmed_action = 0;
13801
13802   if (player->MovPos)
13803   {
13804     /* should only happen if pre-1.2 tape recordings are played */
13805     /* this is only for backward compatibility */
13806
13807     int original_move_delay_value = player->move_delay_value;
13808
13809 #if DEBUG
13810     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13811            tape.counter);
13812 #endif
13813
13814     /* scroll remaining steps with finest movement resolution */
13815     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13816
13817     while (player->MovPos)
13818     {
13819       ScrollPlayer(player, SCROLL_GO_ON);
13820       ScrollScreen(NULL, SCROLL_GO_ON);
13821
13822       AdvanceFrameAndPlayerCounters(player->index_nr);
13823
13824       DrawAllPlayers();
13825       BackToFront();
13826     }
13827
13828     player->move_delay_value = original_move_delay_value;
13829   }
13830
13831   player->is_active = FALSE;
13832
13833   if (player->last_move_dir & MV_HORIZONTAL)
13834   {
13835     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13836       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13837   }
13838   else
13839   {
13840     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13841       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13842   }
13843
13844 #if USE_FIXED_BORDER_RUNNING_GFX
13845   if (!moved && !player->is_active)
13846   {
13847     player->is_moving = FALSE;
13848     player->is_digging = FALSE;
13849     player->is_collecting = FALSE;
13850     player->is_snapping = FALSE;
13851     player->is_pushing = FALSE;
13852   }
13853 #endif
13854
13855   jx = player->jx;
13856   jy = player->jy;
13857
13858 #if 1
13859   if (moved & MP_MOVING && !ScreenMovPos &&
13860       (player->index_nr == game.centered_player_nr ||
13861        game.centered_player_nr == -1))
13862 #else
13863   if (moved & MP_MOVING && !ScreenMovPos &&
13864       (player == local_player || !options.network))
13865 #endif
13866   {
13867     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13868     int offset = game.scroll_delay_value;
13869
13870     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13871     {
13872       /* actual player has left the screen -- scroll in that direction */
13873       if (jx != old_jx)         /* player has moved horizontally */
13874         scroll_x += (jx - old_jx);
13875       else                      /* player has moved vertically */
13876         scroll_y += (jy - old_jy);
13877     }
13878     else
13879     {
13880       if (jx != old_jx)         /* player has moved horizontally */
13881       {
13882         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13883             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13884           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13885
13886         /* don't scroll over playfield boundaries */
13887         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13888           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13889
13890         /* don't scroll more than one field at a time */
13891         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13892
13893         /* don't scroll against the player's moving direction */
13894         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13895             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13896           scroll_x = old_scroll_x;
13897       }
13898       else                      /* player has moved vertically */
13899       {
13900         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13901             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13902           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13903
13904         /* don't scroll over playfield boundaries */
13905         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13906           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13907
13908         /* don't scroll more than one field at a time */
13909         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13910
13911         /* don't scroll against the player's moving direction */
13912         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13913             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13914           scroll_y = old_scroll_y;
13915       }
13916     }
13917
13918     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13919     {
13920 #if 1
13921       if (!options.network && game.centered_player_nr == -1 &&
13922           !AllPlayersInVisibleScreen())
13923       {
13924         scroll_x = old_scroll_x;
13925         scroll_y = old_scroll_y;
13926       }
13927       else
13928 #else
13929       if (!options.network && !AllPlayersInVisibleScreen())
13930       {
13931         scroll_x = old_scroll_x;
13932         scroll_y = old_scroll_y;
13933       }
13934       else
13935 #endif
13936       {
13937         ScrollScreen(player, SCROLL_INIT);
13938         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13939       }
13940     }
13941   }
13942
13943   player->StepFrame = 0;
13944
13945   if (moved & MP_MOVING)
13946   {
13947     if (old_jx != jx && old_jy == jy)
13948       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13949     else if (old_jx == jx && old_jy != jy)
13950       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13951
13952     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13953
13954     player->last_move_dir = player->MovDir;
13955     player->is_moving = TRUE;
13956     player->is_snapping = FALSE;
13957     player->is_switching = FALSE;
13958     player->is_dropping = FALSE;
13959     player->is_dropping_pressed = FALSE;
13960     player->drop_pressed_delay = 0;
13961
13962 #if 0
13963     /* should better be called here than above, but this breaks some tapes */
13964     ScrollPlayer(player, SCROLL_INIT);
13965 #endif
13966   }
13967   else
13968   {
13969     CheckGravityMovementWhenNotMoving(player);
13970
13971     player->is_moving = FALSE;
13972
13973     /* at this point, the player is allowed to move, but cannot move right now
13974        (e.g. because of something blocking the way) -- ensure that the player
13975        is also allowed to move in the next frame (in old versions before 3.1.1,
13976        the player was forced to wait again for eight frames before next try) */
13977
13978     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13979       player->move_delay = 0;   /* allow direct movement in the next frame */
13980   }
13981
13982   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13983     player->move_delay = player->move_delay_value;
13984
13985   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13986   {
13987     TestIfPlayerTouchesBadThing(jx, jy);
13988     TestIfPlayerTouchesCustomElement(jx, jy);
13989   }
13990
13991   if (!player->active)
13992     RemovePlayer(player);
13993
13994   return moved;
13995 }
13996
13997 void ScrollPlayer(struct PlayerInfo *player, int mode)
13998 {
13999   int jx = player->jx, jy = player->jy;
14000   int last_jx = player->last_jx, last_jy = player->last_jy;
14001   int move_stepsize = TILEX / player->move_delay_value;
14002
14003 #if USE_NEW_PLAYER_SPEED
14004   if (!player->active)
14005     return;
14006
14007   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
14008     return;
14009 #else
14010   if (!player->active || player->MovPos == 0)
14011     return;
14012 #endif
14013
14014   if (mode == SCROLL_INIT)
14015   {
14016     player->actual_frame_counter = FrameCounter;
14017     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14018
14019     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14020         Feld[last_jx][last_jy] == EL_EMPTY)
14021     {
14022       int last_field_block_delay = 0;   /* start with no blocking at all */
14023       int block_delay_adjustment = player->block_delay_adjustment;
14024
14025       /* if player blocks last field, add delay for exactly one move */
14026       if (player->block_last_field)
14027       {
14028         last_field_block_delay += player->move_delay_value;
14029
14030         /* when blocking enabled, prevent moving up despite gravity */
14031 #if USE_PLAYER_GRAVITY
14032         if (player->gravity && player->MovDir == MV_UP)
14033           block_delay_adjustment = -1;
14034 #else
14035         if (game.gravity && player->MovDir == MV_UP)
14036           block_delay_adjustment = -1;
14037 #endif
14038       }
14039
14040       /* add block delay adjustment (also possible when not blocking) */
14041       last_field_block_delay += block_delay_adjustment;
14042
14043       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14044       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14045     }
14046
14047 #if USE_NEW_PLAYER_SPEED
14048     if (player->MovPos != 0)    /* player has not yet reached destination */
14049       return;
14050 #else
14051     return;
14052 #endif
14053   }
14054   else if (!FrameReached(&player->actual_frame_counter, 1))
14055     return;
14056
14057 #if USE_NEW_PLAYER_SPEED
14058   if (player->MovPos != 0)
14059   {
14060     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14061     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14062
14063     /* before DrawPlayer() to draw correct player graphic for this case */
14064     if (player->MovPos == 0)
14065       CheckGravityMovement(player);
14066   }
14067 #else
14068   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14069   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14070
14071   /* before DrawPlayer() to draw correct player graphic for this case */
14072   if (player->MovPos == 0)
14073     CheckGravityMovement(player);
14074 #endif
14075
14076   if (player->MovPos == 0)      /* player reached destination field */
14077   {
14078     if (player->move_delay_reset_counter > 0)
14079     {
14080       player->move_delay_reset_counter--;
14081
14082       if (player->move_delay_reset_counter == 0)
14083       {
14084         /* continue with normal speed after quickly moving through gate */
14085         HALVE_PLAYER_SPEED(player);
14086
14087         /* be able to make the next move without delay */
14088         player->move_delay = 0;
14089       }
14090     }
14091
14092     player->last_jx = jx;
14093     player->last_jy = jy;
14094
14095     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14096         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14097 #if 1
14098         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14099 #endif
14100         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14101         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14102 #if 1
14103         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14104 #endif
14105         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14106         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14107     {
14108       DrawPlayer(player);       /* needed here only to cleanup last field */
14109       RemovePlayer(player);
14110
14111       if (local_player->friends_still_needed == 0 ||
14112           IS_SP_ELEMENT(Feld[jx][jy]))
14113         PlayerWins(player);
14114     }
14115
14116     /* this breaks one level: "machine", level 000 */
14117     {
14118       int move_direction = player->MovDir;
14119       int enter_side = MV_DIR_OPPOSITE(move_direction);
14120       int leave_side = move_direction;
14121       int old_jx = last_jx;
14122       int old_jy = last_jy;
14123       int old_element = Feld[old_jx][old_jy];
14124       int new_element = Feld[jx][jy];
14125
14126       if (IS_CUSTOM_ELEMENT(old_element))
14127         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14128                                    CE_LEFT_BY_PLAYER,
14129                                    player->index_bit, leave_side);
14130
14131       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14132                                           CE_PLAYER_LEAVES_X,
14133                                           player->index_bit, leave_side);
14134
14135       if (IS_CUSTOM_ELEMENT(new_element))
14136         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14137                                    player->index_bit, enter_side);
14138
14139       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14140                                           CE_PLAYER_ENTERS_X,
14141                                           player->index_bit, enter_side);
14142
14143 #if USE_FIX_CE_ACTION_WITH_PLAYER
14144       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14145                                         CE_MOVE_OF_X, move_direction);
14146 #else
14147       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14148                                         CE_MOVE_OF_X, move_direction);
14149 #endif
14150     }
14151
14152     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14153     {
14154       TestIfPlayerTouchesBadThing(jx, jy);
14155       TestIfPlayerTouchesCustomElement(jx, jy);
14156
14157       /* needed because pushed element has not yet reached its destination,
14158          so it would trigger a change event at its previous field location */
14159       if (!player->is_pushing)
14160         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14161
14162       if (!player->active)
14163         RemovePlayer(player);
14164     }
14165
14166     if (!local_player->LevelSolved && level.use_step_counter)
14167     {
14168       int i;
14169
14170       TimePlayed++;
14171
14172       if (TimeLeft > 0)
14173       {
14174         TimeLeft--;
14175
14176         if (TimeLeft <= 10 && setup.time_limit)
14177           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14178
14179 #if 1
14180         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14181
14182         DisplayGameControlValues();
14183 #else
14184         DrawGameValue_Time(TimeLeft);
14185 #endif
14186
14187         if (!TimeLeft && setup.time_limit)
14188           for (i = 0; i < MAX_PLAYERS; i++)
14189             KillPlayer(&stored_player[i]);
14190       }
14191 #if 1
14192       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14193       {
14194         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14195
14196         DisplayGameControlValues();
14197       }
14198 #else
14199       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14200         DrawGameValue_Time(TimePlayed);
14201 #endif
14202     }
14203
14204     if (tape.single_step && tape.recording && !tape.pausing &&
14205         !player->programmed_action)
14206       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14207   }
14208 }
14209
14210 void ScrollScreen(struct PlayerInfo *player, int mode)
14211 {
14212   static unsigned int screen_frame_counter = 0;
14213
14214   if (mode == SCROLL_INIT)
14215   {
14216     /* set scrolling step size according to actual player's moving speed */
14217     ScrollStepSize = TILEX / player->move_delay_value;
14218
14219     screen_frame_counter = FrameCounter;
14220     ScreenMovDir = player->MovDir;
14221     ScreenMovPos = player->MovPos;
14222     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14223     return;
14224   }
14225   else if (!FrameReached(&screen_frame_counter, 1))
14226     return;
14227
14228   if (ScreenMovPos)
14229   {
14230     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14231     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14232     redraw_mask |= REDRAW_FIELD;
14233   }
14234   else
14235     ScreenMovDir = MV_NONE;
14236 }
14237
14238 void TestIfPlayerTouchesCustomElement(int x, int y)
14239 {
14240   static int xy[4][2] =
14241   {
14242     { 0, -1 },
14243     { -1, 0 },
14244     { +1, 0 },
14245     { 0, +1 }
14246   };
14247   static int trigger_sides[4][2] =
14248   {
14249     /* center side       border side */
14250     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14251     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14252     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14253     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14254   };
14255   static int touch_dir[4] =
14256   {
14257     MV_LEFT | MV_RIGHT,
14258     MV_UP   | MV_DOWN,
14259     MV_UP   | MV_DOWN,
14260     MV_LEFT | MV_RIGHT
14261   };
14262   int center_element = Feld[x][y];      /* should always be non-moving! */
14263   int i;
14264
14265   for (i = 0; i < NUM_DIRECTIONS; i++)
14266   {
14267     int xx = x + xy[i][0];
14268     int yy = y + xy[i][1];
14269     int center_side = trigger_sides[i][0];
14270     int border_side = trigger_sides[i][1];
14271     int border_element;
14272
14273     if (!IN_LEV_FIELD(xx, yy))
14274       continue;
14275
14276     if (IS_PLAYER(x, y))                /* player found at center element */
14277     {
14278       struct PlayerInfo *player = PLAYERINFO(x, y);
14279
14280       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14281         border_element = Feld[xx][yy];          /* may be moving! */
14282       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14283         border_element = Feld[xx][yy];
14284       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14285         border_element = MovingOrBlocked2Element(xx, yy);
14286       else
14287         continue;               /* center and border element do not touch */
14288
14289       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14290                                  player->index_bit, border_side);
14291       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14292                                           CE_PLAYER_TOUCHES_X,
14293                                           player->index_bit, border_side);
14294
14295 #if USE_FIX_CE_ACTION_WITH_PLAYER
14296       {
14297         /* use player element that is initially defined in the level playfield,
14298            not the player element that corresponds to the runtime player number
14299            (example: a level that contains EL_PLAYER_3 as the only player would
14300            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14301         int player_element = PLAYERINFO(x, y)->initial_element;
14302
14303         CheckElementChangeBySide(xx, yy, border_element, player_element,
14304                                  CE_TOUCHING_X, border_side);
14305       }
14306 #endif
14307     }
14308     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14309     {
14310       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14311
14312       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14313       {
14314         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14315           continue;             /* center and border element do not touch */
14316       }
14317
14318       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14319                                  player->index_bit, center_side);
14320       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14321                                           CE_PLAYER_TOUCHES_X,
14322                                           player->index_bit, center_side);
14323
14324 #if USE_FIX_CE_ACTION_WITH_PLAYER
14325       {
14326         /* use player element that is initially defined in the level playfield,
14327            not the player element that corresponds to the runtime player number
14328            (example: a level that contains EL_PLAYER_3 as the only player would
14329            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14330         int player_element = PLAYERINFO(xx, yy)->initial_element;
14331
14332         CheckElementChangeBySide(x, y, center_element, player_element,
14333                                  CE_TOUCHING_X, center_side);
14334       }
14335 #endif
14336
14337       break;
14338     }
14339   }
14340 }
14341
14342 #if USE_ELEMENT_TOUCHING_BUGFIX
14343
14344 void TestIfElementTouchesCustomElement(int x, int y)
14345 {
14346   static int xy[4][2] =
14347   {
14348     { 0, -1 },
14349     { -1, 0 },
14350     { +1, 0 },
14351     { 0, +1 }
14352   };
14353   static int trigger_sides[4][2] =
14354   {
14355     /* center side      border side */
14356     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14357     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14358     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14359     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14360   };
14361   static int touch_dir[4] =
14362   {
14363     MV_LEFT | MV_RIGHT,
14364     MV_UP   | MV_DOWN,
14365     MV_UP   | MV_DOWN,
14366     MV_LEFT | MV_RIGHT
14367   };
14368   boolean change_center_element = FALSE;
14369   int center_element = Feld[x][y];      /* should always be non-moving! */
14370   int border_element_old[NUM_DIRECTIONS];
14371   int i;
14372
14373   for (i = 0; i < NUM_DIRECTIONS; i++)
14374   {
14375     int xx = x + xy[i][0];
14376     int yy = y + xy[i][1];
14377     int border_element;
14378
14379     border_element_old[i] = -1;
14380
14381     if (!IN_LEV_FIELD(xx, yy))
14382       continue;
14383
14384     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14385       border_element = Feld[xx][yy];    /* may be moving! */
14386     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14387       border_element = Feld[xx][yy];
14388     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14389       border_element = MovingOrBlocked2Element(xx, yy);
14390     else
14391       continue;                 /* center and border element do not touch */
14392
14393     border_element_old[i] = border_element;
14394   }
14395
14396   for (i = 0; i < NUM_DIRECTIONS; i++)
14397   {
14398     int xx = x + xy[i][0];
14399     int yy = y + xy[i][1];
14400     int center_side = trigger_sides[i][0];
14401     int border_element = border_element_old[i];
14402
14403     if (border_element == -1)
14404       continue;
14405
14406     /* check for change of border element */
14407     CheckElementChangeBySide(xx, yy, border_element, center_element,
14408                              CE_TOUCHING_X, center_side);
14409
14410     /* (center element cannot be player, so we dont have to check this here) */
14411   }
14412
14413   for (i = 0; i < NUM_DIRECTIONS; i++)
14414   {
14415     int xx = x + xy[i][0];
14416     int yy = y + xy[i][1];
14417     int border_side = trigger_sides[i][1];
14418     int border_element = border_element_old[i];
14419
14420     if (border_element == -1)
14421       continue;
14422
14423     /* check for change of center element (but change it only once) */
14424     if (!change_center_element)
14425       change_center_element =
14426         CheckElementChangeBySide(x, y, center_element, border_element,
14427                                  CE_TOUCHING_X, border_side);
14428
14429 #if USE_FIX_CE_ACTION_WITH_PLAYER
14430     if (IS_PLAYER(xx, yy))
14431     {
14432       /* use player element that is initially defined in the level playfield,
14433          not the player element that corresponds to the runtime player number
14434          (example: a level that contains EL_PLAYER_3 as the only player would
14435          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14436       int player_element = PLAYERINFO(xx, yy)->initial_element;
14437
14438       CheckElementChangeBySide(x, y, center_element, player_element,
14439                                CE_TOUCHING_X, border_side);
14440     }
14441 #endif
14442   }
14443 }
14444
14445 #else
14446
14447 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14448 {
14449   static int xy[4][2] =
14450   {
14451     { 0, -1 },
14452     { -1, 0 },
14453     { +1, 0 },
14454     { 0, +1 }
14455   };
14456   static int trigger_sides[4][2] =
14457   {
14458     /* center side      border side */
14459     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14460     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14461     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14462     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14463   };
14464   static int touch_dir[4] =
14465   {
14466     MV_LEFT | MV_RIGHT,
14467     MV_UP   | MV_DOWN,
14468     MV_UP   | MV_DOWN,
14469     MV_LEFT | MV_RIGHT
14470   };
14471   boolean change_center_element = FALSE;
14472   int center_element = Feld[x][y];      /* should always be non-moving! */
14473   int i;
14474
14475   for (i = 0; i < NUM_DIRECTIONS; i++)
14476   {
14477     int xx = x + xy[i][0];
14478     int yy = y + xy[i][1];
14479     int center_side = trigger_sides[i][0];
14480     int border_side = trigger_sides[i][1];
14481     int border_element;
14482
14483     if (!IN_LEV_FIELD(xx, yy))
14484       continue;
14485
14486     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14487       border_element = Feld[xx][yy];    /* may be moving! */
14488     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14489       border_element = Feld[xx][yy];
14490     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14491       border_element = MovingOrBlocked2Element(xx, yy);
14492     else
14493       continue;                 /* center and border element do not touch */
14494
14495     /* check for change of center element (but change it only once) */
14496     if (!change_center_element)
14497       change_center_element =
14498         CheckElementChangeBySide(x, y, center_element, border_element,
14499                                  CE_TOUCHING_X, border_side);
14500
14501     /* check for change of border element */
14502     CheckElementChangeBySide(xx, yy, border_element, center_element,
14503                              CE_TOUCHING_X, center_side);
14504   }
14505 }
14506
14507 #endif
14508
14509 void TestIfElementHitsCustomElement(int x, int y, int direction)
14510 {
14511   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14512   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14513   int hitx = x + dx, hity = y + dy;
14514   int hitting_element = Feld[x][y];
14515   int touched_element;
14516
14517   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14518     return;
14519
14520   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14521                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14522
14523   if (IN_LEV_FIELD(hitx, hity))
14524   {
14525     int opposite_direction = MV_DIR_OPPOSITE(direction);
14526     int hitting_side = direction;
14527     int touched_side = opposite_direction;
14528     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14529                           MovDir[hitx][hity] != direction ||
14530                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14531
14532     object_hit = TRUE;
14533
14534     if (object_hit)
14535     {
14536       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14537                                CE_HITTING_X, touched_side);
14538
14539       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14540                                CE_HIT_BY_X, hitting_side);
14541
14542       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14543                                CE_HIT_BY_SOMETHING, opposite_direction);
14544
14545 #if USE_FIX_CE_ACTION_WITH_PLAYER
14546       if (IS_PLAYER(hitx, hity))
14547       {
14548         /* use player element that is initially defined in the level playfield,
14549            not the player element that corresponds to the runtime player number
14550            (example: a level that contains EL_PLAYER_3 as the only player would
14551            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14552         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14553
14554         CheckElementChangeBySide(x, y, hitting_element, player_element,
14555                                  CE_HITTING_X, touched_side);
14556       }
14557 #endif
14558     }
14559   }
14560
14561   /* "hitting something" is also true when hitting the playfield border */
14562   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14563                            CE_HITTING_SOMETHING, direction);
14564 }
14565
14566 #if 0
14567 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14568 {
14569   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14570   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14571   int hitx = x + dx, hity = y + dy;
14572   int hitting_element = Feld[x][y];
14573   int touched_element;
14574 #if 0
14575   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14576                         !IS_FREE(hitx, hity) &&
14577                         (!IS_MOVING(hitx, hity) ||
14578                          MovDir[hitx][hity] != direction ||
14579                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14580 #endif
14581
14582   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14583     return;
14584
14585 #if 0
14586   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14587     return;
14588 #endif
14589
14590   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14591                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14592
14593   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14594                            EP_CAN_SMASH_EVERYTHING, direction);
14595
14596   if (IN_LEV_FIELD(hitx, hity))
14597   {
14598     int opposite_direction = MV_DIR_OPPOSITE(direction);
14599     int hitting_side = direction;
14600     int touched_side = opposite_direction;
14601 #if 0
14602     int touched_element = MovingOrBlocked2Element(hitx, hity);
14603 #endif
14604 #if 1
14605     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14606                           MovDir[hitx][hity] != direction ||
14607                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14608
14609     object_hit = TRUE;
14610 #endif
14611
14612     if (object_hit)
14613     {
14614       int i;
14615
14616       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14617                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14618
14619       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14620                                CE_OTHER_IS_SMASHING, touched_side);
14621
14622       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14623                                CE_OTHER_GETS_SMASHED, hitting_side);
14624     }
14625   }
14626 }
14627 #endif
14628
14629 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14630 {
14631   int i, kill_x = -1, kill_y = -1;
14632
14633   int bad_element = -1;
14634   static int test_xy[4][2] =
14635   {
14636     { 0, -1 },
14637     { -1, 0 },
14638     { +1, 0 },
14639     { 0, +1 }
14640   };
14641   static int test_dir[4] =
14642   {
14643     MV_UP,
14644     MV_LEFT,
14645     MV_RIGHT,
14646     MV_DOWN
14647   };
14648
14649   for (i = 0; i < NUM_DIRECTIONS; i++)
14650   {
14651     int test_x, test_y, test_move_dir, test_element;
14652
14653     test_x = good_x + test_xy[i][0];
14654     test_y = good_y + test_xy[i][1];
14655
14656     if (!IN_LEV_FIELD(test_x, test_y))
14657       continue;
14658
14659     test_move_dir =
14660       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14661
14662     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14663
14664     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14665        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14666     */
14667     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14668         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14669     {
14670       kill_x = test_x;
14671       kill_y = test_y;
14672       bad_element = test_element;
14673
14674       break;
14675     }
14676   }
14677
14678   if (kill_x != -1 || kill_y != -1)
14679   {
14680     if (IS_PLAYER(good_x, good_y))
14681     {
14682       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14683
14684       if (player->shield_deadly_time_left > 0 &&
14685           !IS_INDESTRUCTIBLE(bad_element))
14686         Bang(kill_x, kill_y);
14687       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14688         KillPlayer(player);
14689     }
14690     else
14691       Bang(good_x, good_y);
14692   }
14693 }
14694
14695 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14696 {
14697   int i, kill_x = -1, kill_y = -1;
14698   int bad_element = Feld[bad_x][bad_y];
14699   static int test_xy[4][2] =
14700   {
14701     { 0, -1 },
14702     { -1, 0 },
14703     { +1, 0 },
14704     { 0, +1 }
14705   };
14706   static int touch_dir[4] =
14707   {
14708     MV_LEFT | MV_RIGHT,
14709     MV_UP   | MV_DOWN,
14710     MV_UP   | MV_DOWN,
14711     MV_LEFT | MV_RIGHT
14712   };
14713   static int test_dir[4] =
14714   {
14715     MV_UP,
14716     MV_LEFT,
14717     MV_RIGHT,
14718     MV_DOWN
14719   };
14720
14721   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14722     return;
14723
14724   for (i = 0; i < NUM_DIRECTIONS; i++)
14725   {
14726     int test_x, test_y, test_move_dir, test_element;
14727
14728     test_x = bad_x + test_xy[i][0];
14729     test_y = bad_y + test_xy[i][1];
14730
14731     if (!IN_LEV_FIELD(test_x, test_y))
14732       continue;
14733
14734     test_move_dir =
14735       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14736
14737     test_element = Feld[test_x][test_y];
14738
14739     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14740        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14741     */
14742     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14743         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14744     {
14745       /* good thing is player or penguin that does not move away */
14746       if (IS_PLAYER(test_x, test_y))
14747       {
14748         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14749
14750         if (bad_element == EL_ROBOT && player->is_moving)
14751           continue;     /* robot does not kill player if he is moving */
14752
14753         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14754         {
14755           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14756             continue;           /* center and border element do not touch */
14757         }
14758
14759         kill_x = test_x;
14760         kill_y = test_y;
14761
14762         break;
14763       }
14764       else if (test_element == EL_PENGUIN)
14765       {
14766         kill_x = test_x;
14767         kill_y = test_y;
14768
14769         break;
14770       }
14771     }
14772   }
14773
14774   if (kill_x != -1 || kill_y != -1)
14775   {
14776     if (IS_PLAYER(kill_x, kill_y))
14777     {
14778       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14779
14780       if (player->shield_deadly_time_left > 0 &&
14781           !IS_INDESTRUCTIBLE(bad_element))
14782         Bang(bad_x, bad_y);
14783       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14784         KillPlayer(player);
14785     }
14786     else
14787       Bang(kill_x, kill_y);
14788   }
14789 }
14790
14791 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14792 {
14793   int bad_element = Feld[bad_x][bad_y];
14794   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14795   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14796   int test_x = bad_x + dx, test_y = bad_y + dy;
14797   int test_move_dir, test_element;
14798   int kill_x = -1, kill_y = -1;
14799
14800   if (!IN_LEV_FIELD(test_x, test_y))
14801     return;
14802
14803   test_move_dir =
14804     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14805
14806   test_element = Feld[test_x][test_y];
14807
14808   if (test_move_dir != bad_move_dir)
14809   {
14810     /* good thing can be player or penguin that does not move away */
14811     if (IS_PLAYER(test_x, test_y))
14812     {
14813       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14814
14815       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14816          player as being hit when he is moving towards the bad thing, because
14817          the "get hit by" condition would be lost after the player stops) */
14818       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14819         return;         /* player moves away from bad thing */
14820
14821       kill_x = test_x;
14822       kill_y = test_y;
14823     }
14824     else if (test_element == EL_PENGUIN)
14825     {
14826       kill_x = test_x;
14827       kill_y = test_y;
14828     }
14829   }
14830
14831   if (kill_x != -1 || kill_y != -1)
14832   {
14833     if (IS_PLAYER(kill_x, kill_y))
14834     {
14835       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14836
14837       if (player->shield_deadly_time_left > 0 &&
14838           !IS_INDESTRUCTIBLE(bad_element))
14839         Bang(bad_x, bad_y);
14840       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14841         KillPlayer(player);
14842     }
14843     else
14844       Bang(kill_x, kill_y);
14845   }
14846 }
14847
14848 void TestIfPlayerTouchesBadThing(int x, int y)
14849 {
14850   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14851 }
14852
14853 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14854 {
14855   TestIfGoodThingHitsBadThing(x, y, move_dir);
14856 }
14857
14858 void TestIfBadThingTouchesPlayer(int x, int y)
14859 {
14860   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14861 }
14862
14863 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14864 {
14865   TestIfBadThingHitsGoodThing(x, y, move_dir);
14866 }
14867
14868 void TestIfFriendTouchesBadThing(int x, int y)
14869 {
14870   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14871 }
14872
14873 void TestIfBadThingTouchesFriend(int x, int y)
14874 {
14875   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14876 }
14877
14878 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14879 {
14880   int i, kill_x = bad_x, kill_y = bad_y;
14881   static int xy[4][2] =
14882   {
14883     { 0, -1 },
14884     { -1, 0 },
14885     { +1, 0 },
14886     { 0, +1 }
14887   };
14888
14889   for (i = 0; i < NUM_DIRECTIONS; i++)
14890   {
14891     int x, y, element;
14892
14893     x = bad_x + xy[i][0];
14894     y = bad_y + xy[i][1];
14895     if (!IN_LEV_FIELD(x, y))
14896       continue;
14897
14898     element = Feld[x][y];
14899     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14900         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14901     {
14902       kill_x = x;
14903       kill_y = y;
14904       break;
14905     }
14906   }
14907
14908   if (kill_x != bad_x || kill_y != bad_y)
14909     Bang(bad_x, bad_y);
14910 }
14911
14912 void KillPlayer(struct PlayerInfo *player)
14913 {
14914   int jx = player->jx, jy = player->jy;
14915
14916   if (!player->active)
14917     return;
14918
14919 #if 0
14920   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14921          player->killed, player->active, player->reanimated);
14922 #endif
14923
14924   /* the following code was introduced to prevent an infinite loop when calling
14925      -> Bang()
14926      -> CheckTriggeredElementChangeExt()
14927      -> ExecuteCustomElementAction()
14928      -> KillPlayer()
14929      -> (infinitely repeating the above sequence of function calls)
14930      which occurs when killing the player while having a CE with the setting
14931      "kill player X when explosion of <player X>"; the solution using a new
14932      field "player->killed" was chosen for backwards compatibility, although
14933      clever use of the fields "player->active" etc. would probably also work */
14934 #if 1
14935   if (player->killed)
14936     return;
14937 #endif
14938
14939   player->killed = TRUE;
14940
14941   /* remove accessible field at the player's position */
14942   Feld[jx][jy] = EL_EMPTY;
14943
14944   /* deactivate shield (else Bang()/Explode() would not work right) */
14945   player->shield_normal_time_left = 0;
14946   player->shield_deadly_time_left = 0;
14947
14948 #if 0
14949   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14950          player->killed, player->active, player->reanimated);
14951 #endif
14952
14953   Bang(jx, jy);
14954
14955 #if 0
14956   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14957          player->killed, player->active, player->reanimated);
14958 #endif
14959
14960 #if USE_PLAYER_REANIMATION
14961 #if 1
14962   if (player->reanimated)       /* killed player may have been reanimated */
14963     player->killed = player->reanimated = FALSE;
14964   else
14965     BuryPlayer(player);
14966 #else
14967   if (player->killed)           /* player may have been reanimated */
14968     BuryPlayer(player);
14969 #endif
14970 #else
14971   BuryPlayer(player);
14972 #endif
14973 }
14974
14975 static void KillPlayerUnlessEnemyProtected(int x, int y)
14976 {
14977   if (!PLAYER_ENEMY_PROTECTED(x, y))
14978     KillPlayer(PLAYERINFO(x, y));
14979 }
14980
14981 static void KillPlayerUnlessExplosionProtected(int x, int y)
14982 {
14983   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14984     KillPlayer(PLAYERINFO(x, y));
14985 }
14986
14987 void BuryPlayer(struct PlayerInfo *player)
14988 {
14989   int jx = player->jx, jy = player->jy;
14990
14991   if (!player->active)
14992     return;
14993
14994   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14995   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14996
14997   player->GameOver = TRUE;
14998   RemovePlayer(player);
14999 }
15000
15001 void RemovePlayer(struct PlayerInfo *player)
15002 {
15003   int jx = player->jx, jy = player->jy;
15004   int i, found = FALSE;
15005
15006   player->present = FALSE;
15007   player->active = FALSE;
15008
15009   if (!ExplodeField[jx][jy])
15010     StorePlayer[jx][jy] = 0;
15011
15012   if (player->is_moving)
15013     TEST_DrawLevelField(player->last_jx, player->last_jy);
15014
15015   for (i = 0; i < MAX_PLAYERS; i++)
15016     if (stored_player[i].active)
15017       found = TRUE;
15018
15019   if (!found)
15020     AllPlayersGone = TRUE;
15021
15022   ExitX = ZX = jx;
15023   ExitY = ZY = jy;
15024 }
15025
15026 #if USE_NEW_SNAP_DELAY
15027 static void setFieldForSnapping(int x, int y, int element, int direction)
15028 {
15029   struct ElementInfo *ei = &element_info[element];
15030   int direction_bit = MV_DIR_TO_BIT(direction);
15031   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15032   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15033                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15034
15035   Feld[x][y] = EL_ELEMENT_SNAPPING;
15036   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15037
15038   ResetGfxAnimation(x, y);
15039
15040   GfxElement[x][y] = element;
15041   GfxAction[x][y] = action;
15042   GfxDir[x][y] = direction;
15043   GfxFrame[x][y] = -1;
15044 }
15045 #endif
15046
15047 /*
15048   =============================================================================
15049   checkDiagonalPushing()
15050   -----------------------------------------------------------------------------
15051   check if diagonal input device direction results in pushing of object
15052   (by checking if the alternative direction is walkable, diggable, ...)
15053   =============================================================================
15054 */
15055
15056 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15057                                     int x, int y, int real_dx, int real_dy)
15058 {
15059   int jx, jy, dx, dy, xx, yy;
15060
15061   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
15062     return TRUE;
15063
15064   /* diagonal direction: check alternative direction */
15065   jx = player->jx;
15066   jy = player->jy;
15067   dx = x - jx;
15068   dy = y - jy;
15069   xx = jx + (dx == 0 ? real_dx : 0);
15070   yy = jy + (dy == 0 ? real_dy : 0);
15071
15072   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15073 }
15074
15075 /*
15076   =============================================================================
15077   DigField()
15078   -----------------------------------------------------------------------------
15079   x, y:                 field next to player (non-diagonal) to try to dig to
15080   real_dx, real_dy:     direction as read from input device (can be diagonal)
15081   =============================================================================
15082 */
15083
15084 static int DigField(struct PlayerInfo *player,
15085                     int oldx, int oldy, int x, int y,
15086                     int real_dx, int real_dy, int mode)
15087 {
15088   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15089   boolean player_was_pushing = player->is_pushing;
15090   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15091   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15092   int jx = oldx, jy = oldy;
15093   int dx = x - jx, dy = y - jy;
15094   int nextx = x + dx, nexty = y + dy;
15095   int move_direction = (dx == -1 ? MV_LEFT  :
15096                         dx == +1 ? MV_RIGHT :
15097                         dy == -1 ? MV_UP    :
15098                         dy == +1 ? MV_DOWN  : MV_NONE);
15099   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15100   int dig_side = MV_DIR_OPPOSITE(move_direction);
15101   int old_element = Feld[jx][jy];
15102 #if USE_FIXED_DONT_RUN_INTO
15103   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15104 #else
15105   int element;
15106 #endif
15107   int collect_count;
15108
15109   if (is_player)                /* function can also be called by EL_PENGUIN */
15110   {
15111     if (player->MovPos == 0)
15112     {
15113       player->is_digging = FALSE;
15114       player->is_collecting = FALSE;
15115     }
15116
15117     if (player->MovPos == 0)    /* last pushing move finished */
15118       player->is_pushing = FALSE;
15119
15120     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15121     {
15122       player->is_switching = FALSE;
15123       player->push_delay = -1;
15124
15125       return MP_NO_ACTION;
15126     }
15127   }
15128
15129 #if !USE_FIXED_DONT_RUN_INTO
15130   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15131     return MP_NO_ACTION;
15132 #endif
15133
15134   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15135     old_element = Back[jx][jy];
15136
15137   /* in case of element dropped at player position, check background */
15138   else if (Back[jx][jy] != EL_EMPTY &&
15139            game.engine_version >= VERSION_IDENT(2,2,0,0))
15140     old_element = Back[jx][jy];
15141
15142   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15143     return MP_NO_ACTION;        /* field has no opening in this direction */
15144
15145   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15146     return MP_NO_ACTION;        /* field has no opening in this direction */
15147
15148 #if USE_FIXED_DONT_RUN_INTO
15149   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15150   {
15151     SplashAcid(x, y);
15152
15153     Feld[jx][jy] = player->artwork_element;
15154     InitMovingField(jx, jy, MV_DOWN);
15155     Store[jx][jy] = EL_ACID;
15156     ContinueMoving(jx, jy);
15157     BuryPlayer(player);
15158
15159     return MP_DONT_RUN_INTO;
15160   }
15161 #endif
15162
15163 #if USE_FIXED_DONT_RUN_INTO
15164   if (player_can_move && DONT_RUN_INTO(element))
15165   {
15166     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15167
15168     return MP_DONT_RUN_INTO;
15169   }
15170 #endif
15171
15172 #if USE_FIXED_DONT_RUN_INTO
15173   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15174     return MP_NO_ACTION;
15175 #endif
15176
15177 #if !USE_FIXED_DONT_RUN_INTO
15178   element = Feld[x][y];
15179 #endif
15180
15181   collect_count = element_info[element].collect_count_initial;
15182
15183   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15184     return MP_NO_ACTION;
15185
15186   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15187     player_can_move = player_can_move_or_snap;
15188
15189   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15190       game.engine_version >= VERSION_IDENT(2,2,0,0))
15191   {
15192     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15193                                player->index_bit, dig_side);
15194     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15195                                         player->index_bit, dig_side);
15196
15197     if (element == EL_DC_LANDMINE)
15198       Bang(x, y);
15199
15200     if (Feld[x][y] != element)          /* field changed by snapping */
15201       return MP_ACTION;
15202
15203     return MP_NO_ACTION;
15204   }
15205
15206 #if USE_PLAYER_GRAVITY
15207   if (player->gravity && is_player && !player->is_auto_moving &&
15208       canFallDown(player) && move_direction != MV_DOWN &&
15209       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15210     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15211 #else
15212   if (game.gravity && is_player && !player->is_auto_moving &&
15213       canFallDown(player) && move_direction != MV_DOWN &&
15214       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15215     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15216 #endif
15217
15218   if (player_can_move &&
15219       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15220   {
15221     int sound_element = SND_ELEMENT(element);
15222     int sound_action = ACTION_WALKING;
15223
15224     if (IS_RND_GATE(element))
15225     {
15226       if (!player->key[RND_GATE_NR(element)])
15227         return MP_NO_ACTION;
15228     }
15229     else if (IS_RND_GATE_GRAY(element))
15230     {
15231       if (!player->key[RND_GATE_GRAY_NR(element)])
15232         return MP_NO_ACTION;
15233     }
15234     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15235     {
15236       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15237         return MP_NO_ACTION;
15238     }
15239     else if (element == EL_EXIT_OPEN ||
15240              element == EL_EM_EXIT_OPEN ||
15241 #if 1
15242              element == EL_EM_EXIT_OPENING ||
15243 #endif
15244              element == EL_STEEL_EXIT_OPEN ||
15245              element == EL_EM_STEEL_EXIT_OPEN ||
15246 #if 1
15247              element == EL_EM_STEEL_EXIT_OPENING ||
15248 #endif
15249              element == EL_SP_EXIT_OPEN ||
15250              element == EL_SP_EXIT_OPENING)
15251     {
15252       sound_action = ACTION_PASSING;    /* player is passing exit */
15253     }
15254     else if (element == EL_EMPTY)
15255     {
15256       sound_action = ACTION_MOVING;             /* nothing to walk on */
15257     }
15258
15259     /* play sound from background or player, whatever is available */
15260     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15261       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15262     else
15263       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15264   }
15265   else if (player_can_move &&
15266            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15267   {
15268     if (!ACCESS_FROM(element, opposite_direction))
15269       return MP_NO_ACTION;      /* field not accessible from this direction */
15270
15271     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15272       return MP_NO_ACTION;
15273
15274     if (IS_EM_GATE(element))
15275     {
15276       if (!player->key[EM_GATE_NR(element)])
15277         return MP_NO_ACTION;
15278     }
15279     else if (IS_EM_GATE_GRAY(element))
15280     {
15281       if (!player->key[EM_GATE_GRAY_NR(element)])
15282         return MP_NO_ACTION;
15283     }
15284     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15285     {
15286       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15287         return MP_NO_ACTION;
15288     }
15289     else if (IS_EMC_GATE(element))
15290     {
15291       if (!player->key[EMC_GATE_NR(element)])
15292         return MP_NO_ACTION;
15293     }
15294     else if (IS_EMC_GATE_GRAY(element))
15295     {
15296       if (!player->key[EMC_GATE_GRAY_NR(element)])
15297         return MP_NO_ACTION;
15298     }
15299     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15300     {
15301       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15302         return MP_NO_ACTION;
15303     }
15304     else if (element == EL_DC_GATE_WHITE ||
15305              element == EL_DC_GATE_WHITE_GRAY ||
15306              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15307     {
15308       if (player->num_white_keys == 0)
15309         return MP_NO_ACTION;
15310
15311       player->num_white_keys--;
15312     }
15313     else if (IS_SP_PORT(element))
15314     {
15315       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15316           element == EL_SP_GRAVITY_PORT_RIGHT ||
15317           element == EL_SP_GRAVITY_PORT_UP ||
15318           element == EL_SP_GRAVITY_PORT_DOWN)
15319 #if USE_PLAYER_GRAVITY
15320         player->gravity = !player->gravity;
15321 #else
15322         game.gravity = !game.gravity;
15323 #endif
15324       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15325                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15326                element == EL_SP_GRAVITY_ON_PORT_UP ||
15327                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15328 #if USE_PLAYER_GRAVITY
15329         player->gravity = TRUE;
15330 #else
15331         game.gravity = TRUE;
15332 #endif
15333       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15334                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15335                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15336                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15337 #if USE_PLAYER_GRAVITY
15338         player->gravity = FALSE;
15339 #else
15340         game.gravity = FALSE;
15341 #endif
15342     }
15343
15344     /* automatically move to the next field with double speed */
15345     player->programmed_action = move_direction;
15346
15347     if (player->move_delay_reset_counter == 0)
15348     {
15349       player->move_delay_reset_counter = 2;     /* two double speed steps */
15350
15351       DOUBLE_PLAYER_SPEED(player);
15352     }
15353
15354     PlayLevelSoundAction(x, y, ACTION_PASSING);
15355   }
15356   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15357   {
15358     RemoveField(x, y);
15359
15360     if (mode != DF_SNAP)
15361     {
15362       GfxElement[x][y] = GFX_ELEMENT(element);
15363       player->is_digging = TRUE;
15364     }
15365
15366     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15367
15368     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15369                                         player->index_bit, dig_side);
15370
15371     if (mode == DF_SNAP)
15372     {
15373 #if USE_NEW_SNAP_DELAY
15374       if (level.block_snap_field)
15375         setFieldForSnapping(x, y, element, move_direction);
15376       else
15377         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15378 #else
15379       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15380 #endif
15381
15382       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15383                                           player->index_bit, dig_side);
15384     }
15385   }
15386   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15387   {
15388     RemoveField(x, y);
15389
15390     if (is_player && mode != DF_SNAP)
15391     {
15392       GfxElement[x][y] = element;
15393       player->is_collecting = TRUE;
15394     }
15395
15396     if (element == EL_SPEED_PILL)
15397     {
15398       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15399     }
15400     else if (element == EL_EXTRA_TIME && level.time > 0)
15401     {
15402       TimeLeft += level.extra_time;
15403
15404 #if 1
15405       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15406
15407       DisplayGameControlValues();
15408 #else
15409       DrawGameValue_Time(TimeLeft);
15410 #endif
15411     }
15412     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15413     {
15414       player->shield_normal_time_left += level.shield_normal_time;
15415       if (element == EL_SHIELD_DEADLY)
15416         player->shield_deadly_time_left += level.shield_deadly_time;
15417     }
15418     else if (element == EL_DYNAMITE ||
15419              element == EL_EM_DYNAMITE ||
15420              element == EL_SP_DISK_RED)
15421     {
15422       if (player->inventory_size < MAX_INVENTORY_SIZE)
15423         player->inventory_element[player->inventory_size++] = element;
15424
15425       DrawGameDoorValues();
15426     }
15427     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15428     {
15429       player->dynabomb_count++;
15430       player->dynabombs_left++;
15431     }
15432     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15433     {
15434       player->dynabomb_size++;
15435     }
15436     else if (element == EL_DYNABOMB_INCREASE_POWER)
15437     {
15438       player->dynabomb_xl = TRUE;
15439     }
15440     else if (IS_KEY(element))
15441     {
15442       player->key[KEY_NR(element)] = TRUE;
15443
15444       DrawGameDoorValues();
15445     }
15446     else if (element == EL_DC_KEY_WHITE)
15447     {
15448       player->num_white_keys++;
15449
15450       /* display white keys? */
15451       /* DrawGameDoorValues(); */
15452     }
15453     else if (IS_ENVELOPE(element))
15454     {
15455       player->show_envelope = element;
15456     }
15457     else if (element == EL_EMC_LENSES)
15458     {
15459       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15460
15461       RedrawAllInvisibleElementsForLenses();
15462     }
15463     else if (element == EL_EMC_MAGNIFIER)
15464     {
15465       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15466
15467       RedrawAllInvisibleElementsForMagnifier();
15468     }
15469     else if (IS_DROPPABLE(element) ||
15470              IS_THROWABLE(element))     /* can be collected and dropped */
15471     {
15472       int i;
15473
15474       if (collect_count == 0)
15475         player->inventory_infinite_element = element;
15476       else
15477         for (i = 0; i < collect_count; i++)
15478           if (player->inventory_size < MAX_INVENTORY_SIZE)
15479             player->inventory_element[player->inventory_size++] = element;
15480
15481       DrawGameDoorValues();
15482     }
15483     else if (collect_count > 0)
15484     {
15485       local_player->gems_still_needed -= collect_count;
15486       if (local_player->gems_still_needed < 0)
15487         local_player->gems_still_needed = 0;
15488
15489 #if 1
15490       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15491
15492       DisplayGameControlValues();
15493 #else
15494       DrawGameValue_Emeralds(local_player->gems_still_needed);
15495 #endif
15496     }
15497
15498     RaiseScoreElement(element);
15499     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15500
15501     if (is_player)
15502       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15503                                           player->index_bit, dig_side);
15504
15505     if (mode == DF_SNAP)
15506     {
15507 #if USE_NEW_SNAP_DELAY
15508       if (level.block_snap_field)
15509         setFieldForSnapping(x, y, element, move_direction);
15510       else
15511         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15512 #else
15513       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15514 #endif
15515
15516       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15517                                           player->index_bit, dig_side);
15518     }
15519   }
15520   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15521   {
15522     if (mode == DF_SNAP && element != EL_BD_ROCK)
15523       return MP_NO_ACTION;
15524
15525     if (CAN_FALL(element) && dy)
15526       return MP_NO_ACTION;
15527
15528     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15529         !(element == EL_SPRING && level.use_spring_bug))
15530       return MP_NO_ACTION;
15531
15532     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15533         ((move_direction & MV_VERTICAL &&
15534           ((element_info[element].move_pattern & MV_LEFT &&
15535             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15536            (element_info[element].move_pattern & MV_RIGHT &&
15537             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15538          (move_direction & MV_HORIZONTAL &&
15539           ((element_info[element].move_pattern & MV_UP &&
15540             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15541            (element_info[element].move_pattern & MV_DOWN &&
15542             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15543       return MP_NO_ACTION;
15544
15545     /* do not push elements already moving away faster than player */
15546     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15547         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15548       return MP_NO_ACTION;
15549
15550     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15551     {
15552       if (player->push_delay_value == -1 || !player_was_pushing)
15553         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15554     }
15555     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15556     {
15557       if (player->push_delay_value == -1)
15558         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15559     }
15560     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15561     {
15562       if (!player->is_pushing)
15563         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15564     }
15565
15566     player->is_pushing = TRUE;
15567     player->is_active = TRUE;
15568
15569     if (!(IN_LEV_FIELD(nextx, nexty) &&
15570           (IS_FREE(nextx, nexty) ||
15571            (IS_SB_ELEMENT(element) &&
15572             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15573            (IS_CUSTOM_ELEMENT(element) &&
15574             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15575       return MP_NO_ACTION;
15576
15577     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15578       return MP_NO_ACTION;
15579
15580     if (player->push_delay == -1)       /* new pushing; restart delay */
15581       player->push_delay = 0;
15582
15583     if (player->push_delay < player->push_delay_value &&
15584         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15585         element != EL_SPRING && element != EL_BALLOON)
15586     {
15587       /* make sure that there is no move delay before next try to push */
15588       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15589         player->move_delay = 0;
15590
15591       return MP_NO_ACTION;
15592     }
15593
15594     if (IS_CUSTOM_ELEMENT(element) &&
15595         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15596     {
15597       if (!DigFieldByCE(nextx, nexty, element))
15598         return MP_NO_ACTION;
15599     }
15600
15601     if (IS_SB_ELEMENT(element))
15602     {
15603       if (element == EL_SOKOBAN_FIELD_FULL)
15604       {
15605         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15606         local_player->sokobanfields_still_needed++;
15607       }
15608
15609       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15610       {
15611         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15612         local_player->sokobanfields_still_needed--;
15613       }
15614
15615       Feld[x][y] = EL_SOKOBAN_OBJECT;
15616
15617       if (Back[x][y] == Back[nextx][nexty])
15618         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15619       else if (Back[x][y] != 0)
15620         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15621                                     ACTION_EMPTYING);
15622       else
15623         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15624                                     ACTION_FILLING);
15625
15626 #if 1
15627       if (local_player->sokobanfields_still_needed == 0 &&
15628           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15629 #else
15630       if (local_player->sokobanfields_still_needed == 0 &&
15631           game.emulation == EMU_SOKOBAN)
15632 #endif
15633       {
15634         PlayerWins(player);
15635
15636         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15637       }
15638     }
15639     else
15640       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15641
15642     InitMovingField(x, y, move_direction);
15643     GfxAction[x][y] = ACTION_PUSHING;
15644
15645     if (mode == DF_SNAP)
15646       ContinueMoving(x, y);
15647     else
15648       MovPos[x][y] = (dx != 0 ? dx : dy);
15649
15650     Pushed[x][y] = TRUE;
15651     Pushed[nextx][nexty] = TRUE;
15652
15653     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15654       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15655     else
15656       player->push_delay_value = -1;    /* get new value later */
15657
15658     /* check for element change _after_ element has been pushed */
15659     if (game.use_change_when_pushing_bug)
15660     {
15661       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15662                                  player->index_bit, dig_side);
15663       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15664                                           player->index_bit, dig_side);
15665     }
15666   }
15667   else if (IS_SWITCHABLE(element))
15668   {
15669     if (PLAYER_SWITCHING(player, x, y))
15670     {
15671       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15672                                           player->index_bit, dig_side);
15673
15674       return MP_ACTION;
15675     }
15676
15677     player->is_switching = TRUE;
15678     player->switch_x = x;
15679     player->switch_y = y;
15680
15681     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15682
15683     if (element == EL_ROBOT_WHEEL)
15684     {
15685       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15686       ZX = x;
15687       ZY = y;
15688
15689       game.robot_wheel_active = TRUE;
15690
15691       TEST_DrawLevelField(x, y);
15692     }
15693     else if (element == EL_SP_TERMINAL)
15694     {
15695       int xx, yy;
15696
15697       SCAN_PLAYFIELD(xx, yy)
15698       {
15699         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15700           Bang(xx, yy);
15701         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15702           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15703       }
15704     }
15705     else if (IS_BELT_SWITCH(element))
15706     {
15707       ToggleBeltSwitch(x, y);
15708     }
15709     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15710              element == EL_SWITCHGATE_SWITCH_DOWN ||
15711              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15712              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15713     {
15714       ToggleSwitchgateSwitch(x, y);
15715     }
15716     else if (element == EL_LIGHT_SWITCH ||
15717              element == EL_LIGHT_SWITCH_ACTIVE)
15718     {
15719       ToggleLightSwitch(x, y);
15720     }
15721     else if (element == EL_TIMEGATE_SWITCH ||
15722              element == EL_DC_TIMEGATE_SWITCH)
15723     {
15724       ActivateTimegateSwitch(x, y);
15725     }
15726     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15727              element == EL_BALLOON_SWITCH_RIGHT ||
15728              element == EL_BALLOON_SWITCH_UP    ||
15729              element == EL_BALLOON_SWITCH_DOWN  ||
15730              element == EL_BALLOON_SWITCH_NONE  ||
15731              element == EL_BALLOON_SWITCH_ANY)
15732     {
15733       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15734                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15735                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15736                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15737                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15738                              move_direction);
15739     }
15740     else if (element == EL_LAMP)
15741     {
15742       Feld[x][y] = EL_LAMP_ACTIVE;
15743       local_player->lights_still_needed--;
15744
15745       ResetGfxAnimation(x, y);
15746       TEST_DrawLevelField(x, y);
15747     }
15748     else if (element == EL_TIME_ORB_FULL)
15749     {
15750       Feld[x][y] = EL_TIME_ORB_EMPTY;
15751
15752       if (level.time > 0 || level.use_time_orb_bug)
15753       {
15754         TimeLeft += level.time_orb_time;
15755         game.no_time_limit = FALSE;
15756
15757 #if 1
15758         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15759
15760         DisplayGameControlValues();
15761 #else
15762         DrawGameValue_Time(TimeLeft);
15763 #endif
15764       }
15765
15766       ResetGfxAnimation(x, y);
15767       TEST_DrawLevelField(x, y);
15768     }
15769     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15770              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15771     {
15772       int xx, yy;
15773
15774       game.ball_state = !game.ball_state;
15775
15776       SCAN_PLAYFIELD(xx, yy)
15777       {
15778         int e = Feld[xx][yy];
15779
15780         if (game.ball_state)
15781         {
15782           if (e == EL_EMC_MAGIC_BALL)
15783             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15784           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15785             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15786         }
15787         else
15788         {
15789           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15790             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15791           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15792             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15793         }
15794       }
15795     }
15796
15797     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15798                                         player->index_bit, dig_side);
15799
15800     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15801                                         player->index_bit, dig_side);
15802
15803     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15804                                         player->index_bit, dig_side);
15805
15806     return MP_ACTION;
15807   }
15808   else
15809   {
15810     if (!PLAYER_SWITCHING(player, x, y))
15811     {
15812       player->is_switching = TRUE;
15813       player->switch_x = x;
15814       player->switch_y = y;
15815
15816       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15817                                  player->index_bit, dig_side);
15818       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15819                                           player->index_bit, dig_side);
15820
15821       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15822                                  player->index_bit, dig_side);
15823       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15824                                           player->index_bit, dig_side);
15825     }
15826
15827     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15828                                player->index_bit, dig_side);
15829     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15830                                         player->index_bit, dig_side);
15831
15832     return MP_NO_ACTION;
15833   }
15834
15835   player->push_delay = -1;
15836
15837   if (is_player)                /* function can also be called by EL_PENGUIN */
15838   {
15839     if (Feld[x][y] != element)          /* really digged/collected something */
15840     {
15841       player->is_collecting = !player->is_digging;
15842       player->is_active = TRUE;
15843     }
15844   }
15845
15846   return MP_MOVING;
15847 }
15848
15849 static boolean DigFieldByCE(int x, int y, int digging_element)
15850 {
15851   int element = Feld[x][y];
15852
15853   if (!IS_FREE(x, y))
15854   {
15855     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15856                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15857                   ACTION_BREAKING);
15858
15859     /* no element can dig solid indestructible elements */
15860     if (IS_INDESTRUCTIBLE(element) &&
15861         !IS_DIGGABLE(element) &&
15862         !IS_COLLECTIBLE(element))
15863       return FALSE;
15864
15865     if (AmoebaNr[x][y] &&
15866         (element == EL_AMOEBA_FULL ||
15867          element == EL_BD_AMOEBA ||
15868          element == EL_AMOEBA_GROWING))
15869     {
15870       AmoebaCnt[AmoebaNr[x][y]]--;
15871       AmoebaCnt2[AmoebaNr[x][y]]--;
15872     }
15873
15874     if (IS_MOVING(x, y))
15875       RemoveMovingField(x, y);
15876     else
15877     {
15878       RemoveField(x, y);
15879       TEST_DrawLevelField(x, y);
15880     }
15881
15882     /* if digged element was about to explode, prevent the explosion */
15883     ExplodeField[x][y] = EX_TYPE_NONE;
15884
15885     PlayLevelSoundAction(x, y, action);
15886   }
15887
15888   Store[x][y] = EL_EMPTY;
15889
15890 #if 1
15891   /* this makes it possible to leave the removed element again */
15892   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15893     Store[x][y] = element;
15894 #else
15895   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15896   {
15897     int move_leave_element = element_info[digging_element].move_leave_element;
15898
15899     /* this makes it possible to leave the removed element again */
15900     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15901                    element : move_leave_element);
15902   }
15903 #endif
15904
15905   return TRUE;
15906 }
15907
15908 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15909 {
15910   int jx = player->jx, jy = player->jy;
15911   int x = jx + dx, y = jy + dy;
15912   int snap_direction = (dx == -1 ? MV_LEFT  :
15913                         dx == +1 ? MV_RIGHT :
15914                         dy == -1 ? MV_UP    :
15915                         dy == +1 ? MV_DOWN  : MV_NONE);
15916   boolean can_continue_snapping = (level.continuous_snapping &&
15917                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15918
15919   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15920     return FALSE;
15921
15922   if (!player->active || !IN_LEV_FIELD(x, y))
15923     return FALSE;
15924
15925   if (dx && dy)
15926     return FALSE;
15927
15928   if (!dx && !dy)
15929   {
15930     if (player->MovPos == 0)
15931       player->is_pushing = FALSE;
15932
15933     player->is_snapping = FALSE;
15934
15935     if (player->MovPos == 0)
15936     {
15937       player->is_moving = FALSE;
15938       player->is_digging = FALSE;
15939       player->is_collecting = FALSE;
15940     }
15941
15942     return FALSE;
15943   }
15944
15945 #if USE_NEW_CONTINUOUS_SNAPPING
15946   /* prevent snapping with already pressed snap key when not allowed */
15947   if (player->is_snapping && !can_continue_snapping)
15948     return FALSE;
15949 #else
15950   if (player->is_snapping)
15951     return FALSE;
15952 #endif
15953
15954   player->MovDir = snap_direction;
15955
15956   if (player->MovPos == 0)
15957   {
15958     player->is_moving = FALSE;
15959     player->is_digging = FALSE;
15960     player->is_collecting = FALSE;
15961   }
15962
15963   player->is_dropping = FALSE;
15964   player->is_dropping_pressed = FALSE;
15965   player->drop_pressed_delay = 0;
15966
15967   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15968     return FALSE;
15969
15970   player->is_snapping = TRUE;
15971   player->is_active = TRUE;
15972
15973   if (player->MovPos == 0)
15974   {
15975     player->is_moving = FALSE;
15976     player->is_digging = FALSE;
15977     player->is_collecting = FALSE;
15978   }
15979
15980   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15981     TEST_DrawLevelField(player->last_jx, player->last_jy);
15982
15983   TEST_DrawLevelField(x, y);
15984
15985   return TRUE;
15986 }
15987
15988 static boolean DropElement(struct PlayerInfo *player)
15989 {
15990   int old_element, new_element;
15991   int dropx = player->jx, dropy = player->jy;
15992   int drop_direction = player->MovDir;
15993   int drop_side = drop_direction;
15994 #if 1
15995   int drop_element = get_next_dropped_element(player);
15996 #else
15997   int drop_element = (player->inventory_size > 0 ?
15998                       player->inventory_element[player->inventory_size - 1] :
15999                       player->inventory_infinite_element != EL_UNDEFINED ?
16000                       player->inventory_infinite_element :
16001                       player->dynabombs_left > 0 ?
16002                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
16003                       EL_UNDEFINED);
16004 #endif
16005
16006   player->is_dropping_pressed = TRUE;
16007
16008   /* do not drop an element on top of another element; when holding drop key
16009      pressed without moving, dropped element must move away before the next
16010      element can be dropped (this is especially important if the next element
16011      is dynamite, which can be placed on background for historical reasons) */
16012   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
16013     return MP_ACTION;
16014
16015   if (IS_THROWABLE(drop_element))
16016   {
16017     dropx += GET_DX_FROM_DIR(drop_direction);
16018     dropy += GET_DY_FROM_DIR(drop_direction);
16019
16020     if (!IN_LEV_FIELD(dropx, dropy))
16021       return FALSE;
16022   }
16023
16024   old_element = Feld[dropx][dropy];     /* old element at dropping position */
16025   new_element = drop_element;           /* default: no change when dropping */
16026
16027   /* check if player is active, not moving and ready to drop */
16028   if (!player->active || player->MovPos || player->drop_delay > 0)
16029     return FALSE;
16030
16031   /* check if player has anything that can be dropped */
16032   if (new_element == EL_UNDEFINED)
16033     return FALSE;
16034
16035   /* check if drop key was pressed long enough for EM style dynamite */
16036   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16037     return FALSE;
16038
16039   /* check if anything can be dropped at the current position */
16040   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16041     return FALSE;
16042
16043   /* collected custom elements can only be dropped on empty fields */
16044   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16045     return FALSE;
16046
16047   if (old_element != EL_EMPTY)
16048     Back[dropx][dropy] = old_element;   /* store old element on this field */
16049
16050   ResetGfxAnimation(dropx, dropy);
16051   ResetRandomAnimationValue(dropx, dropy);
16052
16053   if (player->inventory_size > 0 ||
16054       player->inventory_infinite_element != EL_UNDEFINED)
16055   {
16056     if (player->inventory_size > 0)
16057     {
16058       player->inventory_size--;
16059
16060       DrawGameDoorValues();
16061
16062       if (new_element == EL_DYNAMITE)
16063         new_element = EL_DYNAMITE_ACTIVE;
16064       else if (new_element == EL_EM_DYNAMITE)
16065         new_element = EL_EM_DYNAMITE_ACTIVE;
16066       else if (new_element == EL_SP_DISK_RED)
16067         new_element = EL_SP_DISK_RED_ACTIVE;
16068     }
16069
16070     Feld[dropx][dropy] = new_element;
16071
16072     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16073       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16074                           el2img(Feld[dropx][dropy]), 0);
16075
16076     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16077
16078     /* needed if previous element just changed to "empty" in the last frame */
16079     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16080
16081     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16082                                player->index_bit, drop_side);
16083     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16084                                         CE_PLAYER_DROPS_X,
16085                                         player->index_bit, drop_side);
16086
16087     TestIfElementTouchesCustomElement(dropx, dropy);
16088   }
16089   else          /* player is dropping a dyna bomb */
16090   {
16091     player->dynabombs_left--;
16092
16093     Feld[dropx][dropy] = new_element;
16094
16095     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16096       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16097                           el2img(Feld[dropx][dropy]), 0);
16098
16099     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16100   }
16101
16102   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16103     InitField_WithBug1(dropx, dropy, FALSE);
16104
16105   new_element = Feld[dropx][dropy];     /* element might have changed */
16106
16107   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16108       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16109   {
16110 #if 0
16111     int move_direction;
16112     int nextx, nexty;
16113 #endif
16114
16115     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16116       MovDir[dropx][dropy] = drop_direction;
16117
16118 #if 0
16119     move_direction = MovDir[dropx][dropy];
16120     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16121     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16122 #endif
16123
16124     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16125
16126 #if USE_FIX_IMPACT_COLLISION
16127     /* do not cause impact style collision by dropping elements that can fall */
16128     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16129 #else
16130     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16131 #endif
16132   }
16133
16134   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16135   player->is_dropping = TRUE;
16136
16137   player->drop_pressed_delay = 0;
16138   player->is_dropping_pressed = FALSE;
16139
16140   player->drop_x = dropx;
16141   player->drop_y = dropy;
16142
16143   return TRUE;
16144 }
16145
16146 /* ------------------------------------------------------------------------- */
16147 /* game sound playing functions                                              */
16148 /* ------------------------------------------------------------------------- */
16149
16150 static int *loop_sound_frame = NULL;
16151 static int *loop_sound_volume = NULL;
16152
16153 void InitPlayLevelSound()
16154 {
16155   int num_sounds = getSoundListSize();
16156
16157   checked_free(loop_sound_frame);
16158   checked_free(loop_sound_volume);
16159
16160   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16161   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16162 }
16163
16164 static void PlayLevelSound(int x, int y, int nr)
16165 {
16166   int sx = SCREENX(x), sy = SCREENY(y);
16167   int volume, stereo_position;
16168   int max_distance = 8;
16169   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16170
16171   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16172       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16173     return;
16174
16175   if (!IN_LEV_FIELD(x, y) ||
16176       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16177       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16178     return;
16179
16180   volume = SOUND_MAX_VOLUME;
16181
16182   if (!IN_SCR_FIELD(sx, sy))
16183   {
16184     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16185     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16186
16187     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16188   }
16189
16190   stereo_position = (SOUND_MAX_LEFT +
16191                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16192                      (SCR_FIELDX + 2 * max_distance));
16193
16194   if (IS_LOOP_SOUND(nr))
16195   {
16196     /* This assures that quieter loop sounds do not overwrite louder ones,
16197        while restarting sound volume comparison with each new game frame. */
16198
16199     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16200       return;
16201
16202     loop_sound_volume[nr] = volume;
16203     loop_sound_frame[nr] = FrameCounter;
16204   }
16205
16206   PlaySoundExt(nr, volume, stereo_position, type);
16207 }
16208
16209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16210 {
16211   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16212                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16213                  y < LEVELY(BY1) ? LEVELY(BY1) :
16214                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16215                  sound_action);
16216 }
16217
16218 static void PlayLevelSoundAction(int x, int y, int action)
16219 {
16220   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16221 }
16222
16223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16224 {
16225   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16226
16227   if (sound_effect != SND_UNDEFINED)
16228     PlayLevelSound(x, y, sound_effect);
16229 }
16230
16231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16232                                               int action)
16233 {
16234   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16235
16236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16237     PlayLevelSound(x, y, sound_effect);
16238 }
16239
16240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16241 {
16242   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16243
16244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16245     PlayLevelSound(x, y, sound_effect);
16246 }
16247
16248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16249 {
16250   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16251
16252   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16253     StopSound(sound_effect);
16254 }
16255
16256 static void PlayLevelMusic()
16257 {
16258   if (levelset.music[level_nr] != MUS_UNDEFINED)
16259     PlayMusic(levelset.music[level_nr]);        /* from config file */
16260   else
16261     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16262 }
16263
16264 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16265 {
16266   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16267   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16268   int x = xx - 1 - offset;
16269   int y = yy - 1 - offset;
16270
16271   switch (sample)
16272   {
16273     case SAMPLE_blank:
16274       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16275       break;
16276
16277     case SAMPLE_roll:
16278       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16279       break;
16280
16281     case SAMPLE_stone:
16282       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16283       break;
16284
16285     case SAMPLE_nut:
16286       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16287       break;
16288
16289     case SAMPLE_crack:
16290       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16291       break;
16292
16293     case SAMPLE_bug:
16294       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16295       break;
16296
16297     case SAMPLE_tank:
16298       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16299       break;
16300
16301     case SAMPLE_android_clone:
16302       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16303       break;
16304
16305     case SAMPLE_android_move:
16306       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16307       break;
16308
16309     case SAMPLE_spring:
16310       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16311       break;
16312
16313     case SAMPLE_slurp:
16314       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16315       break;
16316
16317     case SAMPLE_eater:
16318       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16319       break;
16320
16321     case SAMPLE_eater_eat:
16322       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16323       break;
16324
16325     case SAMPLE_alien:
16326       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16327       break;
16328
16329     case SAMPLE_collect:
16330       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16331       break;
16332
16333     case SAMPLE_diamond:
16334       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16335       break;
16336
16337     case SAMPLE_squash:
16338       /* !!! CHECK THIS !!! */
16339 #if 1
16340       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16341 #else
16342       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16343 #endif
16344       break;
16345
16346     case SAMPLE_wonderfall:
16347       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16348       break;
16349
16350     case SAMPLE_drip:
16351       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16352       break;
16353
16354     case SAMPLE_push:
16355       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16356       break;
16357
16358     case SAMPLE_dirt:
16359       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16360       break;
16361
16362     case SAMPLE_acid:
16363       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16364       break;
16365
16366     case SAMPLE_ball:
16367       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16368       break;
16369
16370     case SAMPLE_grow:
16371       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16372       break;
16373
16374     case SAMPLE_wonder:
16375       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16376       break;
16377
16378     case SAMPLE_door:
16379       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16380       break;
16381
16382     case SAMPLE_exit_open:
16383       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16384       break;
16385
16386     case SAMPLE_exit_leave:
16387       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16388       break;
16389
16390     case SAMPLE_dynamite:
16391       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16392       break;
16393
16394     case SAMPLE_tick:
16395       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16396       break;
16397
16398     case SAMPLE_press:
16399       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16400       break;
16401
16402     case SAMPLE_wheel:
16403       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16404       break;
16405
16406     case SAMPLE_boom:
16407       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16408       break;
16409
16410     case SAMPLE_die:
16411       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16412       break;
16413
16414     case SAMPLE_time:
16415       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16416       break;
16417
16418     default:
16419       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16420       break;
16421   }
16422 }
16423
16424 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16425 {
16426   int element = map_element_SP_to_RND(element_sp);
16427   int action = map_action_SP_to_RND(action_sp);
16428   int offset = (setup.sp_show_border_elements ? 0 : 1);
16429   int x = xx - offset;
16430   int y = yy - offset;
16431
16432 #if 0
16433   printf("::: %d -> %d\n", element_sp, action_sp);
16434 #endif
16435
16436   PlayLevelSoundElementAction(x, y, element, action);
16437 }
16438
16439 void RaiseScore(int value)
16440 {
16441   local_player->score += value;
16442
16443 #if 1
16444   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16445
16446   DisplayGameControlValues();
16447 #else
16448   DrawGameValue_Score(local_player->score);
16449 #endif
16450 }
16451
16452 void RaiseScoreElement(int element)
16453 {
16454   switch (element)
16455   {
16456     case EL_EMERALD:
16457     case EL_BD_DIAMOND:
16458     case EL_EMERALD_YELLOW:
16459     case EL_EMERALD_RED:
16460     case EL_EMERALD_PURPLE:
16461     case EL_SP_INFOTRON:
16462       RaiseScore(level.score[SC_EMERALD]);
16463       break;
16464     case EL_DIAMOND:
16465       RaiseScore(level.score[SC_DIAMOND]);
16466       break;
16467     case EL_CRYSTAL:
16468       RaiseScore(level.score[SC_CRYSTAL]);
16469       break;
16470     case EL_PEARL:
16471       RaiseScore(level.score[SC_PEARL]);
16472       break;
16473     case EL_BUG:
16474     case EL_BD_BUTTERFLY:
16475     case EL_SP_ELECTRON:
16476       RaiseScore(level.score[SC_BUG]);
16477       break;
16478     case EL_SPACESHIP:
16479     case EL_BD_FIREFLY:
16480     case EL_SP_SNIKSNAK:
16481       RaiseScore(level.score[SC_SPACESHIP]);
16482       break;
16483     case EL_YAMYAM:
16484     case EL_DARK_YAMYAM:
16485       RaiseScore(level.score[SC_YAMYAM]);
16486       break;
16487     case EL_ROBOT:
16488       RaiseScore(level.score[SC_ROBOT]);
16489       break;
16490     case EL_PACMAN:
16491       RaiseScore(level.score[SC_PACMAN]);
16492       break;
16493     case EL_NUT:
16494       RaiseScore(level.score[SC_NUT]);
16495       break;
16496     case EL_DYNAMITE:
16497     case EL_EM_DYNAMITE:
16498     case EL_SP_DISK_RED:
16499     case EL_DYNABOMB_INCREASE_NUMBER:
16500     case EL_DYNABOMB_INCREASE_SIZE:
16501     case EL_DYNABOMB_INCREASE_POWER:
16502       RaiseScore(level.score[SC_DYNAMITE]);
16503       break;
16504     case EL_SHIELD_NORMAL:
16505     case EL_SHIELD_DEADLY:
16506       RaiseScore(level.score[SC_SHIELD]);
16507       break;
16508     case EL_EXTRA_TIME:
16509       RaiseScore(level.extra_time_score);
16510       break;
16511     case EL_KEY_1:
16512     case EL_KEY_2:
16513     case EL_KEY_3:
16514     case EL_KEY_4:
16515     case EL_EM_KEY_1:
16516     case EL_EM_KEY_2:
16517     case EL_EM_KEY_3:
16518     case EL_EM_KEY_4:
16519     case EL_EMC_KEY_5:
16520     case EL_EMC_KEY_6:
16521     case EL_EMC_KEY_7:
16522     case EL_EMC_KEY_8:
16523     case EL_DC_KEY_WHITE:
16524       RaiseScore(level.score[SC_KEY]);
16525       break;
16526     default:
16527       RaiseScore(element_info[element].collect_score);
16528       break;
16529   }
16530 }
16531
16532 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16533 {
16534   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16535   {
16536 #if 1
16537     /* closing door required in case of envelope style request dialogs */
16538     if (!skip_request)
16539       CloseDoor(DOOR_CLOSE_1);
16540 #endif
16541
16542 #if defined(NETWORK_AVALIABLE)
16543     if (options.network)
16544       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16545     else
16546 #endif
16547     {
16548       if (quick_quit)
16549       {
16550 #if 1
16551
16552 #if 1
16553         FadeSkipNextFadeIn();
16554 #else
16555         fading = fading_none;
16556 #endif
16557
16558 #else
16559         OpenDoor(DOOR_CLOSE_1);
16560 #endif
16561
16562         game_status = GAME_MODE_MAIN;
16563
16564 #if 1
16565         DrawAndFadeInMainMenu(REDRAW_FIELD);
16566 #else
16567         DrawMainMenu();
16568 #endif
16569       }
16570       else
16571       {
16572 #if 0
16573         FadeOut(REDRAW_FIELD);
16574 #endif
16575
16576         game_status = GAME_MODE_MAIN;
16577
16578         DrawAndFadeInMainMenu(REDRAW_FIELD);
16579       }
16580     }
16581   }
16582   else          /* continue playing the game */
16583   {
16584     if (tape.playing && tape.deactivate_display)
16585       TapeDeactivateDisplayOff(TRUE);
16586
16587     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16588
16589     if (tape.playing && tape.deactivate_display)
16590       TapeDeactivateDisplayOn();
16591   }
16592 }
16593
16594 void RequestQuitGame(boolean ask_if_really_quit)
16595 {
16596   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16597   boolean skip_request = AllPlayersGone || quick_quit;
16598
16599   RequestQuitGameExt(skip_request, quick_quit,
16600                      "Do you really want to quit the game?");
16601 }
16602
16603
16604 /* ------------------------------------------------------------------------- */
16605 /* random generator functions                                                */
16606 /* ------------------------------------------------------------------------- */
16607
16608 unsigned int InitEngineRandom_RND(int seed)
16609 {
16610   game.num_random_calls = 0;
16611
16612 #if 0
16613   unsigned int rnd_seed = InitEngineRandom(seed);
16614
16615   printf("::: START RND: %d\n", rnd_seed);
16616
16617   return rnd_seed;
16618 #else
16619
16620   return InitEngineRandom(seed);
16621
16622 #endif
16623
16624 }
16625
16626 unsigned int RND(int max)
16627 {
16628   if (max > 0)
16629   {
16630     game.num_random_calls++;
16631
16632     return GetEngineRandom(max);
16633   }
16634
16635   return 0;
16636 }
16637
16638
16639 /* ------------------------------------------------------------------------- */
16640 /* game engine snapshot handling functions                                   */
16641 /* ------------------------------------------------------------------------- */
16642
16643 struct EngineSnapshotInfo
16644 {
16645   /* runtime values for custom element collect score */
16646   int collect_score[NUM_CUSTOM_ELEMENTS];
16647
16648   /* runtime values for group element choice position */
16649   int choice_pos[NUM_GROUP_ELEMENTS];
16650
16651   /* runtime values for belt position animations */
16652   int belt_graphic[4][NUM_BELT_PARTS];
16653   int belt_anim_mode[4][NUM_BELT_PARTS];
16654 };
16655
16656 static struct EngineSnapshotInfo engine_snapshot_rnd;
16657 static char *snapshot_level_identifier = NULL;
16658 static int snapshot_level_nr = -1;
16659
16660 static void SaveEngineSnapshotValues_RND()
16661 {
16662   static int belt_base_active_element[4] =
16663   {
16664     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16665     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16666     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16667     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16668   };
16669   int i, j;
16670
16671   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16672   {
16673     int element = EL_CUSTOM_START + i;
16674
16675     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16676   }
16677
16678   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16679   {
16680     int element = EL_GROUP_START + i;
16681
16682     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16683   }
16684
16685   for (i = 0; i < 4; i++)
16686   {
16687     for (j = 0; j < NUM_BELT_PARTS; j++)
16688     {
16689       int element = belt_base_active_element[i] + j;
16690       int graphic = el2img(element);
16691       int anim_mode = graphic_info[graphic].anim_mode;
16692
16693       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16694       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16695     }
16696   }
16697 }
16698
16699 static void LoadEngineSnapshotValues_RND()
16700 {
16701   unsigned int num_random_calls = game.num_random_calls;
16702   int i, j;
16703
16704   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16705   {
16706     int element = EL_CUSTOM_START + i;
16707
16708     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16709   }
16710
16711   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16712   {
16713     int element = EL_GROUP_START + i;
16714
16715     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16716   }
16717
16718   for (i = 0; i < 4; i++)
16719   {
16720     for (j = 0; j < NUM_BELT_PARTS; j++)
16721     {
16722       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16723       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16724
16725       graphic_info[graphic].anim_mode = anim_mode;
16726     }
16727   }
16728
16729   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16730   {
16731     InitRND(tape.random_seed);
16732     for (i = 0; i < num_random_calls; i++)
16733       RND(1);
16734   }
16735
16736   if (game.num_random_calls != num_random_calls)
16737   {
16738     Error(ERR_INFO, "number of random calls out of sync");
16739     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16740     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16741     Error(ERR_EXIT, "this should not happen -- please debug");
16742   }
16743 }
16744
16745 void FreeEngineSnapshot()
16746 {
16747   FreeEngineSnapshotBuffers();
16748
16749   setString(&snapshot_level_identifier, NULL);
16750   snapshot_level_nr = -1;
16751 }
16752
16753 void SaveEngineSnapshot()
16754 {
16755   /* do not save snapshots from editor */
16756   if (level_editor_test_game)
16757     return;
16758
16759   /* free previous snapshot buffers, if needed */
16760   FreeEngineSnapshotBuffers();
16761
16762 #if 1
16763   /* copy some special values to a structure better suited for the snapshot */
16764
16765   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16766     SaveEngineSnapshotValues_RND();
16767   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16768     SaveEngineSnapshotValues_EM();
16769   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16770     SaveEngineSnapshotValues_SP();
16771
16772   /* save values stored in special snapshot structure */
16773
16774   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16775     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16776   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16777     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16778   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16779     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16780 #else
16781   /* copy some special values to a structure better suited for the snapshot */
16782
16783   SaveEngineSnapshotValues_RND();
16784   SaveEngineSnapshotValues_EM();
16785   SaveEngineSnapshotValues_SP();
16786
16787   /* save values stored in special snapshot structure */
16788
16789   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16790   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16791   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16792 #endif
16793
16794   /* save further RND engine values */
16795
16796   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16797   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16799
16800   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16801   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16802   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16804
16805   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16807   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16808   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16810
16811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16813   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16814
16815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16816
16817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16818
16819   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16820   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16821
16822   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16824   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16827   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16840
16841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16843
16844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16847
16848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16849   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16850
16851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16853   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16856
16857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16859
16860   /* save level identification information */
16861
16862   setString(&snapshot_level_identifier, leveldir_current->identifier);
16863   snapshot_level_nr = level_nr;
16864
16865 #if 0
16866   ListNode *node = engine_snapshot_list_rnd;
16867   int num_bytes = 0;
16868
16869   while (node != NULL)
16870   {
16871     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16872
16873     node = node->next;
16874   }
16875
16876   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16877 #endif
16878 }
16879
16880 void LoadEngineSnapshot()
16881 {
16882   /* restore generically stored snapshot buffers */
16883
16884   LoadEngineSnapshotBuffers();
16885
16886   /* restore special values from snapshot structure */
16887
16888 #if 1
16889   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16890     LoadEngineSnapshotValues_RND();
16891   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16892     LoadEngineSnapshotValues_EM();
16893   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16894     LoadEngineSnapshotValues_SP();
16895 #else
16896   LoadEngineSnapshotValues_RND();
16897   LoadEngineSnapshotValues_EM();
16898   LoadEngineSnapshotValues_SP();
16899 #endif
16900
16901 #if 0
16902   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16903 #endif
16904
16905 #if 0
16906   // needed if tile size was different when saving and loading engine snapshot
16907   if (local_player->present)
16908   {
16909     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
16910                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16911                 local_player->jx - MIDPOSX);
16912
16913     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16914                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16915                 local_player->jy - MIDPOSY);
16916   }
16917 #endif
16918
16919 #if 0
16920   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16921 #endif
16922 }
16923
16924 boolean CheckEngineSnapshot()
16925 {
16926   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16927           snapshot_level_nr == level_nr);
16928 }
16929
16930
16931 /* ---------- new game button stuff ---------------------------------------- */
16932
16933 static struct
16934 {
16935   int graphic;
16936   struct Rect *pos;
16937   int gadget_id;
16938   char *infotext;
16939 } gamebutton_info[NUM_GAME_BUTTONS] =
16940 {
16941   {
16942     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16943     GAME_CTRL_ID_STOP,                  "stop game"
16944   },
16945   {
16946     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16947     GAME_CTRL_ID_PAUSE,                 "pause game"
16948   },
16949   {
16950     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16951     GAME_CTRL_ID_PLAY,                  "play game"
16952   },
16953   {
16954     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16955     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16956   },
16957   {
16958     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16959     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16960   },
16961   {
16962     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16963     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16964   },
16965   {
16966     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16967     GAME_CTRL_ID_SAVE,                  "save game"
16968   },
16969   {
16970     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16971     GAME_CTRL_ID_LOAD,                  "load game"
16972   }
16973 };
16974
16975 void CreateGameButtons()
16976 {
16977   int i;
16978
16979   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16980   {
16981     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16982     struct Rect *pos = gamebutton_info[i].pos;
16983     struct GadgetInfo *gi;
16984     int button_type;
16985     boolean checked;
16986     unsigned int event_mask;
16987     int base_x = (tape.show_game_buttons ? VX : DX);
16988     int base_y = (tape.show_game_buttons ? VY : DY);
16989     int gd_x   = gfx->src_x;
16990     int gd_y   = gfx->src_y;
16991     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16992     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16993     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16994     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16995     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16996     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16997     int id = i;
16998
16999     if (gfx->bitmap == NULL)
17000     {
17001       game_gadget[id] = NULL;
17002
17003       continue;
17004     }
17005
17006     if (id == GAME_CTRL_ID_STOP ||
17007         id == GAME_CTRL_ID_PAUSE ||
17008         id == GAME_CTRL_ID_PLAY ||
17009         id == GAME_CTRL_ID_SAVE ||
17010         id == GAME_CTRL_ID_LOAD)
17011     {
17012       button_type = GD_TYPE_NORMAL_BUTTON;
17013       checked = FALSE;
17014       event_mask = GD_EVENT_RELEASED;
17015     }
17016     else
17017     {
17018       button_type = GD_TYPE_CHECK_BUTTON;
17019       checked =
17020         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17021          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17022          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17023       event_mask = GD_EVENT_PRESSED;
17024     }
17025
17026     gi = CreateGadget(GDI_CUSTOM_ID, id,
17027                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
17028                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17029                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17030                       GDI_WIDTH, gfx->width,
17031                       GDI_HEIGHT, gfx->height,
17032                       GDI_TYPE, button_type,
17033                       GDI_STATE, GD_BUTTON_UNPRESSED,
17034                       GDI_CHECKED, checked,
17035                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17036                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17037                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17038                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17039                       GDI_DIRECT_DRAW, FALSE,
17040                       GDI_EVENT_MASK, event_mask,
17041                       GDI_CALLBACK_ACTION, HandleGameButtons,
17042                       GDI_END);
17043
17044     if (gi == NULL)
17045       Error(ERR_EXIT, "cannot create gadget");
17046
17047     game_gadget[id] = gi;
17048   }
17049 }
17050
17051 void FreeGameButtons()
17052 {
17053   int i;
17054
17055   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17056     FreeGadget(game_gadget[i]);
17057 }
17058
17059 void MapGameButtons()
17060 {
17061   int i;
17062
17063   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17064     MapGadget(game_gadget[i]);
17065 }
17066
17067 void UnmapGameButtons()
17068 {
17069   int i;
17070
17071   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17072     UnmapGadget(game_gadget[i]);
17073 }
17074
17075 void RedrawGameButtons()
17076 {
17077   int i;
17078
17079   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17080     RedrawGadget(game_gadget[i]);
17081
17082   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
17083   redraw_mask &= ~REDRAW_ALL;
17084 }
17085
17086 static void HandleGameButtonsExt(int id)
17087 {
17088   boolean handle_game_buttons =
17089     (game_status == GAME_MODE_PLAYING ||
17090      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17091
17092   if (!handle_game_buttons)
17093     return;
17094
17095   switch (id)
17096   {
17097     case GAME_CTRL_ID_STOP:
17098       if (game_status == GAME_MODE_MAIN)
17099         break;
17100
17101       if (tape.playing)
17102         TapeStop();
17103       else
17104         RequestQuitGame(TRUE);
17105
17106       break;
17107
17108     case GAME_CTRL_ID_PAUSE:
17109       if (options.network && game_status == GAME_MODE_PLAYING)
17110       {
17111 #if defined(NETWORK_AVALIABLE)
17112         if (tape.pausing)
17113           SendToServer_ContinuePlaying();
17114         else
17115           SendToServer_PausePlaying();
17116 #endif
17117       }
17118       else
17119         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17120       break;
17121
17122     case GAME_CTRL_ID_PLAY:
17123       if (game_status == GAME_MODE_MAIN)
17124       {
17125         StartGameActions(options.network, setup.autorecord, level.random_seed);
17126       }
17127       else if (tape.pausing)
17128       {
17129 #if defined(NETWORK_AVALIABLE)
17130         if (options.network)
17131           SendToServer_ContinuePlaying();
17132         else
17133 #endif
17134         {
17135           tape.pausing = FALSE;
17136           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17137         }
17138       }
17139       break;
17140
17141     case SOUND_CTRL_ID_MUSIC:
17142       if (setup.sound_music)
17143       { 
17144         setup.sound_music = FALSE;
17145
17146         FadeMusic();
17147       }
17148       else if (audio.music_available)
17149       { 
17150         setup.sound = setup.sound_music = TRUE;
17151
17152         SetAudioMode(setup.sound);
17153
17154         PlayLevelMusic();
17155       }
17156       break;
17157
17158     case SOUND_CTRL_ID_LOOPS:
17159       if (setup.sound_loops)
17160         setup.sound_loops = FALSE;
17161       else if (audio.loops_available)
17162       {
17163         setup.sound = setup.sound_loops = TRUE;
17164
17165         SetAudioMode(setup.sound);
17166       }
17167       break;
17168
17169     case SOUND_CTRL_ID_SIMPLE:
17170       if (setup.sound_simple)
17171         setup.sound_simple = FALSE;
17172       else if (audio.sound_available)
17173       {
17174         setup.sound = setup.sound_simple = TRUE;
17175
17176         SetAudioMode(setup.sound);
17177       }
17178       break;
17179
17180     case GAME_CTRL_ID_SAVE:
17181       TapeQuickSave();
17182       break;
17183
17184     case GAME_CTRL_ID_LOAD:
17185       TapeQuickLoad();
17186       break;
17187
17188     default:
17189       break;
17190   }
17191 }
17192
17193 static void HandleGameButtons(struct GadgetInfo *gi)
17194 {
17195   HandleGameButtonsExt(gi->custom_id);
17196 }
17197
17198 void HandleSoundButtonKeys(Key key)
17199 {
17200 #if 1
17201   if (key == setup.shortcut.sound_simple)
17202     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17203   else if (key == setup.shortcut.sound_loops)
17204     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17205   else if (key == setup.shortcut.sound_music)
17206     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17207 #else
17208   if (key == setup.shortcut.sound_simple)
17209     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17210   else if (key == setup.shortcut.sound_loops)
17211     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17212   else if (key == setup.shortcut.sound_music)
17213     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17214 #endif
17215 }