rnd-20140514-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24
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     /* blit playfield from scroll buffer to normal back buffer for fading in */
4511     BlitScreenToBitmap_EM(backbuffer);
4512   }
4513   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4514   {
4515     InitGameEngine_SP();
4516
4517     /* blit playfield from scroll buffer to normal back buffer for fading in */
4518     BlitScreenToBitmap_SP(backbuffer);
4519   }
4520   else
4521   {
4522     DrawLevel();
4523     DrawAllPlayers();
4524
4525     /* after drawing the level, correct some elements */
4526     if (game.timegate_time_left == 0)
4527       CloseAllOpenTimegates();
4528
4529 #if NEW_TILESIZE
4530     BlitScreenToBitmap(backbuffer);
4531 #else
4532     /* blit playfield from scroll buffer to normal back buffer for fading in */
4533     if (setup.soft_scrolling)
4534       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4535 #endif
4536
4537     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4538   }
4539   /* !!! FIX THIS (END) !!! */
4540
4541 #if 1
4542   FadeIn(REDRAW_FIELD);
4543 #else
4544   if (do_fading)
4545     FadeIn(REDRAW_FIELD);
4546
4547   BackToFront();
4548 #endif
4549
4550   if (!game.restart_level)
4551   {
4552     /* copy default game door content to main double buffer */
4553 #if 1
4554 #if 1
4555     /* !!! CHECK AGAIN !!! */
4556     SetPanelBackground();
4557     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4558     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4559 #else
4560     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4561
4562     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4563     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4564     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4565                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4566 #endif
4567 #else
4568     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4569                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4570 #endif
4571   }
4572
4573   SetPanelBackground();
4574   SetDrawBackgroundMask(REDRAW_DOOR_1);
4575
4576 #if 1
4577   UpdateAndDisplayGameControlValues();
4578 #else
4579   UpdateGameDoorValues();
4580   DrawGameDoorValues();
4581 #endif
4582
4583   if (!game.restart_level)
4584   {
4585     UnmapGameButtons();
4586     UnmapTapeButtons();
4587     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4588     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4589     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4590     MapGameButtons();
4591     MapTapeButtons();
4592
4593     /* copy actual game door content to door double buffer for OpenDoor() */
4594 #if 1
4595     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4596 #else
4597     BlitBitmap(drawto, bitmap_db_door,
4598                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4599 #endif
4600
4601     OpenDoor(DOOR_OPEN_ALL);
4602
4603     PlaySound(SND_GAME_STARTING);
4604
4605     if (setup.sound_music)
4606       PlayLevelMusic();
4607
4608     KeyboardAutoRepeatOffUnlessAutoplay();
4609
4610 #if DEBUG_INIT_PLAYER
4611     if (options.debug)
4612     {
4613       printf("Player status (final):\n");
4614
4615       for (i = 0; i < MAX_PLAYERS; i++)
4616       {
4617         struct PlayerInfo *player = &stored_player[i];
4618
4619         printf("- player %d: present == %d, connected == %d, active == %d",
4620                i + 1,
4621                player->present,
4622                player->connected,
4623                player->active);
4624
4625         if (local_player == player)
4626           printf(" (local player)");
4627
4628         printf("\n");
4629       }
4630     }
4631 #endif
4632   }
4633
4634 #if 1
4635   UnmapAllGadgets();
4636
4637   MapGameButtons();
4638   MapTapeButtons();
4639 #endif
4640
4641   if (!game.restart_level && !tape.playing)
4642   {
4643     LevelStats_incPlayed(level_nr);
4644
4645     SaveLevelSetup_SeriesInfo();
4646
4647 #if 0
4648     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4649 #endif
4650   }
4651
4652   game.restart_level = FALSE;
4653 }
4654
4655 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4656 {
4657   /* this is used for non-R'n'D game engines to update certain engine values */
4658
4659   /* needed to determine if sounds are played within the visible screen area */
4660   scroll_x = actual_scroll_x;
4661   scroll_y = actual_scroll_y;
4662 }
4663
4664 void InitMovDir(int x, int y)
4665 {
4666   int i, element = Feld[x][y];
4667   static int xy[4][2] =
4668   {
4669     {  0, +1 },
4670     { +1,  0 },
4671     {  0, -1 },
4672     { -1,  0 }
4673   };
4674   static int direction[3][4] =
4675   {
4676     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4677     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4678     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4679   };
4680
4681   switch (element)
4682   {
4683     case EL_BUG_RIGHT:
4684     case EL_BUG_UP:
4685     case EL_BUG_LEFT:
4686     case EL_BUG_DOWN:
4687       Feld[x][y] = EL_BUG;
4688       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4689       break;
4690
4691     case EL_SPACESHIP_RIGHT:
4692     case EL_SPACESHIP_UP:
4693     case EL_SPACESHIP_LEFT:
4694     case EL_SPACESHIP_DOWN:
4695       Feld[x][y] = EL_SPACESHIP;
4696       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4697       break;
4698
4699     case EL_BD_BUTTERFLY_RIGHT:
4700     case EL_BD_BUTTERFLY_UP:
4701     case EL_BD_BUTTERFLY_LEFT:
4702     case EL_BD_BUTTERFLY_DOWN:
4703       Feld[x][y] = EL_BD_BUTTERFLY;
4704       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4705       break;
4706
4707     case EL_BD_FIREFLY_RIGHT:
4708     case EL_BD_FIREFLY_UP:
4709     case EL_BD_FIREFLY_LEFT:
4710     case EL_BD_FIREFLY_DOWN:
4711       Feld[x][y] = EL_BD_FIREFLY;
4712       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4713       break;
4714
4715     case EL_PACMAN_RIGHT:
4716     case EL_PACMAN_UP:
4717     case EL_PACMAN_LEFT:
4718     case EL_PACMAN_DOWN:
4719       Feld[x][y] = EL_PACMAN;
4720       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4721       break;
4722
4723     case EL_YAMYAM_LEFT:
4724     case EL_YAMYAM_RIGHT:
4725     case EL_YAMYAM_UP:
4726     case EL_YAMYAM_DOWN:
4727       Feld[x][y] = EL_YAMYAM;
4728       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4729       break;
4730
4731     case EL_SP_SNIKSNAK:
4732       MovDir[x][y] = MV_UP;
4733       break;
4734
4735     case EL_SP_ELECTRON:
4736       MovDir[x][y] = MV_LEFT;
4737       break;
4738
4739     case EL_MOLE_LEFT:
4740     case EL_MOLE_RIGHT:
4741     case EL_MOLE_UP:
4742     case EL_MOLE_DOWN:
4743       Feld[x][y] = EL_MOLE;
4744       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4745       break;
4746
4747     default:
4748       if (IS_CUSTOM_ELEMENT(element))
4749       {
4750         struct ElementInfo *ei = &element_info[element];
4751         int move_direction_initial = ei->move_direction_initial;
4752         int move_pattern = ei->move_pattern;
4753
4754         if (move_direction_initial == MV_START_PREVIOUS)
4755         {
4756           if (MovDir[x][y] != MV_NONE)
4757             return;
4758
4759           move_direction_initial = MV_START_AUTOMATIC;
4760         }
4761
4762         if (move_direction_initial == MV_START_RANDOM)
4763           MovDir[x][y] = 1 << RND(4);
4764         else if (move_direction_initial & MV_ANY_DIRECTION)
4765           MovDir[x][y] = move_direction_initial;
4766         else if (move_pattern == MV_ALL_DIRECTIONS ||
4767                  move_pattern == MV_TURNING_LEFT ||
4768                  move_pattern == MV_TURNING_RIGHT ||
4769                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4770                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4771                  move_pattern == MV_TURNING_RANDOM)
4772           MovDir[x][y] = 1 << RND(4);
4773         else if (move_pattern == MV_HORIZONTAL)
4774           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4775         else if (move_pattern == MV_VERTICAL)
4776           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4777         else if (move_pattern & MV_ANY_DIRECTION)
4778           MovDir[x][y] = element_info[element].move_pattern;
4779         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4780                  move_pattern == MV_ALONG_RIGHT_SIDE)
4781         {
4782           /* use random direction as default start direction */
4783           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4784             MovDir[x][y] = 1 << RND(4);
4785
4786           for (i = 0; i < NUM_DIRECTIONS; i++)
4787           {
4788             int x1 = x + xy[i][0];
4789             int y1 = y + xy[i][1];
4790
4791             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4792             {
4793               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4794                 MovDir[x][y] = direction[0][i];
4795               else
4796                 MovDir[x][y] = direction[1][i];
4797
4798               break;
4799             }
4800           }
4801         }                
4802       }
4803       else
4804       {
4805         MovDir[x][y] = 1 << RND(4);
4806
4807         if (element != EL_BUG &&
4808             element != EL_SPACESHIP &&
4809             element != EL_BD_BUTTERFLY &&
4810             element != EL_BD_FIREFLY)
4811           break;
4812
4813         for (i = 0; i < NUM_DIRECTIONS; i++)
4814         {
4815           int x1 = x + xy[i][0];
4816           int y1 = y + xy[i][1];
4817
4818           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4819           {
4820             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4821             {
4822               MovDir[x][y] = direction[0][i];
4823               break;
4824             }
4825             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4826                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4827             {
4828               MovDir[x][y] = direction[1][i];
4829               break;
4830             }
4831           }
4832         }
4833       }
4834       break;
4835   }
4836
4837   GfxDir[x][y] = MovDir[x][y];
4838 }
4839
4840 void InitAmoebaNr(int x, int y)
4841 {
4842   int i;
4843   int group_nr = AmoebeNachbarNr(x, y);
4844
4845   if (group_nr == 0)
4846   {
4847     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4848     {
4849       if (AmoebaCnt[i] == 0)
4850       {
4851         group_nr = i;
4852         break;
4853       }
4854     }
4855   }
4856
4857   AmoebaNr[x][y] = group_nr;
4858   AmoebaCnt[group_nr]++;
4859   AmoebaCnt2[group_nr]++;
4860 }
4861
4862 static void PlayerWins(struct PlayerInfo *player)
4863 {
4864   player->LevelSolved = TRUE;
4865   player->GameOver = TRUE;
4866
4867   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4868                          level.native_em_level->lev->score : player->score);
4869
4870   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4871                                       TimeLeft);
4872   player->LevelSolved_CountingScore = player->score_final;
4873 }
4874
4875 void GameWon()
4876 {
4877   static int time, time_final;
4878   static int score, score_final;
4879   static int game_over_delay_1 = 0;
4880   static int game_over_delay_2 = 0;
4881   int game_over_delay_value_1 = 50;
4882   int game_over_delay_value_2 = 50;
4883
4884   if (!local_player->LevelSolved_GameWon)
4885   {
4886     int i;
4887
4888     /* do not start end game actions before the player stops moving (to exit) */
4889     if (local_player->MovPos)
4890       return;
4891
4892     local_player->LevelSolved_GameWon = TRUE;
4893     local_player->LevelSolved_SaveTape = tape.recording;
4894     local_player->LevelSolved_SaveScore = !tape.playing;
4895
4896     if (!tape.playing)
4897     {
4898       LevelStats_incSolved(level_nr);
4899
4900       SaveLevelSetup_SeriesInfo();
4901
4902 #if 0
4903       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4904 #endif
4905     }
4906
4907     if (tape.auto_play)         /* tape might already be stopped here */
4908       tape.auto_play_level_solved = TRUE;
4909
4910 #if 1
4911     TapeStop();
4912 #endif
4913
4914     game_over_delay_1 = game_over_delay_value_1;
4915     game_over_delay_2 = game_over_delay_value_2;
4916
4917     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4918     score = score_final = local_player->score_final;
4919
4920     if (TimeLeft > 0)
4921     {
4922       time_final = 0;
4923       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4924     }
4925     else if (game.no_time_limit && TimePlayed < 999)
4926     {
4927       time_final = 999;
4928       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4929     }
4930
4931     local_player->score_final = score_final;
4932
4933     if (level_editor_test_game)
4934     {
4935       time = time_final;
4936       score = score_final;
4937
4938 #if 1
4939       local_player->LevelSolved_CountingTime = time;
4940       local_player->LevelSolved_CountingScore = score;
4941
4942       game_panel_controls[GAME_PANEL_TIME].value = time;
4943       game_panel_controls[GAME_PANEL_SCORE].value = score;
4944
4945       DisplayGameControlValues();
4946 #else
4947       DrawGameValue_Time(time);
4948       DrawGameValue_Score(score);
4949 #endif
4950     }
4951
4952     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4953     {
4954       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4955       {
4956         /* close exit door after last player */
4957         if ((AllPlayersGone &&
4958              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4959               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4960               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4961             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4962             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4963         {
4964           int element = Feld[ExitX][ExitY];
4965
4966 #if 0
4967           if (element == EL_EM_EXIT_OPEN ||
4968               element == EL_EM_STEEL_EXIT_OPEN)
4969           {
4970             Bang(ExitX, ExitY);
4971           }
4972           else
4973 #endif
4974           {
4975             Feld[ExitX][ExitY] =
4976               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4977                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4978                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4979                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4980                EL_EM_STEEL_EXIT_CLOSING);
4981
4982             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4983           }
4984         }
4985
4986         /* player disappears */
4987         DrawLevelField(ExitX, ExitY);
4988       }
4989
4990       for (i = 0; i < MAX_PLAYERS; i++)
4991       {
4992         struct PlayerInfo *player = &stored_player[i];
4993
4994         if (player->present)
4995         {
4996           RemovePlayer(player);
4997
4998           /* player disappears */
4999           DrawLevelField(player->jx, player->jy);
5000         }
5001       }
5002     }
5003
5004     PlaySound(SND_GAME_WINNING);
5005   }
5006
5007   if (game_over_delay_1 > 0)
5008   {
5009     game_over_delay_1--;
5010
5011     return;
5012   }
5013
5014   if (time != time_final)
5015   {
5016     int time_to_go = ABS(time_final - time);
5017     int time_count_dir = (time < time_final ? +1 : -1);
5018     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5019
5020     time  += time_count_steps * time_count_dir;
5021     score += time_count_steps * level.score[SC_TIME_BONUS];
5022
5023 #if 1
5024     local_player->LevelSolved_CountingTime = time;
5025     local_player->LevelSolved_CountingScore = score;
5026
5027     game_panel_controls[GAME_PANEL_TIME].value = time;
5028     game_panel_controls[GAME_PANEL_SCORE].value = score;
5029
5030     DisplayGameControlValues();
5031 #else
5032     DrawGameValue_Time(time);
5033     DrawGameValue_Score(score);
5034 #endif
5035
5036     if (time == time_final)
5037       StopSound(SND_GAME_LEVELTIME_BONUS);
5038     else if (setup.sound_loops)
5039       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5040     else
5041       PlaySound(SND_GAME_LEVELTIME_BONUS);
5042
5043     return;
5044   }
5045
5046   local_player->LevelSolved_PanelOff = TRUE;
5047
5048   if (game_over_delay_2 > 0)
5049   {
5050     game_over_delay_2--;
5051
5052     return;
5053   }
5054
5055 #if 1
5056   GameEnd();
5057 #endif
5058 }
5059
5060 void GameEnd()
5061 {
5062   int hi_pos;
5063   boolean raise_level = FALSE;
5064
5065   local_player->LevelSolved_GameEnd = TRUE;
5066
5067   CloseDoor(DOOR_CLOSE_1);
5068
5069   if (local_player->LevelSolved_SaveTape)
5070   {
5071 #if 0
5072     TapeStop();
5073 #endif
5074
5075 #if 1
5076     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5077 #else
5078     SaveTape(tape.level_nr);            /* ask to save tape */
5079 #endif
5080   }
5081
5082   if (level_editor_test_game)
5083   {
5084     game_status = GAME_MODE_MAIN;
5085
5086 #if 1
5087     DrawAndFadeInMainMenu(REDRAW_FIELD);
5088 #else
5089     DrawMainMenu();
5090 #endif
5091
5092     return;
5093   }
5094
5095   if (!local_player->LevelSolved_SaveScore)
5096   {
5097 #if 1
5098     FadeOut(REDRAW_FIELD);
5099 #endif
5100
5101     game_status = GAME_MODE_MAIN;
5102
5103     DrawAndFadeInMainMenu(REDRAW_FIELD);
5104
5105     return;
5106   }
5107
5108   if (level_nr == leveldir_current->handicap_level)
5109   {
5110     leveldir_current->handicap_level++;
5111
5112     SaveLevelSetup_SeriesInfo();
5113   }
5114
5115   if (level_nr < leveldir_current->last_level)
5116     raise_level = TRUE;                 /* advance to next level */
5117
5118   if ((hi_pos = NewHiScore()) >= 0) 
5119   {
5120     game_status = GAME_MODE_SCORES;
5121
5122     DrawHallOfFame(hi_pos);
5123
5124     if (raise_level)
5125     {
5126       level_nr++;
5127       TapeErase();
5128     }
5129   }
5130   else
5131   {
5132 #if 1
5133     FadeOut(REDRAW_FIELD);
5134 #endif
5135
5136     game_status = GAME_MODE_MAIN;
5137
5138     if (raise_level)
5139     {
5140       level_nr++;
5141       TapeErase();
5142     }
5143
5144     DrawAndFadeInMainMenu(REDRAW_FIELD);
5145   }
5146 }
5147
5148 int NewHiScore()
5149 {
5150   int k, l;
5151   int position = -1;
5152
5153   LoadScore(level_nr);
5154
5155   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5156       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5157     return -1;
5158
5159   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5160   {
5161     if (local_player->score_final > highscore[k].Score)
5162     {
5163       /* player has made it to the hall of fame */
5164
5165       if (k < MAX_SCORE_ENTRIES - 1)
5166       {
5167         int m = MAX_SCORE_ENTRIES - 1;
5168
5169 #ifdef ONE_PER_NAME
5170         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5171           if (strEqual(setup.player_name, highscore[l].Name))
5172             m = l;
5173         if (m == k)     /* player's new highscore overwrites his old one */
5174           goto put_into_list;
5175 #endif
5176
5177         for (l = m; l > k; l--)
5178         {
5179           strcpy(highscore[l].Name, highscore[l - 1].Name);
5180           highscore[l].Score = highscore[l - 1].Score;
5181         }
5182       }
5183
5184 #ifdef ONE_PER_NAME
5185       put_into_list:
5186 #endif
5187       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5188       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5189       highscore[k].Score = local_player->score_final; 
5190       position = k;
5191       break;
5192     }
5193
5194 #ifdef ONE_PER_NAME
5195     else if (!strncmp(setup.player_name, highscore[k].Name,
5196                       MAX_PLAYER_NAME_LEN))
5197       break;    /* player already there with a higher score */
5198 #endif
5199
5200   }
5201
5202   if (position >= 0) 
5203     SaveScore(level_nr);
5204
5205   return position;
5206 }
5207
5208 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5209 {
5210   int element = Feld[x][y];
5211   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5212   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5213   int horiz_move = (dx != 0);
5214   int sign = (horiz_move ? dx : dy);
5215   int step = sign * element_info[element].move_stepsize;
5216
5217   /* special values for move stepsize for spring and things on conveyor belt */
5218   if (horiz_move)
5219   {
5220     if (CAN_FALL(element) &&
5221         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5222       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5223     else if (element == EL_SPRING)
5224       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5225   }
5226
5227   return step;
5228 }
5229
5230 inline static int getElementMoveStepsize(int x, int y)
5231 {
5232   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5233 }
5234
5235 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5236 {
5237   if (player->GfxAction != action || player->GfxDir != dir)
5238   {
5239 #if 0
5240     printf("Player frame reset! (%d => %d, %d => %d)\n",
5241            player->GfxAction, action, player->GfxDir, dir);
5242 #endif
5243
5244     player->GfxAction = action;
5245     player->GfxDir = dir;
5246     player->Frame = 0;
5247     player->StepFrame = 0;
5248   }
5249 }
5250
5251 #if USE_GFX_RESET_GFX_ANIMATION
5252 static void ResetGfxFrame(int x, int y, boolean redraw)
5253 {
5254   int element = Feld[x][y];
5255   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5256   int last_gfx_frame = GfxFrame[x][y];
5257
5258   if (graphic_info[graphic].anim_global_sync)
5259     GfxFrame[x][y] = FrameCounter;
5260   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5261     GfxFrame[x][y] = CustomValue[x][y];
5262   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5263     GfxFrame[x][y] = element_info[element].collect_score;
5264   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5265     GfxFrame[x][y] = ChangeDelay[x][y];
5266
5267   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5268     DrawLevelGraphicAnimation(x, y, graphic);
5269 }
5270 #endif
5271
5272 static void ResetGfxAnimation(int x, int y)
5273 {
5274   GfxAction[x][y] = ACTION_DEFAULT;
5275   GfxDir[x][y] = MovDir[x][y];
5276   GfxFrame[x][y] = 0;
5277
5278 #if USE_GFX_RESET_GFX_ANIMATION
5279   ResetGfxFrame(x, y, FALSE);
5280 #endif
5281 }
5282
5283 static void ResetRandomAnimationValue(int x, int y)
5284 {
5285   GfxRandom[x][y] = INIT_GFX_RANDOM();
5286 }
5287
5288 void InitMovingField(int x, int y, int direction)
5289 {
5290   int element = Feld[x][y];
5291   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5292   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5293   int newx = x + dx;
5294   int newy = y + dy;
5295   boolean is_moving_before, is_moving_after;
5296 #if 0
5297   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5298 #endif
5299
5300   /* check if element was/is moving or being moved before/after mode change */
5301 #if 1
5302 #if 1
5303   is_moving_before = (WasJustMoving[x][y] != 0);
5304 #else
5305   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5306   is_moving_before = WasJustMoving[x][y];
5307 #endif
5308 #else
5309   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5310 #endif
5311   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5312
5313   /* reset animation only for moving elements which change direction of moving
5314      or which just started or stopped moving
5315      (else CEs with property "can move" / "not moving" are reset each frame) */
5316 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5317 #if 1
5318   if (is_moving_before != is_moving_after ||
5319       direction != MovDir[x][y])
5320     ResetGfxAnimation(x, y);
5321 #else
5322   if ((is_moving_before || is_moving_after) && !continues_moving)
5323     ResetGfxAnimation(x, y);
5324 #endif
5325 #else
5326   if (!continues_moving)
5327     ResetGfxAnimation(x, y);
5328 #endif
5329
5330   MovDir[x][y] = direction;
5331   GfxDir[x][y] = direction;
5332
5333 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5334   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5335                      direction == MV_DOWN && CAN_FALL(element) ?
5336                      ACTION_FALLING : ACTION_MOVING);
5337 #else
5338   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5339                      ACTION_FALLING : ACTION_MOVING);
5340 #endif
5341
5342   /* this is needed for CEs with property "can move" / "not moving" */
5343
5344   if (is_moving_after)
5345   {
5346     if (Feld[newx][newy] == EL_EMPTY)
5347       Feld[newx][newy] = EL_BLOCKED;
5348
5349     MovDir[newx][newy] = MovDir[x][y];
5350
5351 #if USE_NEW_CUSTOM_VALUE
5352     CustomValue[newx][newy] = CustomValue[x][y];
5353 #endif
5354
5355     GfxFrame[newx][newy] = GfxFrame[x][y];
5356     GfxRandom[newx][newy] = GfxRandom[x][y];
5357     GfxAction[newx][newy] = GfxAction[x][y];
5358     GfxDir[newx][newy] = GfxDir[x][y];
5359   }
5360 }
5361
5362 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5363 {
5364   int direction = MovDir[x][y];
5365   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5366   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5367
5368   *goes_to_x = newx;
5369   *goes_to_y = newy;
5370 }
5371
5372 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5373 {
5374   int oldx = x, oldy = y;
5375   int direction = MovDir[x][y];
5376
5377   if (direction == MV_LEFT)
5378     oldx++;
5379   else if (direction == MV_RIGHT)
5380     oldx--;
5381   else if (direction == MV_UP)
5382     oldy++;
5383   else if (direction == MV_DOWN)
5384     oldy--;
5385
5386   *comes_from_x = oldx;
5387   *comes_from_y = oldy;
5388 }
5389
5390 int MovingOrBlocked2Element(int x, int y)
5391 {
5392   int element = Feld[x][y];
5393
5394   if (element == EL_BLOCKED)
5395   {
5396     int oldx, oldy;
5397
5398     Blocked2Moving(x, y, &oldx, &oldy);
5399     return Feld[oldx][oldy];
5400   }
5401   else
5402     return element;
5403 }
5404
5405 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5406 {
5407   /* like MovingOrBlocked2Element(), but if element is moving
5408      and (x,y) is the field the moving element is just leaving,
5409      return EL_BLOCKED instead of the element value */
5410   int element = Feld[x][y];
5411
5412   if (IS_MOVING(x, y))
5413   {
5414     if (element == EL_BLOCKED)
5415     {
5416       int oldx, oldy;
5417
5418       Blocked2Moving(x, y, &oldx, &oldy);
5419       return Feld[oldx][oldy];
5420     }
5421     else
5422       return EL_BLOCKED;
5423   }
5424   else
5425     return element;
5426 }
5427
5428 static void RemoveField(int x, int y)
5429 {
5430   Feld[x][y] = EL_EMPTY;
5431
5432   MovPos[x][y] = 0;
5433   MovDir[x][y] = 0;
5434   MovDelay[x][y] = 0;
5435
5436 #if USE_NEW_CUSTOM_VALUE
5437   CustomValue[x][y] = 0;
5438 #endif
5439
5440   AmoebaNr[x][y] = 0;
5441   ChangeDelay[x][y] = 0;
5442   ChangePage[x][y] = -1;
5443   Pushed[x][y] = FALSE;
5444
5445 #if 0
5446   ExplodeField[x][y] = EX_TYPE_NONE;
5447 #endif
5448
5449   GfxElement[x][y] = EL_UNDEFINED;
5450   GfxAction[x][y] = ACTION_DEFAULT;
5451   GfxDir[x][y] = MV_NONE;
5452 #if 0
5453   /* !!! this would prevent the removed tile from being redrawn !!! */
5454   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5455 #endif
5456 }
5457
5458 void RemoveMovingField(int x, int y)
5459 {
5460   int oldx = x, oldy = y, newx = x, newy = y;
5461   int element = Feld[x][y];
5462   int next_element = EL_UNDEFINED;
5463
5464   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5465     return;
5466
5467   if (IS_MOVING(x, y))
5468   {
5469     Moving2Blocked(x, y, &newx, &newy);
5470
5471     if (Feld[newx][newy] != EL_BLOCKED)
5472     {
5473       /* element is moving, but target field is not free (blocked), but
5474          already occupied by something different (example: acid pool);
5475          in this case, only remove the moving field, but not the target */
5476
5477       RemoveField(oldx, oldy);
5478
5479       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5480
5481       TEST_DrawLevelField(oldx, oldy);
5482
5483       return;
5484     }
5485   }
5486   else if (element == EL_BLOCKED)
5487   {
5488     Blocked2Moving(x, y, &oldx, &oldy);
5489     if (!IS_MOVING(oldx, oldy))
5490       return;
5491   }
5492
5493   if (element == EL_BLOCKED &&
5494       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5495        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5496        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5497        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5498        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5499        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5500     next_element = get_next_element(Feld[oldx][oldy]);
5501
5502   RemoveField(oldx, oldy);
5503   RemoveField(newx, newy);
5504
5505   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5506
5507   if (next_element != EL_UNDEFINED)
5508     Feld[oldx][oldy] = next_element;
5509
5510   TEST_DrawLevelField(oldx, oldy);
5511   TEST_DrawLevelField(newx, newy);
5512 }
5513
5514 void DrawDynamite(int x, int y)
5515 {
5516   int sx = SCREENX(x), sy = SCREENY(y);
5517   int graphic = el2img(Feld[x][y]);
5518   int frame;
5519
5520   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5521     return;
5522
5523   if (IS_WALKABLE_INSIDE(Back[x][y]))
5524     return;
5525
5526   if (Back[x][y])
5527     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5528   else if (Store[x][y])
5529     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5530
5531   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5532
5533   if (Back[x][y] || Store[x][y])
5534     DrawGraphicThruMask(sx, sy, graphic, frame);
5535   else
5536     DrawGraphic(sx, sy, graphic, frame);
5537 }
5538
5539 void CheckDynamite(int x, int y)
5540 {
5541   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5542   {
5543     MovDelay[x][y]--;
5544
5545     if (MovDelay[x][y] != 0)
5546     {
5547       DrawDynamite(x, y);
5548       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5549
5550       return;
5551     }
5552   }
5553
5554   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5555
5556   Bang(x, y);
5557 }
5558
5559 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5560 {
5561   boolean num_checked_players = 0;
5562   int i;
5563
5564   for (i = 0; i < MAX_PLAYERS; i++)
5565   {
5566     if (stored_player[i].active)
5567     {
5568       int sx = stored_player[i].jx;
5569       int sy = stored_player[i].jy;
5570
5571       if (num_checked_players == 0)
5572       {
5573         *sx1 = *sx2 = sx;
5574         *sy1 = *sy2 = sy;
5575       }
5576       else
5577       {
5578         *sx1 = MIN(*sx1, sx);
5579         *sy1 = MIN(*sy1, sy);
5580         *sx2 = MAX(*sx2, sx);
5581         *sy2 = MAX(*sy2, sy);
5582       }
5583
5584       num_checked_players++;
5585     }
5586   }
5587 }
5588
5589 static boolean checkIfAllPlayersFitToScreen_RND()
5590 {
5591   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5592
5593   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5594
5595   return (sx2 - sx1 < SCR_FIELDX &&
5596           sy2 - sy1 < SCR_FIELDY);
5597 }
5598
5599 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5600 {
5601   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5602
5603   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5604
5605   *sx = (sx1 + sx2) / 2;
5606   *sy = (sy1 + sy2) / 2;
5607 }
5608
5609 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5610                         boolean center_screen, boolean quick_relocation)
5611 {
5612   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5613   boolean no_delay = (tape.warp_forward);
5614   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5615   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5616
5617   if (quick_relocation)
5618   {
5619     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5620     {
5621       if (!level.shifted_relocation || center_screen)
5622       {
5623         /* quick relocation (without scrolling), with centering of screen */
5624
5625         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5626                     x > SBX_Right + MIDPOSX ? SBX_Right :
5627                     x - MIDPOSX);
5628
5629         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5630                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5631                     y - MIDPOSY);
5632       }
5633       else
5634       {
5635         /* quick relocation (without scrolling), but do not center screen */
5636
5637         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5638                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5639                                old_x - MIDPOSX);
5640
5641         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643                                old_y - MIDPOSY);
5644
5645         int offset_x = x + (scroll_x - center_scroll_x);
5646         int offset_y = y + (scroll_y - center_scroll_y);
5647
5648         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5649                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5650                     offset_x - MIDPOSX);
5651
5652         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5653                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5654                     offset_y - MIDPOSY);
5655       }
5656     }
5657     else
5658     {
5659 #if 1
5660       if (!level.shifted_relocation || center_screen)
5661       {
5662         /* quick relocation (without scrolling), with centering of screen */
5663
5664         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5665                     x > SBX_Right + MIDPOSX ? SBX_Right :
5666                     x - MIDPOSX);
5667
5668         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5669                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5670                     y - MIDPOSY);
5671       }
5672       else
5673       {
5674         /* quick relocation (without scrolling), but do not center screen */
5675
5676         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5677                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5678                                old_x - MIDPOSX);
5679
5680         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5682                                old_y - MIDPOSY);
5683
5684         int offset_x = x + (scroll_x - center_scroll_x);
5685         int offset_y = y + (scroll_y - center_scroll_y);
5686
5687         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5688                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5689                     offset_x - MIDPOSX);
5690
5691         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5692                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5693                     offset_y - MIDPOSY);
5694       }
5695 #else
5696       /* quick relocation (without scrolling), inside visible screen area */
5697
5698       int offset = game.scroll_delay_value;
5699
5700       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5701           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5702         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5703
5704       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5705           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5706         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5707
5708       /* don't scroll over playfield boundaries */
5709       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5710         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5711
5712       /* don't scroll over playfield boundaries */
5713       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5714         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5715 #endif
5716     }
5717
5718     RedrawPlayfield(TRUE, 0,0,0,0);
5719   }
5720   else
5721   {
5722 #if 1
5723     int scroll_xx, scroll_yy;
5724
5725     if (!level.shifted_relocation || center_screen)
5726     {
5727       /* visible relocation (with scrolling), with centering of screen */
5728
5729       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5730                    x > SBX_Right + MIDPOSX ? SBX_Right :
5731                    x - MIDPOSX);
5732
5733       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5734                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5735                    y - MIDPOSY);
5736     }
5737     else
5738     {
5739       /* visible relocation (with scrolling), but do not center screen */
5740
5741       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5742                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5743                              old_x - MIDPOSX);
5744
5745       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5747                              old_y - MIDPOSY);
5748
5749       int offset_x = x + (scroll_x - center_scroll_x);
5750       int offset_y = y + (scroll_y - center_scroll_y);
5751
5752       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5753                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5754                    offset_x - MIDPOSX);
5755
5756       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5757                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5758                    offset_y - MIDPOSY);
5759     }
5760
5761 #else
5762
5763     /* visible relocation (with scrolling), with centering of screen */
5764
5765     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5766                      x > SBX_Right + MIDPOSX ? SBX_Right :
5767                      x - MIDPOSX);
5768
5769     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5770                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5771                      y - MIDPOSY);
5772 #endif
5773
5774     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5775
5776     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5777     {
5778       int dx = 0, dy = 0;
5779       int fx = FX, fy = FY;
5780
5781       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5782       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5783
5784       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5785         break;
5786
5787       scroll_x -= dx;
5788       scroll_y -= dy;
5789
5790       fx += dx * TILEX / 2;
5791       fy += dy * TILEY / 2;
5792
5793       ScrollLevel(dx, dy);
5794       DrawAllPlayers();
5795
5796       /* scroll in two steps of half tile size to make things smoother */
5797       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5798       FlushDisplay();
5799       Delay(wait_delay_value);
5800
5801       /* scroll second step to align at full tile size */
5802       BackToFront();
5803       Delay(wait_delay_value);
5804     }
5805
5806     DrawAllPlayers();
5807     BackToFront();
5808     Delay(wait_delay_value);
5809   }
5810 }
5811
5812 void RelocatePlayer(int jx, int jy, int el_player_raw)
5813 {
5814   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5815   int player_nr = GET_PLAYER_NR(el_player);
5816   struct PlayerInfo *player = &stored_player[player_nr];
5817   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5818   boolean no_delay = (tape.warp_forward);
5819   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5820   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5821   int old_jx = player->jx;
5822   int old_jy = player->jy;
5823   int old_element = Feld[old_jx][old_jy];
5824   int element = Feld[jx][jy];
5825   boolean player_relocated = (old_jx != jx || old_jy != jy);
5826
5827   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5828   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5829   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5830   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5831   int leave_side_horiz = move_dir_horiz;
5832   int leave_side_vert  = move_dir_vert;
5833   int enter_side = enter_side_horiz | enter_side_vert;
5834   int leave_side = leave_side_horiz | leave_side_vert;
5835
5836   if (player->GameOver)         /* do not reanimate dead player */
5837     return;
5838
5839   if (!player_relocated)        /* no need to relocate the player */
5840     return;
5841
5842   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5843   {
5844     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5845     DrawLevelField(jx, jy);
5846   }
5847
5848   if (player->present)
5849   {
5850     while (player->MovPos)
5851     {
5852       ScrollPlayer(player, SCROLL_GO_ON);
5853       ScrollScreen(NULL, SCROLL_GO_ON);
5854
5855       AdvanceFrameAndPlayerCounters(player->index_nr);
5856
5857       DrawPlayer(player);
5858
5859       BackToFront();
5860       Delay(wait_delay_value);
5861     }
5862
5863     DrawPlayer(player);         /* needed here only to cleanup last field */
5864     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5865
5866     player->is_moving = FALSE;
5867   }
5868
5869   if (IS_CUSTOM_ELEMENT(old_element))
5870     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5871                                CE_LEFT_BY_PLAYER,
5872                                player->index_bit, leave_side);
5873
5874   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5875                                       CE_PLAYER_LEAVES_X,
5876                                       player->index_bit, leave_side);
5877
5878   Feld[jx][jy] = el_player;
5879   InitPlayerField(jx, jy, el_player, TRUE);
5880
5881   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5882      possible that the relocation target field did not contain a player element,
5883      but a walkable element, to which the new player was relocated -- in this
5884      case, restore that (already initialized!) element on the player field */
5885   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5886   {
5887     Feld[jx][jy] = element;     /* restore previously existing element */
5888 #if 0
5889     /* !!! do not initialize already initialized element a second time !!! */
5890     /* (this causes at least problems with "element creation" CE trigger for
5891        already existing elements, and existing Sokoban fields counted twice) */
5892     InitField(jx, jy, FALSE);
5893 #endif
5894   }
5895
5896   /* only visually relocate centered player */
5897   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5898                      FALSE, level.instant_relocation);
5899
5900   TestIfPlayerTouchesBadThing(jx, jy);
5901   TestIfPlayerTouchesCustomElement(jx, jy);
5902
5903   if (IS_CUSTOM_ELEMENT(element))
5904     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5905                                player->index_bit, enter_side);
5906
5907   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5908                                       player->index_bit, enter_side);
5909
5910 #if 1
5911   if (player->is_switching)
5912   {
5913     /* ensure that relocation while still switching an element does not cause
5914        a new element to be treated as also switched directly after relocation
5915        (this is important for teleporter switches that teleport the player to
5916        a place where another teleporter switch is in the same direction, which
5917        would then incorrectly be treated as immediately switched before the
5918        direction key that caused the switch was released) */
5919
5920     player->switch_x += jx - old_jx;
5921     player->switch_y += jy - old_jy;
5922   }
5923 #endif
5924 }
5925
5926 void Explode(int ex, int ey, int phase, int mode)
5927 {
5928   int x, y;
5929   int last_phase;
5930   int border_element;
5931
5932   /* !!! eliminate this variable !!! */
5933   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5934
5935   if (game.explosions_delayed)
5936   {
5937     ExplodeField[ex][ey] = mode;
5938     return;
5939   }
5940
5941   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5942   {
5943     int center_element = Feld[ex][ey];
5944     int artwork_element, explosion_element;     /* set these values later */
5945
5946 #if 0
5947     /* --- This is only really needed (and now handled) in "Impact()". --- */
5948     /* do not explode moving elements that left the explode field in time */
5949     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5950         center_element == EL_EMPTY &&
5951         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5952       return;
5953 #endif
5954
5955 #if 0
5956     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5957     if (mode == EX_TYPE_NORMAL ||
5958         mode == EX_TYPE_CENTER ||
5959         mode == EX_TYPE_CROSS)
5960       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5961 #endif
5962
5963     /* remove things displayed in background while burning dynamite */
5964     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5965       Back[ex][ey] = 0;
5966
5967     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5968     {
5969       /* put moving element to center field (and let it explode there) */
5970       center_element = MovingOrBlocked2Element(ex, ey);
5971       RemoveMovingField(ex, ey);
5972       Feld[ex][ey] = center_element;
5973     }
5974
5975     /* now "center_element" is finally determined -- set related values now */
5976     artwork_element = center_element;           /* for custom player artwork */
5977     explosion_element = center_element;         /* for custom player artwork */
5978
5979     if (IS_PLAYER(ex, ey))
5980     {
5981       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5982
5983       artwork_element = stored_player[player_nr].artwork_element;
5984
5985       if (level.use_explosion_element[player_nr])
5986       {
5987         explosion_element = level.explosion_element[player_nr];
5988         artwork_element = explosion_element;
5989       }
5990     }
5991
5992 #if 1
5993     if (mode == EX_TYPE_NORMAL ||
5994         mode == EX_TYPE_CENTER ||
5995         mode == EX_TYPE_CROSS)
5996       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5997 #endif
5998
5999     last_phase = element_info[explosion_element].explosion_delay + 1;
6000
6001     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6002     {
6003       int xx = x - ex + 1;
6004       int yy = y - ey + 1;
6005       int element;
6006
6007       if (!IN_LEV_FIELD(x, y) ||
6008           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6009           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6010         continue;
6011
6012       element = Feld[x][y];
6013
6014       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6015       {
6016         element = MovingOrBlocked2Element(x, y);
6017
6018         if (!IS_EXPLOSION_PROOF(element))
6019           RemoveMovingField(x, y);
6020       }
6021
6022       /* indestructible elements can only explode in center (but not flames) */
6023       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6024                                            mode == EX_TYPE_BORDER)) ||
6025           element == EL_FLAMES)
6026         continue;
6027
6028       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6029          behaviour, for example when touching a yamyam that explodes to rocks
6030          with active deadly shield, a rock is created under the player !!! */
6031       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6032 #if 0
6033       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6034           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6035            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6036 #else
6037       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6038 #endif
6039       {
6040         if (IS_ACTIVE_BOMB(element))
6041         {
6042           /* re-activate things under the bomb like gate or penguin */
6043           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6044           Back[x][y] = 0;
6045         }
6046
6047         continue;
6048       }
6049
6050       /* save walkable background elements while explosion on same tile */
6051       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6052           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6053         Back[x][y] = element;
6054
6055       /* ignite explodable elements reached by other explosion */
6056       if (element == EL_EXPLOSION)
6057         element = Store2[x][y];
6058
6059       if (AmoebaNr[x][y] &&
6060           (element == EL_AMOEBA_FULL ||
6061            element == EL_BD_AMOEBA ||
6062            element == EL_AMOEBA_GROWING))
6063       {
6064         AmoebaCnt[AmoebaNr[x][y]]--;
6065         AmoebaCnt2[AmoebaNr[x][y]]--;
6066       }
6067
6068       RemoveField(x, y);
6069
6070       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6071       {
6072         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6073
6074         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6075
6076         if (PLAYERINFO(ex, ey)->use_murphy)
6077           Store[x][y] = EL_EMPTY;
6078       }
6079
6080       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6081          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6082       else if (ELEM_IS_PLAYER(center_element))
6083         Store[x][y] = EL_EMPTY;
6084       else if (center_element == EL_YAMYAM)
6085         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6086       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6087         Store[x][y] = element_info[center_element].content.e[xx][yy];
6088 #if 1
6089       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6090          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6091          otherwise) -- FIX THIS !!! */
6092       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6093         Store[x][y] = element_info[element].content.e[1][1];
6094 #else
6095       else if (!CAN_EXPLODE(element))
6096         Store[x][y] = element_info[element].content.e[1][1];
6097 #endif
6098       else
6099         Store[x][y] = EL_EMPTY;
6100
6101       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6102           center_element == EL_AMOEBA_TO_DIAMOND)
6103         Store2[x][y] = element;
6104
6105       Feld[x][y] = EL_EXPLOSION;
6106       GfxElement[x][y] = artwork_element;
6107
6108       ExplodePhase[x][y] = 1;
6109       ExplodeDelay[x][y] = last_phase;
6110
6111       Stop[x][y] = TRUE;
6112     }
6113
6114     if (center_element == EL_YAMYAM)
6115       game.yamyam_content_nr =
6116         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6117
6118     return;
6119   }
6120
6121   if (Stop[ex][ey])
6122     return;
6123
6124   x = ex;
6125   y = ey;
6126
6127   if (phase == 1)
6128     GfxFrame[x][y] = 0;         /* restart explosion animation */
6129
6130   last_phase = ExplodeDelay[x][y];
6131
6132   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6133
6134 #ifdef DEBUG
6135
6136   /* activate this even in non-DEBUG version until cause for crash in
6137      getGraphicAnimationFrame() (see below) is found and eliminated */
6138
6139 #endif
6140 #if 1
6141
6142 #if 1
6143   /* this can happen if the player leaves an explosion just in time */
6144   if (GfxElement[x][y] == EL_UNDEFINED)
6145     GfxElement[x][y] = EL_EMPTY;
6146 #else
6147   if (GfxElement[x][y] == EL_UNDEFINED)
6148   {
6149     printf("\n\n");
6150     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6151     printf("Explode(): This should never happen!\n");
6152     printf("\n\n");
6153
6154     GfxElement[x][y] = EL_EMPTY;
6155   }
6156 #endif
6157
6158 #endif
6159
6160   border_element = Store2[x][y];
6161   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6162     border_element = StorePlayer[x][y];
6163
6164   if (phase == element_info[border_element].ignition_delay ||
6165       phase == last_phase)
6166   {
6167     boolean border_explosion = FALSE;
6168
6169     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6170         !PLAYER_EXPLOSION_PROTECTED(x, y))
6171     {
6172       KillPlayerUnlessExplosionProtected(x, y);
6173       border_explosion = TRUE;
6174     }
6175     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6176     {
6177       Feld[x][y] = Store2[x][y];
6178       Store2[x][y] = 0;
6179       Bang(x, y);
6180       border_explosion = TRUE;
6181     }
6182     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6183     {
6184       AmoebeUmwandeln(x, y);
6185       Store2[x][y] = 0;
6186       border_explosion = TRUE;
6187     }
6188
6189     /* if an element just explodes due to another explosion (chain-reaction),
6190        do not immediately end the new explosion when it was the last frame of
6191        the explosion (as it would be done in the following "if"-statement!) */
6192     if (border_explosion && phase == last_phase)
6193       return;
6194   }
6195
6196   if (phase == last_phase)
6197   {
6198     int element;
6199
6200     element = Feld[x][y] = Store[x][y];
6201     Store[x][y] = Store2[x][y] = 0;
6202     GfxElement[x][y] = EL_UNDEFINED;
6203
6204     /* player can escape from explosions and might therefore be still alive */
6205     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6206         element <= EL_PLAYER_IS_EXPLODING_4)
6207     {
6208       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6209       int explosion_element = EL_PLAYER_1 + player_nr;
6210       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6211       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6212
6213       if (level.use_explosion_element[player_nr])
6214         explosion_element = level.explosion_element[player_nr];
6215
6216       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6217                     element_info[explosion_element].content.e[xx][yy]);
6218     }
6219
6220     /* restore probably existing indestructible background element */
6221     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6222       element = Feld[x][y] = Back[x][y];
6223     Back[x][y] = 0;
6224
6225     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6226     GfxDir[x][y] = MV_NONE;
6227     ChangeDelay[x][y] = 0;
6228     ChangePage[x][y] = -1;
6229
6230 #if USE_NEW_CUSTOM_VALUE
6231     CustomValue[x][y] = 0;
6232 #endif
6233
6234     InitField_WithBug2(x, y, FALSE);
6235
6236     TEST_DrawLevelField(x, y);
6237
6238     TestIfElementTouchesCustomElement(x, y);
6239
6240     if (GFX_CRUMBLED(element))
6241       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6242
6243     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6244       StorePlayer[x][y] = 0;
6245
6246     if (ELEM_IS_PLAYER(element))
6247       RelocatePlayer(x, y, element);
6248   }
6249   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6250   {
6251     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6252     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6253
6254     if (phase == delay)
6255       TEST_DrawLevelFieldCrumbled(x, y);
6256
6257     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6258     {
6259       DrawLevelElement(x, y, Back[x][y]);
6260       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6261     }
6262     else if (IS_WALKABLE_UNDER(Back[x][y]))
6263     {
6264       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6265       DrawLevelElementThruMask(x, y, Back[x][y]);
6266     }
6267     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6268       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6269   }
6270 }
6271
6272 void DynaExplode(int ex, int ey)
6273 {
6274   int i, j;
6275   int dynabomb_element = Feld[ex][ey];
6276   int dynabomb_size = 1;
6277   boolean dynabomb_xl = FALSE;
6278   struct PlayerInfo *player;
6279   static int xy[4][2] =
6280   {
6281     { 0, -1 },
6282     { -1, 0 },
6283     { +1, 0 },
6284     { 0, +1 }
6285   };
6286
6287   if (IS_ACTIVE_BOMB(dynabomb_element))
6288   {
6289     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6290     dynabomb_size = player->dynabomb_size;
6291     dynabomb_xl = player->dynabomb_xl;
6292     player->dynabombs_left++;
6293   }
6294
6295   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6296
6297   for (i = 0; i < NUM_DIRECTIONS; i++)
6298   {
6299     for (j = 1; j <= dynabomb_size; j++)
6300     {
6301       int x = ex + j * xy[i][0];
6302       int y = ey + j * xy[i][1];
6303       int element;
6304
6305       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6306         break;
6307
6308       element = Feld[x][y];
6309
6310       /* do not restart explosions of fields with active bombs */
6311       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6312         continue;
6313
6314       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6315
6316       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6317           !IS_DIGGABLE(element) && !dynabomb_xl)
6318         break;
6319     }
6320   }
6321 }
6322
6323 void Bang(int x, int y)
6324 {
6325   int element = MovingOrBlocked2Element(x, y);
6326   int explosion_type = EX_TYPE_NORMAL;
6327
6328   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6329   {
6330     struct PlayerInfo *player = PLAYERINFO(x, y);
6331
6332 #if USE_FIX_CE_ACTION_WITH_PLAYER
6333     element = Feld[x][y] = player->initial_element;
6334 #else
6335     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6336                             player->element_nr);
6337 #endif
6338
6339     if (level.use_explosion_element[player->index_nr])
6340     {
6341       int explosion_element = level.explosion_element[player->index_nr];
6342
6343       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6344         explosion_type = EX_TYPE_CROSS;
6345       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6346         explosion_type = EX_TYPE_CENTER;
6347     }
6348   }
6349
6350   switch (element)
6351   {
6352     case EL_BUG:
6353     case EL_SPACESHIP:
6354     case EL_BD_BUTTERFLY:
6355     case EL_BD_FIREFLY:
6356     case EL_YAMYAM:
6357     case EL_DARK_YAMYAM:
6358     case EL_ROBOT:
6359     case EL_PACMAN:
6360     case EL_MOLE:
6361       RaiseScoreElement(element);
6362       break;
6363
6364     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6365     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6366     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6367     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6368     case EL_DYNABOMB_INCREASE_NUMBER:
6369     case EL_DYNABOMB_INCREASE_SIZE:
6370     case EL_DYNABOMB_INCREASE_POWER:
6371       explosion_type = EX_TYPE_DYNA;
6372       break;
6373
6374     case EL_DC_LANDMINE:
6375 #if 0
6376     case EL_EM_EXIT_OPEN:
6377     case EL_EM_STEEL_EXIT_OPEN:
6378 #endif
6379       explosion_type = EX_TYPE_CENTER;
6380       break;
6381
6382     case EL_PENGUIN:
6383     case EL_LAMP:
6384     case EL_LAMP_ACTIVE:
6385     case EL_AMOEBA_TO_DIAMOND:
6386       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6387         explosion_type = EX_TYPE_CENTER;
6388       break;
6389
6390     default:
6391       if (element_info[element].explosion_type == EXPLODES_CROSS)
6392         explosion_type = EX_TYPE_CROSS;
6393       else if (element_info[element].explosion_type == EXPLODES_1X1)
6394         explosion_type = EX_TYPE_CENTER;
6395       break;
6396   }
6397
6398   if (explosion_type == EX_TYPE_DYNA)
6399     DynaExplode(x, y);
6400   else
6401     Explode(x, y, EX_PHASE_START, explosion_type);
6402
6403   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6404 }
6405
6406 void SplashAcid(int x, int y)
6407 {
6408   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6409       (!IN_LEV_FIELD(x - 1, y - 2) ||
6410        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6411     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6412
6413   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6414       (!IN_LEV_FIELD(x + 1, y - 2) ||
6415        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6416     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6417
6418   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6419 }
6420
6421 static void InitBeltMovement()
6422 {
6423   static int belt_base_element[4] =
6424   {
6425     EL_CONVEYOR_BELT_1_LEFT,
6426     EL_CONVEYOR_BELT_2_LEFT,
6427     EL_CONVEYOR_BELT_3_LEFT,
6428     EL_CONVEYOR_BELT_4_LEFT
6429   };
6430   static int belt_base_active_element[4] =
6431   {
6432     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6433     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6434     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6435     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6436   };
6437
6438   int x, y, i, j;
6439
6440   /* set frame order for belt animation graphic according to belt direction */
6441   for (i = 0; i < NUM_BELTS; i++)
6442   {
6443     int belt_nr = i;
6444
6445     for (j = 0; j < NUM_BELT_PARTS; j++)
6446     {
6447       int element = belt_base_active_element[belt_nr] + j;
6448       int graphic_1 = el2img(element);
6449       int graphic_2 = el2panelimg(element);
6450
6451       if (game.belt_dir[i] == MV_LEFT)
6452       {
6453         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6454         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6455       }
6456       else
6457       {
6458         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6459         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6460       }
6461     }
6462   }
6463
6464   SCAN_PLAYFIELD(x, y)
6465   {
6466     int element = Feld[x][y];
6467
6468     for (i = 0; i < NUM_BELTS; i++)
6469     {
6470       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6471       {
6472         int e_belt_nr = getBeltNrFromBeltElement(element);
6473         int belt_nr = i;
6474
6475         if (e_belt_nr == belt_nr)
6476         {
6477           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6478
6479           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6480         }
6481       }
6482     }
6483   }
6484 }
6485
6486 static void ToggleBeltSwitch(int x, int y)
6487 {
6488   static int belt_base_element[4] =
6489   {
6490     EL_CONVEYOR_BELT_1_LEFT,
6491     EL_CONVEYOR_BELT_2_LEFT,
6492     EL_CONVEYOR_BELT_3_LEFT,
6493     EL_CONVEYOR_BELT_4_LEFT
6494   };
6495   static int belt_base_active_element[4] =
6496   {
6497     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6498     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6499     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6500     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6501   };
6502   static int belt_base_switch_element[4] =
6503   {
6504     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6505     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6506     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6507     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6508   };
6509   static int belt_move_dir[4] =
6510   {
6511     MV_LEFT,
6512     MV_NONE,
6513     MV_RIGHT,
6514     MV_NONE,
6515   };
6516
6517   int element = Feld[x][y];
6518   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6519   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6520   int belt_dir = belt_move_dir[belt_dir_nr];
6521   int xx, yy, i;
6522
6523   if (!IS_BELT_SWITCH(element))
6524     return;
6525
6526   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6527   game.belt_dir[belt_nr] = belt_dir;
6528
6529   if (belt_dir_nr == 3)
6530     belt_dir_nr = 1;
6531
6532   /* set frame order for belt animation graphic according to belt direction */
6533   for (i = 0; i < NUM_BELT_PARTS; i++)
6534   {
6535     int element = belt_base_active_element[belt_nr] + i;
6536     int graphic_1 = el2img(element);
6537     int graphic_2 = el2panelimg(element);
6538
6539     if (belt_dir == MV_LEFT)
6540     {
6541       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6542       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6543     }
6544     else
6545     {
6546       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6547       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6548     }
6549   }
6550
6551   SCAN_PLAYFIELD(xx, yy)
6552   {
6553     int element = Feld[xx][yy];
6554
6555     if (IS_BELT_SWITCH(element))
6556     {
6557       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6558
6559       if (e_belt_nr == belt_nr)
6560       {
6561         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6562         TEST_DrawLevelField(xx, yy);
6563       }
6564     }
6565     else if (IS_BELT(element) && belt_dir != MV_NONE)
6566     {
6567       int e_belt_nr = getBeltNrFromBeltElement(element);
6568
6569       if (e_belt_nr == belt_nr)
6570       {
6571         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6572
6573         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6574         TEST_DrawLevelField(xx, yy);
6575       }
6576     }
6577     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6578     {
6579       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6580
6581       if (e_belt_nr == belt_nr)
6582       {
6583         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6584
6585         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6586         TEST_DrawLevelField(xx, yy);
6587       }
6588     }
6589   }
6590 }
6591
6592 static void ToggleSwitchgateSwitch(int x, int y)
6593 {
6594   int xx, yy;
6595
6596   game.switchgate_pos = !game.switchgate_pos;
6597
6598   SCAN_PLAYFIELD(xx, yy)
6599   {
6600     int element = Feld[xx][yy];
6601
6602 #if !USE_BOTH_SWITCHGATE_SWITCHES
6603     if (element == EL_SWITCHGATE_SWITCH_UP ||
6604         element == EL_SWITCHGATE_SWITCH_DOWN)
6605     {
6606       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6607       TEST_DrawLevelField(xx, yy);
6608     }
6609     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6610              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6611     {
6612       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6613       TEST_DrawLevelField(xx, yy);
6614     }
6615 #else
6616     if (element == EL_SWITCHGATE_SWITCH_UP)
6617     {
6618       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6619       TEST_DrawLevelField(xx, yy);
6620     }
6621     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6622     {
6623       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6624       TEST_DrawLevelField(xx, yy);
6625     }
6626     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6627     {
6628       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6629       TEST_DrawLevelField(xx, yy);
6630     }
6631     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6632     {
6633       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6634       TEST_DrawLevelField(xx, yy);
6635     }
6636 #endif
6637     else if (element == EL_SWITCHGATE_OPEN ||
6638              element == EL_SWITCHGATE_OPENING)
6639     {
6640       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6641
6642       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6643     }
6644     else if (element == EL_SWITCHGATE_CLOSED ||
6645              element == EL_SWITCHGATE_CLOSING)
6646     {
6647       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6648
6649       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6650     }
6651   }
6652 }
6653
6654 static int getInvisibleActiveFromInvisibleElement(int element)
6655 {
6656   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6657           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6658           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6659           element);
6660 }
6661
6662 static int getInvisibleFromInvisibleActiveElement(int element)
6663 {
6664   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6665           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6666           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6667           element);
6668 }
6669
6670 static void RedrawAllLightSwitchesAndInvisibleElements()
6671 {
6672   int x, y;
6673
6674   SCAN_PLAYFIELD(x, y)
6675   {
6676     int element = Feld[x][y];
6677
6678     if (element == EL_LIGHT_SWITCH &&
6679         game.light_time_left > 0)
6680     {
6681       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6682       TEST_DrawLevelField(x, y);
6683     }
6684     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6685              game.light_time_left == 0)
6686     {
6687       Feld[x][y] = EL_LIGHT_SWITCH;
6688       TEST_DrawLevelField(x, y);
6689     }
6690     else if (element == EL_EMC_DRIPPER &&
6691              game.light_time_left > 0)
6692     {
6693       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6697              game.light_time_left == 0)
6698     {
6699       Feld[x][y] = EL_EMC_DRIPPER;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_INVISIBLE_STEELWALL ||
6703              element == EL_INVISIBLE_WALL ||
6704              element == EL_INVISIBLE_SAND)
6705     {
6706       if (game.light_time_left > 0)
6707         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6708
6709       TEST_DrawLevelField(x, y);
6710
6711       /* uncrumble neighbour fields, if needed */
6712       if (element == EL_INVISIBLE_SAND)
6713         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6714     }
6715     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6716              element == EL_INVISIBLE_WALL_ACTIVE ||
6717              element == EL_INVISIBLE_SAND_ACTIVE)
6718     {
6719       if (game.light_time_left == 0)
6720         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6721
6722       TEST_DrawLevelField(x, y);
6723
6724       /* re-crumble neighbour fields, if needed */
6725       if (element == EL_INVISIBLE_SAND)
6726         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6727     }
6728   }
6729 }
6730
6731 static void RedrawAllInvisibleElementsForLenses()
6732 {
6733   int x, y;
6734
6735   SCAN_PLAYFIELD(x, y)
6736   {
6737     int element = Feld[x][y];
6738
6739     if (element == EL_EMC_DRIPPER &&
6740         game.lenses_time_left > 0)
6741     {
6742       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6743       TEST_DrawLevelField(x, y);
6744     }
6745     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6746              game.lenses_time_left == 0)
6747     {
6748       Feld[x][y] = EL_EMC_DRIPPER;
6749       TEST_DrawLevelField(x, y);
6750     }
6751     else if (element == EL_INVISIBLE_STEELWALL ||
6752              element == EL_INVISIBLE_WALL ||
6753              element == EL_INVISIBLE_SAND)
6754     {
6755       if (game.lenses_time_left > 0)
6756         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6757
6758       TEST_DrawLevelField(x, y);
6759
6760       /* uncrumble neighbour fields, if needed */
6761       if (element == EL_INVISIBLE_SAND)
6762         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6763     }
6764     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6765              element == EL_INVISIBLE_WALL_ACTIVE ||
6766              element == EL_INVISIBLE_SAND_ACTIVE)
6767     {
6768       if (game.lenses_time_left == 0)
6769         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6770
6771       TEST_DrawLevelField(x, y);
6772
6773       /* re-crumble neighbour fields, if needed */
6774       if (element == EL_INVISIBLE_SAND)
6775         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6776     }
6777   }
6778 }
6779
6780 static void RedrawAllInvisibleElementsForMagnifier()
6781 {
6782   int x, y;
6783
6784   SCAN_PLAYFIELD(x, y)
6785   {
6786     int element = Feld[x][y];
6787
6788     if (element == EL_EMC_FAKE_GRASS &&
6789         game.magnify_time_left > 0)
6790     {
6791       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6792       TEST_DrawLevelField(x, y);
6793     }
6794     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6795              game.magnify_time_left == 0)
6796     {
6797       Feld[x][y] = EL_EMC_FAKE_GRASS;
6798       TEST_DrawLevelField(x, y);
6799     }
6800     else if (IS_GATE_GRAY(element) &&
6801              game.magnify_time_left > 0)
6802     {
6803       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6804                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6805                     IS_EM_GATE_GRAY(element) ?
6806                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6807                     IS_EMC_GATE_GRAY(element) ?
6808                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6809                     IS_DC_GATE_GRAY(element) ?
6810                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6811                     element);
6812       TEST_DrawLevelField(x, y);
6813     }
6814     else if (IS_GATE_GRAY_ACTIVE(element) &&
6815              game.magnify_time_left == 0)
6816     {
6817       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6818                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6819                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6820                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6821                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6822                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6823                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6824                     EL_DC_GATE_WHITE_GRAY :
6825                     element);
6826       TEST_DrawLevelField(x, y);
6827     }
6828   }
6829 }
6830
6831 static void ToggleLightSwitch(int x, int y)
6832 {
6833   int element = Feld[x][y];
6834
6835   game.light_time_left =
6836     (element == EL_LIGHT_SWITCH ?
6837      level.time_light * FRAMES_PER_SECOND : 0);
6838
6839   RedrawAllLightSwitchesAndInvisibleElements();
6840 }
6841
6842 static void ActivateTimegateSwitch(int x, int y)
6843 {
6844   int xx, yy;
6845
6846   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6847
6848   SCAN_PLAYFIELD(xx, yy)
6849   {
6850     int element = Feld[xx][yy];
6851
6852     if (element == EL_TIMEGATE_CLOSED ||
6853         element == EL_TIMEGATE_CLOSING)
6854     {
6855       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6856       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6857     }
6858
6859     /*
6860     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6861     {
6862       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6863       TEST_DrawLevelField(xx, yy);
6864     }
6865     */
6866
6867   }
6868
6869 #if 1
6870   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6871                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6872 #else
6873   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6874 #endif
6875 }
6876
6877 void Impact(int x, int y)
6878 {
6879   boolean last_line = (y == lev_fieldy - 1);
6880   boolean object_hit = FALSE;
6881   boolean impact = (last_line || object_hit);
6882   int element = Feld[x][y];
6883   int smashed = EL_STEELWALL;
6884
6885   if (!last_line)       /* check if element below was hit */
6886   {
6887     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6888       return;
6889
6890     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6891                                          MovDir[x][y + 1] != MV_DOWN ||
6892                                          MovPos[x][y + 1] <= TILEY / 2));
6893
6894     /* do not smash moving elements that left the smashed field in time */
6895     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6896         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6897       object_hit = FALSE;
6898
6899 #if USE_QUICKSAND_IMPACT_BUGFIX
6900     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6901     {
6902       RemoveMovingField(x, y + 1);
6903       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6904       Feld[x][y + 2] = EL_ROCK;
6905       TEST_DrawLevelField(x, y + 2);
6906
6907       object_hit = TRUE;
6908     }
6909
6910     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6911     {
6912       RemoveMovingField(x, y + 1);
6913       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6914       Feld[x][y + 2] = EL_ROCK;
6915       TEST_DrawLevelField(x, y + 2);
6916
6917       object_hit = TRUE;
6918     }
6919 #endif
6920
6921     if (object_hit)
6922       smashed = MovingOrBlocked2Element(x, y + 1);
6923
6924     impact = (last_line || object_hit);
6925   }
6926
6927   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6928   {
6929     SplashAcid(x, y + 1);
6930     return;
6931   }
6932
6933   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6934   /* only reset graphic animation if graphic really changes after impact */
6935   if (impact &&
6936       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6937   {
6938     ResetGfxAnimation(x, y);
6939     TEST_DrawLevelField(x, y);
6940   }
6941
6942   if (impact && CAN_EXPLODE_IMPACT(element))
6943   {
6944     Bang(x, y);
6945     return;
6946   }
6947   else if (impact && element == EL_PEARL &&
6948            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6949   {
6950     ResetGfxAnimation(x, y);
6951
6952     Feld[x][y] = EL_PEARL_BREAKING;
6953     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6954     return;
6955   }
6956   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6957   {
6958     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6959
6960     return;
6961   }
6962
6963   if (impact && element == EL_AMOEBA_DROP)
6964   {
6965     if (object_hit && IS_PLAYER(x, y + 1))
6966       KillPlayerUnlessEnemyProtected(x, y + 1);
6967     else if (object_hit && smashed == EL_PENGUIN)
6968       Bang(x, y + 1);
6969     else
6970     {
6971       Feld[x][y] = EL_AMOEBA_GROWING;
6972       Store[x][y] = EL_AMOEBA_WET;
6973
6974       ResetRandomAnimationValue(x, y);
6975     }
6976     return;
6977   }
6978
6979   if (object_hit)               /* check which object was hit */
6980   {
6981     if ((CAN_PASS_MAGIC_WALL(element) && 
6982          (smashed == EL_MAGIC_WALL ||
6983           smashed == EL_BD_MAGIC_WALL)) ||
6984         (CAN_PASS_DC_MAGIC_WALL(element) &&
6985          smashed == EL_DC_MAGIC_WALL))
6986     {
6987       int xx, yy;
6988       int activated_magic_wall =
6989         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6990          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6991          EL_DC_MAGIC_WALL_ACTIVE);
6992
6993       /* activate magic wall / mill */
6994       SCAN_PLAYFIELD(xx, yy)
6995       {
6996         if (Feld[xx][yy] == smashed)
6997           Feld[xx][yy] = activated_magic_wall;
6998       }
6999
7000       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7001       game.magic_wall_active = TRUE;
7002
7003       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7004                             SND_MAGIC_WALL_ACTIVATING :
7005                             smashed == EL_BD_MAGIC_WALL ?
7006                             SND_BD_MAGIC_WALL_ACTIVATING :
7007                             SND_DC_MAGIC_WALL_ACTIVATING));
7008     }
7009
7010     if (IS_PLAYER(x, y + 1))
7011     {
7012       if (CAN_SMASH_PLAYER(element))
7013       {
7014         KillPlayerUnlessEnemyProtected(x, y + 1);
7015         return;
7016       }
7017     }
7018     else if (smashed == EL_PENGUIN)
7019     {
7020       if (CAN_SMASH_PLAYER(element))
7021       {
7022         Bang(x, y + 1);
7023         return;
7024       }
7025     }
7026     else if (element == EL_BD_DIAMOND)
7027     {
7028       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7029       {
7030         Bang(x, y + 1);
7031         return;
7032       }
7033     }
7034     else if (((element == EL_SP_INFOTRON ||
7035                element == EL_SP_ZONK) &&
7036               (smashed == EL_SP_SNIKSNAK ||
7037                smashed == EL_SP_ELECTRON ||
7038                smashed == EL_SP_DISK_ORANGE)) ||
7039              (element == EL_SP_INFOTRON &&
7040               smashed == EL_SP_DISK_YELLOW))
7041     {
7042       Bang(x, y + 1);
7043       return;
7044     }
7045     else if (CAN_SMASH_EVERYTHING(element))
7046     {
7047       if (IS_CLASSIC_ENEMY(smashed) ||
7048           CAN_EXPLODE_SMASHED(smashed))
7049       {
7050         Bang(x, y + 1);
7051         return;
7052       }
7053       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7054       {
7055         if (smashed == EL_LAMP ||
7056             smashed == EL_LAMP_ACTIVE)
7057         {
7058           Bang(x, y + 1);
7059           return;
7060         }
7061         else if (smashed == EL_NUT)
7062         {
7063           Feld[x][y + 1] = EL_NUT_BREAKING;
7064           PlayLevelSound(x, y, SND_NUT_BREAKING);
7065           RaiseScoreElement(EL_NUT);
7066           return;
7067         }
7068         else if (smashed == EL_PEARL)
7069         {
7070           ResetGfxAnimation(x, y);
7071
7072           Feld[x][y + 1] = EL_PEARL_BREAKING;
7073           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7074           return;
7075         }
7076         else if (smashed == EL_DIAMOND)
7077         {
7078           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7079           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7080           return;
7081         }
7082         else if (IS_BELT_SWITCH(smashed))
7083         {
7084           ToggleBeltSwitch(x, y + 1);
7085         }
7086         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7087                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7088                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7089                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7090         {
7091           ToggleSwitchgateSwitch(x, y + 1);
7092         }
7093         else if (smashed == EL_LIGHT_SWITCH ||
7094                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7095         {
7096           ToggleLightSwitch(x, y + 1);
7097         }
7098         else
7099         {
7100 #if 0
7101           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7102 #endif
7103
7104           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7105
7106           CheckElementChangeBySide(x, y + 1, smashed, element,
7107                                    CE_SWITCHED, CH_SIDE_TOP);
7108           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7109                                             CH_SIDE_TOP);
7110         }
7111       }
7112       else
7113       {
7114         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7115       }
7116     }
7117   }
7118
7119   /* play sound of magic wall / mill */
7120   if (!last_line &&
7121       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7122        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7123        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7124   {
7125     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7126       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7127     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7128       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7129     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7130       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7131
7132     return;
7133   }
7134
7135   /* play sound of object that hits the ground */
7136   if (last_line || object_hit)
7137     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7138 }
7139
7140 inline static void TurnRoundExt(int x, int y)
7141 {
7142   static struct
7143   {
7144     int dx, dy;
7145   } move_xy[] =
7146   {
7147     {  0,  0 },
7148     { -1,  0 },
7149     { +1,  0 },
7150     {  0,  0 },
7151     {  0, -1 },
7152     {  0,  0 }, { 0, 0 }, { 0, 0 },
7153     {  0, +1 }
7154   };
7155   static struct
7156   {
7157     int left, right, back;
7158   } turn[] =
7159   {
7160     { 0,        0,              0        },
7161     { MV_DOWN,  MV_UP,          MV_RIGHT },
7162     { MV_UP,    MV_DOWN,        MV_LEFT  },
7163     { 0,        0,              0        },
7164     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7165     { 0,        0,              0        },
7166     { 0,        0,              0        },
7167     { 0,        0,              0        },
7168     { MV_RIGHT, MV_LEFT,        MV_UP    }
7169   };
7170
7171   int element = Feld[x][y];
7172   int move_pattern = element_info[element].move_pattern;
7173
7174   int old_move_dir = MovDir[x][y];
7175   int left_dir  = turn[old_move_dir].left;
7176   int right_dir = turn[old_move_dir].right;
7177   int back_dir  = turn[old_move_dir].back;
7178
7179   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7180   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7181   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7182   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7183
7184   int left_x  = x + left_dx,  left_y  = y + left_dy;
7185   int right_x = x + right_dx, right_y = y + right_dy;
7186   int move_x  = x + move_dx,  move_y  = y + move_dy;
7187
7188   int xx, yy;
7189
7190   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7191   {
7192     TestIfBadThingTouchesOtherBadThing(x, y);
7193
7194     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7195       MovDir[x][y] = right_dir;
7196     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7197       MovDir[x][y] = left_dir;
7198
7199     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7200       MovDelay[x][y] = 9;
7201     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7202       MovDelay[x][y] = 1;
7203   }
7204   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7205   {
7206     TestIfBadThingTouchesOtherBadThing(x, y);
7207
7208     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7209       MovDir[x][y] = left_dir;
7210     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7211       MovDir[x][y] = right_dir;
7212
7213     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7214       MovDelay[x][y] = 9;
7215     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7216       MovDelay[x][y] = 1;
7217   }
7218   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7219   {
7220     TestIfBadThingTouchesOtherBadThing(x, y);
7221
7222     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7223       MovDir[x][y] = left_dir;
7224     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7225       MovDir[x][y] = right_dir;
7226
7227     if (MovDir[x][y] != old_move_dir)
7228       MovDelay[x][y] = 9;
7229   }
7230   else if (element == EL_YAMYAM)
7231   {
7232     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7233     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7234
7235     if (can_turn_left && can_turn_right)
7236       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7237     else if (can_turn_left)
7238       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7239     else if (can_turn_right)
7240       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7241     else
7242       MovDir[x][y] = back_dir;
7243
7244     MovDelay[x][y] = 16 + 16 * RND(3);
7245   }
7246   else if (element == EL_DARK_YAMYAM)
7247   {
7248     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7249                                                          left_x, left_y);
7250     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7251                                                          right_x, right_y);
7252
7253     if (can_turn_left && can_turn_right)
7254       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7255     else if (can_turn_left)
7256       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7257     else if (can_turn_right)
7258       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7259     else
7260       MovDir[x][y] = back_dir;
7261
7262     MovDelay[x][y] = 16 + 16 * RND(3);
7263   }
7264   else if (element == EL_PACMAN)
7265   {
7266     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7267     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7268
7269     if (can_turn_left && can_turn_right)
7270       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7271     else if (can_turn_left)
7272       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7273     else if (can_turn_right)
7274       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7275     else
7276       MovDir[x][y] = back_dir;
7277
7278     MovDelay[x][y] = 6 + RND(40);
7279   }
7280   else if (element == EL_PIG)
7281   {
7282     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7283     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7284     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7285     boolean should_turn_left, should_turn_right, should_move_on;
7286     int rnd_value = 24;
7287     int rnd = RND(rnd_value);
7288
7289     should_turn_left = (can_turn_left &&
7290                         (!can_move_on ||
7291                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7292                                                    y + back_dy + left_dy)));
7293     should_turn_right = (can_turn_right &&
7294                          (!can_move_on ||
7295                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7296                                                     y + back_dy + right_dy)));
7297     should_move_on = (can_move_on &&
7298                       (!can_turn_left ||
7299                        !can_turn_right ||
7300                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7301                                                  y + move_dy + left_dy) ||
7302                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7303                                                  y + move_dy + right_dy)));
7304
7305     if (should_turn_left || should_turn_right || should_move_on)
7306     {
7307       if (should_turn_left && should_turn_right && should_move_on)
7308         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7309                         rnd < 2 * rnd_value / 3 ? right_dir :
7310                         old_move_dir);
7311       else if (should_turn_left && should_turn_right)
7312         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7313       else if (should_turn_left && should_move_on)
7314         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7315       else if (should_turn_right && should_move_on)
7316         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7317       else if (should_turn_left)
7318         MovDir[x][y] = left_dir;
7319       else if (should_turn_right)
7320         MovDir[x][y] = right_dir;
7321       else if (should_move_on)
7322         MovDir[x][y] = old_move_dir;
7323     }
7324     else if (can_move_on && rnd > rnd_value / 8)
7325       MovDir[x][y] = old_move_dir;
7326     else if (can_turn_left && can_turn_right)
7327       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7328     else if (can_turn_left && rnd > rnd_value / 8)
7329       MovDir[x][y] = left_dir;
7330     else if (can_turn_right && rnd > rnd_value/8)
7331       MovDir[x][y] = right_dir;
7332     else
7333       MovDir[x][y] = back_dir;
7334
7335     xx = x + move_xy[MovDir[x][y]].dx;
7336     yy = y + move_xy[MovDir[x][y]].dy;
7337
7338     if (!IN_LEV_FIELD(xx, yy) ||
7339         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7340       MovDir[x][y] = old_move_dir;
7341
7342     MovDelay[x][y] = 0;
7343   }
7344   else if (element == EL_DRAGON)
7345   {
7346     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7347     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7348     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7349     int rnd_value = 24;
7350     int rnd = RND(rnd_value);
7351
7352     if (can_move_on && rnd > rnd_value / 8)
7353       MovDir[x][y] = old_move_dir;
7354     else if (can_turn_left && can_turn_right)
7355       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7356     else if (can_turn_left && rnd > rnd_value / 8)
7357       MovDir[x][y] = left_dir;
7358     else if (can_turn_right && rnd > rnd_value / 8)
7359       MovDir[x][y] = right_dir;
7360     else
7361       MovDir[x][y] = back_dir;
7362
7363     xx = x + move_xy[MovDir[x][y]].dx;
7364     yy = y + move_xy[MovDir[x][y]].dy;
7365
7366     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7367       MovDir[x][y] = old_move_dir;
7368
7369     MovDelay[x][y] = 0;
7370   }
7371   else if (element == EL_MOLE)
7372   {
7373     boolean can_move_on =
7374       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7375                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7376                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7377     if (!can_move_on)
7378     {
7379       boolean can_turn_left =
7380         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7381                               IS_AMOEBOID(Feld[left_x][left_y])));
7382
7383       boolean can_turn_right =
7384         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7385                               IS_AMOEBOID(Feld[right_x][right_y])));
7386
7387       if (can_turn_left && can_turn_right)
7388         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7389       else if (can_turn_left)
7390         MovDir[x][y] = left_dir;
7391       else
7392         MovDir[x][y] = right_dir;
7393     }
7394
7395     if (MovDir[x][y] != old_move_dir)
7396       MovDelay[x][y] = 9;
7397   }
7398   else if (element == EL_BALLOON)
7399   {
7400     MovDir[x][y] = game.wind_direction;
7401     MovDelay[x][y] = 0;
7402   }
7403   else if (element == EL_SPRING)
7404   {
7405 #if USE_NEW_SPRING_BUMPER
7406     if (MovDir[x][y] & MV_HORIZONTAL)
7407     {
7408       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7409           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7410       {
7411         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7412         ResetGfxAnimation(move_x, move_y);
7413         TEST_DrawLevelField(move_x, move_y);
7414
7415         MovDir[x][y] = back_dir;
7416       }
7417       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7418                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7419         MovDir[x][y] = MV_NONE;
7420     }
7421 #else
7422     if (MovDir[x][y] & MV_HORIZONTAL &&
7423         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7424          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7425       MovDir[x][y] = MV_NONE;
7426 #endif
7427
7428     MovDelay[x][y] = 0;
7429   }
7430   else if (element == EL_ROBOT ||
7431            element == EL_SATELLITE ||
7432            element == EL_PENGUIN ||
7433            element == EL_EMC_ANDROID)
7434   {
7435     int attr_x = -1, attr_y = -1;
7436
7437     if (AllPlayersGone)
7438     {
7439       attr_x = ExitX;
7440       attr_y = ExitY;
7441     }
7442     else
7443     {
7444       int i;
7445
7446       for (i = 0; i < MAX_PLAYERS; i++)
7447       {
7448         struct PlayerInfo *player = &stored_player[i];
7449         int jx = player->jx, jy = player->jy;
7450
7451         if (!player->active)
7452           continue;
7453
7454         if (attr_x == -1 ||
7455             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7456         {
7457           attr_x = jx;
7458           attr_y = jy;
7459         }
7460       }
7461     }
7462
7463     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7464         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7465          game.engine_version < VERSION_IDENT(3,1,0,0)))
7466     {
7467       attr_x = ZX;
7468       attr_y = ZY;
7469     }
7470
7471     if (element == EL_PENGUIN)
7472     {
7473       int i;
7474       static int xy[4][2] =
7475       {
7476         { 0, -1 },
7477         { -1, 0 },
7478         { +1, 0 },
7479         { 0, +1 }
7480       };
7481
7482       for (i = 0; i < NUM_DIRECTIONS; i++)
7483       {
7484         int ex = x + xy[i][0];
7485         int ey = y + xy[i][1];
7486
7487         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7488                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7489                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7490                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7491         {
7492           attr_x = ex;
7493           attr_y = ey;
7494           break;
7495         }
7496       }
7497     }
7498
7499     MovDir[x][y] = MV_NONE;
7500     if (attr_x < x)
7501       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7502     else if (attr_x > x)
7503       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7504     if (attr_y < y)
7505       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7506     else if (attr_y > y)
7507       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7508
7509     if (element == EL_ROBOT)
7510     {
7511       int newx, newy;
7512
7513       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7514         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7515       Moving2Blocked(x, y, &newx, &newy);
7516
7517       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7518         MovDelay[x][y] = 8 + 8 * !RND(3);
7519       else
7520         MovDelay[x][y] = 16;
7521     }
7522     else if (element == EL_PENGUIN)
7523     {
7524       int newx, newy;
7525
7526       MovDelay[x][y] = 1;
7527
7528       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7529       {
7530         boolean first_horiz = RND(2);
7531         int new_move_dir = MovDir[x][y];
7532
7533         MovDir[x][y] =
7534           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535         Moving2Blocked(x, y, &newx, &newy);
7536
7537         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7538           return;
7539
7540         MovDir[x][y] =
7541           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7542         Moving2Blocked(x, y, &newx, &newy);
7543
7544         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7545           return;
7546
7547         MovDir[x][y] = old_move_dir;
7548         return;
7549       }
7550     }
7551     else if (element == EL_SATELLITE)
7552     {
7553       int newx, newy;
7554
7555       MovDelay[x][y] = 1;
7556
7557       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7558       {
7559         boolean first_horiz = RND(2);
7560         int new_move_dir = MovDir[x][y];
7561
7562         MovDir[x][y] =
7563           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7564         Moving2Blocked(x, y, &newx, &newy);
7565
7566         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7567           return;
7568
7569         MovDir[x][y] =
7570           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7571         Moving2Blocked(x, y, &newx, &newy);
7572
7573         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7574           return;
7575
7576         MovDir[x][y] = old_move_dir;
7577         return;
7578       }
7579     }
7580     else if (element == EL_EMC_ANDROID)
7581     {
7582       static int check_pos[16] =
7583       {
7584         -1,             /*  0 => (invalid)          */
7585         7,              /*  1 => MV_LEFT            */
7586         3,              /*  2 => MV_RIGHT           */
7587         -1,             /*  3 => (invalid)          */
7588         1,              /*  4 =>            MV_UP   */
7589         0,              /*  5 => MV_LEFT  | MV_UP   */
7590         2,              /*  6 => MV_RIGHT | MV_UP   */
7591         -1,             /*  7 => (invalid)          */
7592         5,              /*  8 =>            MV_DOWN */
7593         6,              /*  9 => MV_LEFT  | MV_DOWN */
7594         4,              /* 10 => MV_RIGHT | MV_DOWN */
7595         -1,             /* 11 => (invalid)          */
7596         -1,             /* 12 => (invalid)          */
7597         -1,             /* 13 => (invalid)          */
7598         -1,             /* 14 => (invalid)          */
7599         -1,             /* 15 => (invalid)          */
7600       };
7601       static struct
7602       {
7603         int dx, dy;
7604         int dir;
7605       } check_xy[8] =
7606       {
7607         { -1, -1,       MV_LEFT  | MV_UP   },
7608         {  0, -1,                  MV_UP   },
7609         { +1, -1,       MV_RIGHT | MV_UP   },
7610         { +1,  0,       MV_RIGHT           },
7611         { +1, +1,       MV_RIGHT | MV_DOWN },
7612         {  0, +1,                  MV_DOWN },
7613         { -1, +1,       MV_LEFT  | MV_DOWN },
7614         { -1,  0,       MV_LEFT            },
7615       };
7616       int start_pos, check_order;
7617       boolean can_clone = FALSE;
7618       int i;
7619
7620       /* check if there is any free field around current position */
7621       for (i = 0; i < 8; i++)
7622       {
7623         int newx = x + check_xy[i].dx;
7624         int newy = y + check_xy[i].dy;
7625
7626         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7627         {
7628           can_clone = TRUE;
7629
7630           break;
7631         }
7632       }
7633
7634       if (can_clone)            /* randomly find an element to clone */
7635       {
7636         can_clone = FALSE;
7637
7638         start_pos = check_pos[RND(8)];
7639         check_order = (RND(2) ? -1 : +1);
7640
7641         for (i = 0; i < 8; i++)
7642         {
7643           int pos_raw = start_pos + i * check_order;
7644           int pos = (pos_raw + 8) % 8;
7645           int newx = x + check_xy[pos].dx;
7646           int newy = y + check_xy[pos].dy;
7647
7648           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7649           {
7650             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7651             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7652
7653             Store[x][y] = Feld[newx][newy];
7654
7655             can_clone = TRUE;
7656
7657             break;
7658           }
7659         }
7660       }
7661
7662       if (can_clone)            /* randomly find a direction to move */
7663       {
7664         can_clone = FALSE;
7665
7666         start_pos = check_pos[RND(8)];
7667         check_order = (RND(2) ? -1 : +1);
7668
7669         for (i = 0; i < 8; i++)
7670         {
7671           int pos_raw = start_pos + i * check_order;
7672           int pos = (pos_raw + 8) % 8;
7673           int newx = x + check_xy[pos].dx;
7674           int newy = y + check_xy[pos].dy;
7675           int new_move_dir = check_xy[pos].dir;
7676
7677           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7678           {
7679             MovDir[x][y] = new_move_dir;
7680             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7681
7682             can_clone = TRUE;
7683
7684             break;
7685           }
7686         }
7687       }
7688
7689       if (can_clone)            /* cloning and moving successful */
7690         return;
7691
7692       /* cannot clone -- try to move towards player */
7693
7694       start_pos = check_pos[MovDir[x][y] & 0x0f];
7695       check_order = (RND(2) ? -1 : +1);
7696
7697       for (i = 0; i < 3; i++)
7698       {
7699         /* first check start_pos, then previous/next or (next/previous) pos */
7700         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7701         int pos = (pos_raw + 8) % 8;
7702         int newx = x + check_xy[pos].dx;
7703         int newy = y + check_xy[pos].dy;
7704         int new_move_dir = check_xy[pos].dir;
7705
7706         if (IS_PLAYER(newx, newy))
7707           break;
7708
7709         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7710         {
7711           MovDir[x][y] = new_move_dir;
7712           MovDelay[x][y] = level.android_move_time * 8 + 1;
7713
7714           break;
7715         }
7716       }
7717     }
7718   }
7719   else if (move_pattern == MV_TURNING_LEFT ||
7720            move_pattern == MV_TURNING_RIGHT ||
7721            move_pattern == MV_TURNING_LEFT_RIGHT ||
7722            move_pattern == MV_TURNING_RIGHT_LEFT ||
7723            move_pattern == MV_TURNING_RANDOM ||
7724            move_pattern == MV_ALL_DIRECTIONS)
7725   {
7726     boolean can_turn_left =
7727       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7728     boolean can_turn_right =
7729       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7730
7731     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7732       return;
7733
7734     if (move_pattern == MV_TURNING_LEFT)
7735       MovDir[x][y] = left_dir;
7736     else if (move_pattern == MV_TURNING_RIGHT)
7737       MovDir[x][y] = right_dir;
7738     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7739       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7740     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7741       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7742     else if (move_pattern == MV_TURNING_RANDOM)
7743       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7744                       can_turn_right && !can_turn_left ? right_dir :
7745                       RND(2) ? left_dir : right_dir);
7746     else if (can_turn_left && can_turn_right)
7747       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7748     else if (can_turn_left)
7749       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7750     else if (can_turn_right)
7751       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7752     else
7753       MovDir[x][y] = back_dir;
7754
7755     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756   }
7757   else if (move_pattern == MV_HORIZONTAL ||
7758            move_pattern == MV_VERTICAL)
7759   {
7760     if (move_pattern & old_move_dir)
7761       MovDir[x][y] = back_dir;
7762     else if (move_pattern == MV_HORIZONTAL)
7763       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7764     else if (move_pattern == MV_VERTICAL)
7765       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7766
7767     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768   }
7769   else if (move_pattern & MV_ANY_DIRECTION)
7770   {
7771     MovDir[x][y] = move_pattern;
7772     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7773   }
7774   else if (move_pattern & MV_WIND_DIRECTION)
7775   {
7776     MovDir[x][y] = game.wind_direction;
7777     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7778   }
7779   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7780   {
7781     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7782       MovDir[x][y] = left_dir;
7783     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7784       MovDir[x][y] = right_dir;
7785
7786     if (MovDir[x][y] != old_move_dir)
7787       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7788   }
7789   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7790   {
7791     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7792       MovDir[x][y] = right_dir;
7793     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7794       MovDir[x][y] = left_dir;
7795
7796     if (MovDir[x][y] != old_move_dir)
7797       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7798   }
7799   else if (move_pattern == MV_TOWARDS_PLAYER ||
7800            move_pattern == MV_AWAY_FROM_PLAYER)
7801   {
7802     int attr_x = -1, attr_y = -1;
7803     int newx, newy;
7804     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7805
7806     if (AllPlayersGone)
7807     {
7808       attr_x = ExitX;
7809       attr_y = ExitY;
7810     }
7811     else
7812     {
7813       int i;
7814
7815       for (i = 0; i < MAX_PLAYERS; i++)
7816       {
7817         struct PlayerInfo *player = &stored_player[i];
7818         int jx = player->jx, jy = player->jy;
7819
7820         if (!player->active)
7821           continue;
7822
7823         if (attr_x == -1 ||
7824             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7825         {
7826           attr_x = jx;
7827           attr_y = jy;
7828         }
7829       }
7830     }
7831
7832     MovDir[x][y] = MV_NONE;
7833     if (attr_x < x)
7834       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7835     else if (attr_x > x)
7836       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7837     if (attr_y < y)
7838       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7839     else if (attr_y > y)
7840       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7841
7842     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7843
7844     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7845     {
7846       boolean first_horiz = RND(2);
7847       int new_move_dir = MovDir[x][y];
7848
7849       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7850       {
7851         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7852         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7853
7854         return;
7855       }
7856
7857       MovDir[x][y] =
7858         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7859       Moving2Blocked(x, y, &newx, &newy);
7860
7861       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7862         return;
7863
7864       MovDir[x][y] =
7865         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7866       Moving2Blocked(x, y, &newx, &newy);
7867
7868       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7869         return;
7870
7871       MovDir[x][y] = old_move_dir;
7872     }
7873   }
7874   else if (move_pattern == MV_WHEN_PUSHED ||
7875            move_pattern == MV_WHEN_DROPPED)
7876   {
7877     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7878       MovDir[x][y] = MV_NONE;
7879
7880     MovDelay[x][y] = 0;
7881   }
7882   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7883   {
7884     static int test_xy[7][2] =
7885     {
7886       { 0, -1 },
7887       { -1, 0 },
7888       { +1, 0 },
7889       { 0, +1 },
7890       { 0, -1 },
7891       { -1, 0 },
7892       { +1, 0 },
7893     };
7894     static int test_dir[7] =
7895     {
7896       MV_UP,
7897       MV_LEFT,
7898       MV_RIGHT,
7899       MV_DOWN,
7900       MV_UP,
7901       MV_LEFT,
7902       MV_RIGHT,
7903     };
7904     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7905     int move_preference = -1000000;     /* start with very low preference */
7906     int new_move_dir = MV_NONE;
7907     int start_test = RND(4);
7908     int i;
7909
7910     for (i = 0; i < NUM_DIRECTIONS; i++)
7911     {
7912       int move_dir = test_dir[start_test + i];
7913       int move_dir_preference;
7914
7915       xx = x + test_xy[start_test + i][0];
7916       yy = y + test_xy[start_test + i][1];
7917
7918       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7919           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7920       {
7921         new_move_dir = move_dir;
7922
7923         break;
7924       }
7925
7926       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7927         continue;
7928
7929       move_dir_preference = -1 * RunnerVisit[xx][yy];
7930       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7931         move_dir_preference = PlayerVisit[xx][yy];
7932
7933       if (move_dir_preference > move_preference)
7934       {
7935         /* prefer field that has not been visited for the longest time */
7936         move_preference = move_dir_preference;
7937         new_move_dir = move_dir;
7938       }
7939       else if (move_dir_preference == move_preference &&
7940                move_dir == old_move_dir)
7941       {
7942         /* prefer last direction when all directions are preferred equally */
7943         move_preference = move_dir_preference;
7944         new_move_dir = move_dir;
7945       }
7946     }
7947
7948     MovDir[x][y] = new_move_dir;
7949     if (old_move_dir != new_move_dir)
7950       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7951   }
7952 }
7953
7954 static void TurnRound(int x, int y)
7955 {
7956   int direction = MovDir[x][y];
7957
7958   TurnRoundExt(x, y);
7959
7960   GfxDir[x][y] = MovDir[x][y];
7961
7962   if (direction != MovDir[x][y])
7963     GfxFrame[x][y] = 0;
7964
7965   if (MovDelay[x][y])
7966     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7967
7968   ResetGfxFrame(x, y, FALSE);
7969 }
7970
7971 static boolean JustBeingPushed(int x, int y)
7972 {
7973   int i;
7974
7975   for (i = 0; i < MAX_PLAYERS; i++)
7976   {
7977     struct PlayerInfo *player = &stored_player[i];
7978
7979     if (player->active && player->is_pushing && player->MovPos)
7980     {
7981       int next_jx = player->jx + (player->jx - player->last_jx);
7982       int next_jy = player->jy + (player->jy - player->last_jy);
7983
7984       if (x == next_jx && y == next_jy)
7985         return TRUE;
7986     }
7987   }
7988
7989   return FALSE;
7990 }
7991
7992 void StartMoving(int x, int y)
7993 {
7994   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7995   int element = Feld[x][y];
7996
7997   if (Stop[x][y])
7998     return;
7999
8000   if (MovDelay[x][y] == 0)
8001     GfxAction[x][y] = ACTION_DEFAULT;
8002
8003   if (CAN_FALL(element) && y < lev_fieldy - 1)
8004   {
8005     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8006         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8007       if (JustBeingPushed(x, y))
8008         return;
8009
8010     if (element == EL_QUICKSAND_FULL)
8011     {
8012       if (IS_FREE(x, y + 1))
8013       {
8014         InitMovingField(x, y, MV_DOWN);
8015         started_moving = TRUE;
8016
8017         Feld[x][y] = EL_QUICKSAND_EMPTYING;
8018 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8019         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8020           Store[x][y] = EL_ROCK;
8021 #else
8022         Store[x][y] = EL_ROCK;
8023 #endif
8024
8025         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8026       }
8027       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8028       {
8029         if (!MovDelay[x][y])
8030         {
8031           MovDelay[x][y] = TILEY + 1;
8032
8033           ResetGfxAnimation(x, y);
8034           ResetGfxAnimation(x, y + 1);
8035         }
8036
8037         if (MovDelay[x][y])
8038         {
8039           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8040           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8041
8042           MovDelay[x][y]--;
8043           if (MovDelay[x][y])
8044             return;
8045         }
8046
8047         Feld[x][y] = EL_QUICKSAND_EMPTY;
8048         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8049         Store[x][y + 1] = Store[x][y];
8050         Store[x][y] = 0;
8051
8052         PlayLevelSoundAction(x, y, ACTION_FILLING);
8053       }
8054       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8055       {
8056         if (!MovDelay[x][y])
8057         {
8058           MovDelay[x][y] = TILEY + 1;
8059
8060           ResetGfxAnimation(x, y);
8061           ResetGfxAnimation(x, y + 1);
8062         }
8063
8064         if (MovDelay[x][y])
8065         {
8066           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8067           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8068
8069           MovDelay[x][y]--;
8070           if (MovDelay[x][y])
8071             return;
8072         }
8073
8074         Feld[x][y] = EL_QUICKSAND_EMPTY;
8075         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8076         Store[x][y + 1] = Store[x][y];
8077         Store[x][y] = 0;
8078
8079         PlayLevelSoundAction(x, y, ACTION_FILLING);
8080       }
8081     }
8082     else if (element == EL_QUICKSAND_FAST_FULL)
8083     {
8084       if (IS_FREE(x, y + 1))
8085       {
8086         InitMovingField(x, y, MV_DOWN);
8087         started_moving = TRUE;
8088
8089         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8090 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8091         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8092           Store[x][y] = EL_ROCK;
8093 #else
8094         Store[x][y] = EL_ROCK;
8095 #endif
8096
8097         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8098       }
8099       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8100       {
8101         if (!MovDelay[x][y])
8102         {
8103           MovDelay[x][y] = TILEY + 1;
8104
8105           ResetGfxAnimation(x, y);
8106           ResetGfxAnimation(x, y + 1);
8107         }
8108
8109         if (MovDelay[x][y])
8110         {
8111           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8112           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8113
8114           MovDelay[x][y]--;
8115           if (MovDelay[x][y])
8116             return;
8117         }
8118
8119         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8120         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8121         Store[x][y + 1] = Store[x][y];
8122         Store[x][y] = 0;
8123
8124         PlayLevelSoundAction(x, y, ACTION_FILLING);
8125       }
8126       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8127       {
8128         if (!MovDelay[x][y])
8129         {
8130           MovDelay[x][y] = TILEY + 1;
8131
8132           ResetGfxAnimation(x, y);
8133           ResetGfxAnimation(x, y + 1);
8134         }
8135
8136         if (MovDelay[x][y])
8137         {
8138           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8139           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8140
8141           MovDelay[x][y]--;
8142           if (MovDelay[x][y])
8143             return;
8144         }
8145
8146         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8147         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8148         Store[x][y + 1] = Store[x][y];
8149         Store[x][y] = 0;
8150
8151         PlayLevelSoundAction(x, y, ACTION_FILLING);
8152       }
8153     }
8154     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8155              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8156     {
8157       InitMovingField(x, y, MV_DOWN);
8158       started_moving = TRUE;
8159
8160       Feld[x][y] = EL_QUICKSAND_FILLING;
8161       Store[x][y] = element;
8162
8163       PlayLevelSoundAction(x, y, ACTION_FILLING);
8164     }
8165     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8166              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8167     {
8168       InitMovingField(x, y, MV_DOWN);
8169       started_moving = TRUE;
8170
8171       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8172       Store[x][y] = element;
8173
8174       PlayLevelSoundAction(x, y, ACTION_FILLING);
8175     }
8176     else if (element == EL_MAGIC_WALL_FULL)
8177     {
8178       if (IS_FREE(x, y + 1))
8179       {
8180         InitMovingField(x, y, MV_DOWN);
8181         started_moving = TRUE;
8182
8183         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8184         Store[x][y] = EL_CHANGED(Store[x][y]);
8185       }
8186       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8187       {
8188         if (!MovDelay[x][y])
8189           MovDelay[x][y] = TILEY / 4 + 1;
8190
8191         if (MovDelay[x][y])
8192         {
8193           MovDelay[x][y]--;
8194           if (MovDelay[x][y])
8195             return;
8196         }
8197
8198         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8199         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8200         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8201         Store[x][y] = 0;
8202       }
8203     }
8204     else if (element == EL_BD_MAGIC_WALL_FULL)
8205     {
8206       if (IS_FREE(x, y + 1))
8207       {
8208         InitMovingField(x, y, MV_DOWN);
8209         started_moving = TRUE;
8210
8211         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8212         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8213       }
8214       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8215       {
8216         if (!MovDelay[x][y])
8217           MovDelay[x][y] = TILEY / 4 + 1;
8218
8219         if (MovDelay[x][y])
8220         {
8221           MovDelay[x][y]--;
8222           if (MovDelay[x][y])
8223             return;
8224         }
8225
8226         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8227         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8228         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8229         Store[x][y] = 0;
8230       }
8231     }
8232     else if (element == EL_DC_MAGIC_WALL_FULL)
8233     {
8234       if (IS_FREE(x, y + 1))
8235       {
8236         InitMovingField(x, y, MV_DOWN);
8237         started_moving = TRUE;
8238
8239         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8240         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8241       }
8242       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8243       {
8244         if (!MovDelay[x][y])
8245           MovDelay[x][y] = TILEY / 4 + 1;
8246
8247         if (MovDelay[x][y])
8248         {
8249           MovDelay[x][y]--;
8250           if (MovDelay[x][y])
8251             return;
8252         }
8253
8254         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8255         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8256         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8257         Store[x][y] = 0;
8258       }
8259     }
8260     else if ((CAN_PASS_MAGIC_WALL(element) &&
8261               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8262                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8263              (CAN_PASS_DC_MAGIC_WALL(element) &&
8264               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8265
8266     {
8267       InitMovingField(x, y, MV_DOWN);
8268       started_moving = TRUE;
8269
8270       Feld[x][y] =
8271         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8272          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8273          EL_DC_MAGIC_WALL_FILLING);
8274       Store[x][y] = element;
8275     }
8276     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8277     {
8278       SplashAcid(x, y + 1);
8279
8280       InitMovingField(x, y, MV_DOWN);
8281       started_moving = TRUE;
8282
8283       Store[x][y] = EL_ACID;
8284     }
8285     else if (
8286 #if USE_FIX_IMPACT_COLLISION
8287              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8288               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8289 #else
8290              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8291               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8292 #endif
8293              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8294               CAN_FALL(element) && WasJustFalling[x][y] &&
8295               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8296
8297              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8298               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8299               (Feld[x][y + 1] == EL_BLOCKED)))
8300     {
8301       /* this is needed for a special case not covered by calling "Impact()"
8302          from "ContinueMoving()": if an element moves to a tile directly below
8303          another element which was just falling on that tile (which was empty
8304          in the previous frame), the falling element above would just stop
8305          instead of smashing the element below (in previous version, the above
8306          element was just checked for "moving" instead of "falling", resulting
8307          in incorrect smashes caused by horizontal movement of the above
8308          element; also, the case of the player being the element to smash was
8309          simply not covered here... :-/ ) */
8310
8311       CheckCollision[x][y] = 0;
8312       CheckImpact[x][y] = 0;
8313
8314       Impact(x, y);
8315     }
8316     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8317     {
8318       if (MovDir[x][y] == MV_NONE)
8319       {
8320         InitMovingField(x, y, MV_DOWN);
8321         started_moving = TRUE;
8322       }
8323     }
8324     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8325     {
8326       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8327         MovDir[x][y] = MV_DOWN;
8328
8329       InitMovingField(x, y, MV_DOWN);
8330       started_moving = TRUE;
8331     }
8332     else if (element == EL_AMOEBA_DROP)
8333     {
8334       Feld[x][y] = EL_AMOEBA_GROWING;
8335       Store[x][y] = EL_AMOEBA_WET;
8336     }
8337     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8338               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8339              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8340              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8341     {
8342       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8343                                 (IS_FREE(x - 1, y + 1) ||
8344                                  Feld[x - 1][y + 1] == EL_ACID));
8345       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8346                                 (IS_FREE(x + 1, y + 1) ||
8347                                  Feld[x + 1][y + 1] == EL_ACID));
8348       boolean can_fall_any  = (can_fall_left || can_fall_right);
8349       boolean can_fall_both = (can_fall_left && can_fall_right);
8350       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8351
8352 #if USE_NEW_ALL_SLIPPERY
8353       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8354       {
8355         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8356           can_fall_right = FALSE;
8357         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8358           can_fall_left = FALSE;
8359         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8360           can_fall_right = FALSE;
8361         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8362           can_fall_left = FALSE;
8363
8364         can_fall_any  = (can_fall_left || can_fall_right);
8365         can_fall_both = FALSE;
8366       }
8367 #else
8368       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8369       {
8370         if (slippery_type == SLIPPERY_ONLY_LEFT)
8371           can_fall_right = FALSE;
8372         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8373           can_fall_left = FALSE;
8374         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8375           can_fall_right = FALSE;
8376         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8377           can_fall_left = FALSE;
8378
8379         can_fall_any  = (can_fall_left || can_fall_right);
8380         can_fall_both = (can_fall_left && can_fall_right);
8381       }
8382 #endif
8383
8384 #if USE_NEW_ALL_SLIPPERY
8385 #else
8386 #if USE_NEW_SP_SLIPPERY
8387       /* !!! better use the same properties as for custom elements here !!! */
8388       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8389                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8390       {
8391         can_fall_right = FALSE;         /* slip down on left side */
8392         can_fall_both = FALSE;
8393       }
8394 #endif
8395 #endif
8396
8397 #if USE_NEW_ALL_SLIPPERY
8398       if (can_fall_both)
8399       {
8400         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8401           can_fall_right = FALSE;       /* slip down on left side */
8402         else
8403           can_fall_left = !(can_fall_right = RND(2));
8404
8405         can_fall_both = FALSE;
8406       }
8407 #else
8408       if (can_fall_both)
8409       {
8410         if (game.emulation == EMU_BOULDERDASH ||
8411             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8412           can_fall_right = FALSE;       /* slip down on left side */
8413         else
8414           can_fall_left = !(can_fall_right = RND(2));
8415
8416         can_fall_both = FALSE;
8417       }
8418 #endif
8419
8420       if (can_fall_any)
8421       {
8422         /* if not determined otherwise, prefer left side for slipping down */
8423         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8424         started_moving = TRUE;
8425       }
8426     }
8427 #if 0
8428     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8429 #else
8430     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8431 #endif
8432     {
8433       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8434       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8435       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8436       int belt_dir = game.belt_dir[belt_nr];
8437
8438       if ((belt_dir == MV_LEFT  && left_is_free) ||
8439           (belt_dir == MV_RIGHT && right_is_free))
8440       {
8441         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8442
8443         InitMovingField(x, y, belt_dir);
8444         started_moving = TRUE;
8445
8446         Pushed[x][y] = TRUE;
8447         Pushed[nextx][y] = TRUE;
8448
8449         GfxAction[x][y] = ACTION_DEFAULT;
8450       }
8451       else
8452       {
8453         MovDir[x][y] = 0;       /* if element was moving, stop it */
8454       }
8455     }
8456   }
8457
8458   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8459 #if 0
8460   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8461 #else
8462   if (CAN_MOVE(element) && !started_moving)
8463 #endif
8464   {
8465     int move_pattern = element_info[element].move_pattern;
8466     int newx, newy;
8467
8468 #if 0
8469 #if DEBUG
8470     if (MovDir[x][y] == MV_NONE)
8471     {
8472       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8473              x, y, element, element_info[element].token_name);
8474       printf("StartMoving(): This should never happen!\n");
8475     }
8476 #endif
8477 #endif
8478
8479     Moving2Blocked(x, y, &newx, &newy);
8480
8481     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8482       return;
8483
8484     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8485         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8486     {
8487       WasJustMoving[x][y] = 0;
8488       CheckCollision[x][y] = 0;
8489
8490       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8491
8492       if (Feld[x][y] != element)        /* element has changed */
8493         return;
8494     }
8495
8496     if (!MovDelay[x][y])        /* start new movement phase */
8497     {
8498       /* all objects that can change their move direction after each step
8499          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8500
8501       if (element != EL_YAMYAM &&
8502           element != EL_DARK_YAMYAM &&
8503           element != EL_PACMAN &&
8504           !(move_pattern & MV_ANY_DIRECTION) &&
8505           move_pattern != MV_TURNING_LEFT &&
8506           move_pattern != MV_TURNING_RIGHT &&
8507           move_pattern != MV_TURNING_LEFT_RIGHT &&
8508           move_pattern != MV_TURNING_RIGHT_LEFT &&
8509           move_pattern != MV_TURNING_RANDOM)
8510       {
8511         TurnRound(x, y);
8512
8513         if (MovDelay[x][y] && (element == EL_BUG ||
8514                                element == EL_SPACESHIP ||
8515                                element == EL_SP_SNIKSNAK ||
8516                                element == EL_SP_ELECTRON ||
8517                                element == EL_MOLE))
8518           TEST_DrawLevelField(x, y);
8519       }
8520     }
8521
8522     if (MovDelay[x][y])         /* wait some time before next movement */
8523     {
8524       MovDelay[x][y]--;
8525
8526       if (element == EL_ROBOT ||
8527           element == EL_YAMYAM ||
8528           element == EL_DARK_YAMYAM)
8529       {
8530         DrawLevelElementAnimationIfNeeded(x, y, element);
8531         PlayLevelSoundAction(x, y, ACTION_WAITING);
8532       }
8533       else if (element == EL_SP_ELECTRON)
8534         DrawLevelElementAnimationIfNeeded(x, y, element);
8535       else if (element == EL_DRAGON)
8536       {
8537         int i;
8538         int dir = MovDir[x][y];
8539         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8540         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8541         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8542                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8543                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8544                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8545         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8546
8547         GfxAction[x][y] = ACTION_ATTACKING;
8548
8549         if (IS_PLAYER(x, y))
8550           DrawPlayerField(x, y);
8551         else
8552           TEST_DrawLevelField(x, y);
8553
8554         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8555
8556         for (i = 1; i <= 3; i++)
8557         {
8558           int xx = x + i * dx;
8559           int yy = y + i * dy;
8560           int sx = SCREENX(xx);
8561           int sy = SCREENY(yy);
8562           int flame_graphic = graphic + (i - 1);
8563
8564           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8565             break;
8566
8567           if (MovDelay[x][y])
8568           {
8569             int flamed = MovingOrBlocked2Element(xx, yy);
8570
8571             /* !!! */
8572 #if 0
8573             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8574               Bang(xx, yy);
8575             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8576               RemoveMovingField(xx, yy);
8577             else
8578               RemoveField(xx, yy);
8579 #else
8580             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8581               Bang(xx, yy);
8582             else
8583               RemoveMovingField(xx, yy);
8584 #endif
8585
8586             ChangeDelay[xx][yy] = 0;
8587
8588             Feld[xx][yy] = EL_FLAMES;
8589
8590             if (IN_SCR_FIELD(sx, sy))
8591             {
8592               TEST_DrawLevelFieldCrumbled(xx, yy);
8593               DrawGraphic(sx, sy, flame_graphic, frame);
8594             }
8595           }
8596           else
8597           {
8598             if (Feld[xx][yy] == EL_FLAMES)
8599               Feld[xx][yy] = EL_EMPTY;
8600             TEST_DrawLevelField(xx, yy);
8601           }
8602         }
8603       }
8604
8605       if (MovDelay[x][y])       /* element still has to wait some time */
8606       {
8607         PlayLevelSoundAction(x, y, ACTION_WAITING);
8608
8609         return;
8610       }
8611     }
8612
8613     /* now make next step */
8614
8615     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8616
8617     if (DONT_COLLIDE_WITH(element) &&
8618         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8619         !PLAYER_ENEMY_PROTECTED(newx, newy))
8620     {
8621       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8622
8623       return;
8624     }
8625
8626     else if (CAN_MOVE_INTO_ACID(element) &&
8627              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8628              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8629              (MovDir[x][y] == MV_DOWN ||
8630               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8631     {
8632       SplashAcid(newx, newy);
8633       Store[x][y] = EL_ACID;
8634     }
8635     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8636     {
8637       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8638           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8639           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8640           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8641       {
8642         RemoveField(x, y);
8643         TEST_DrawLevelField(x, y);
8644
8645         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8646         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8647           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8648
8649         local_player->friends_still_needed--;
8650         if (!local_player->friends_still_needed &&
8651             !local_player->GameOver && AllPlayersGone)
8652           PlayerWins(local_player);
8653
8654         return;
8655       }
8656       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8657       {
8658         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8659           TEST_DrawLevelField(newx, newy);
8660         else
8661           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665         GfxAction[x][y] = ACTION_WAITING;
8666
8667         if (IS_PLAYER(x, y))
8668           DrawPlayerField(x, y);
8669         else
8670           TEST_DrawLevelField(x, y);
8671
8672         return;
8673       }
8674     }
8675     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8676     {
8677       if (IS_FOOD_PIG(Feld[newx][newy]))
8678       {
8679         if (IS_MOVING(newx, newy))
8680           RemoveMovingField(newx, newy);
8681         else
8682         {
8683           Feld[newx][newy] = EL_EMPTY;
8684           TEST_DrawLevelField(newx, newy);
8685         }
8686
8687         PlayLevelSound(x, y, SND_PIG_DIGGING);
8688       }
8689       else if (!IS_FREE(newx, newy))
8690       {
8691         if (IS_PLAYER(x, y))
8692           DrawPlayerField(x, y);
8693         else
8694           TEST_DrawLevelField(x, y);
8695
8696         return;
8697       }
8698     }
8699     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8700     {
8701       if (Store[x][y] != EL_EMPTY)
8702       {
8703         boolean can_clone = FALSE;
8704         int xx, yy;
8705
8706         /* check if element to clone is still there */
8707         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8708         {
8709           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8710           {
8711             can_clone = TRUE;
8712
8713             break;
8714           }
8715         }
8716
8717         /* cannot clone or target field not free anymore -- do not clone */
8718         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8719           Store[x][y] = EL_EMPTY;
8720       }
8721
8722       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8723       {
8724         if (IS_MV_DIAGONAL(MovDir[x][y]))
8725         {
8726           int diagonal_move_dir = MovDir[x][y];
8727           int stored = Store[x][y];
8728           int change_delay = 8;
8729           int graphic;
8730
8731           /* android is moving diagonally */
8732
8733           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8734
8735           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8736           GfxElement[x][y] = EL_EMC_ANDROID;
8737           GfxAction[x][y] = ACTION_SHRINKING;
8738           GfxDir[x][y] = diagonal_move_dir;
8739           ChangeDelay[x][y] = change_delay;
8740
8741           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8742                                    GfxDir[x][y]);
8743
8744           DrawLevelGraphicAnimation(x, y, graphic);
8745           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8746
8747           if (Feld[newx][newy] == EL_ACID)
8748           {
8749             SplashAcid(newx, newy);
8750
8751             return;
8752           }
8753
8754           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8755
8756           Store[newx][newy] = EL_EMC_ANDROID;
8757           GfxElement[newx][newy] = EL_EMC_ANDROID;
8758           GfxAction[newx][newy] = ACTION_GROWING;
8759           GfxDir[newx][newy] = diagonal_move_dir;
8760           ChangeDelay[newx][newy] = change_delay;
8761
8762           graphic = el_act_dir2img(GfxElement[newx][newy],
8763                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8764
8765           DrawLevelGraphicAnimation(newx, newy, graphic);
8766           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8767
8768           return;
8769         }
8770         else
8771         {
8772           Feld[newx][newy] = EL_EMPTY;
8773           TEST_DrawLevelField(newx, newy);
8774
8775           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8776         }
8777       }
8778       else if (!IS_FREE(newx, newy))
8779       {
8780 #if 0
8781         if (IS_PLAYER(x, y))
8782           DrawPlayerField(x, y);
8783         else
8784           TEST_DrawLevelField(x, y);
8785 #endif
8786
8787         return;
8788       }
8789     }
8790     else if (IS_CUSTOM_ELEMENT(element) &&
8791              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8792     {
8793 #if 1
8794       if (!DigFieldByCE(newx, newy, element))
8795         return;
8796 #else
8797       int new_element = Feld[newx][newy];
8798
8799       if (!IS_FREE(newx, newy))
8800       {
8801         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8802                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8803                       ACTION_BREAKING);
8804
8805         /* no element can dig solid indestructible elements */
8806         if (IS_INDESTRUCTIBLE(new_element) &&
8807             !IS_DIGGABLE(new_element) &&
8808             !IS_COLLECTIBLE(new_element))
8809           return;
8810
8811         if (AmoebaNr[newx][newy] &&
8812             (new_element == EL_AMOEBA_FULL ||
8813              new_element == EL_BD_AMOEBA ||
8814              new_element == EL_AMOEBA_GROWING))
8815         {
8816           AmoebaCnt[AmoebaNr[newx][newy]]--;
8817           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8818         }
8819
8820         if (IS_MOVING(newx, newy))
8821           RemoveMovingField(newx, newy);
8822         else
8823         {
8824           RemoveField(newx, newy);
8825           TEST_DrawLevelField(newx, newy);
8826         }
8827
8828         /* if digged element was about to explode, prevent the explosion */
8829         ExplodeField[newx][newy] = EX_TYPE_NONE;
8830
8831         PlayLevelSoundAction(x, y, action);
8832       }
8833
8834       Store[newx][newy] = EL_EMPTY;
8835
8836 #if 1
8837       /* this makes it possible to leave the removed element again */
8838       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8839         Store[newx][newy] = new_element;
8840 #else
8841       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8842       {
8843         int move_leave_element = element_info[element].move_leave_element;
8844
8845         /* this makes it possible to leave the removed element again */
8846         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8847                              new_element : move_leave_element);
8848       }
8849 #endif
8850
8851 #endif
8852
8853       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8854       {
8855         RunnerVisit[x][y] = FrameCounter;
8856         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8857       }
8858     }
8859     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8860     {
8861       if (!IS_FREE(newx, newy))
8862       {
8863         if (IS_PLAYER(x, y))
8864           DrawPlayerField(x, y);
8865         else
8866           TEST_DrawLevelField(x, y);
8867
8868         return;
8869       }
8870       else
8871       {
8872         boolean wanna_flame = !RND(10);
8873         int dx = newx - x, dy = newy - y;
8874         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8875         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8876         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8877                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8878         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8879                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8880
8881         if ((wanna_flame ||
8882              IS_CLASSIC_ENEMY(element1) ||
8883              IS_CLASSIC_ENEMY(element2)) &&
8884             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8885             element1 != EL_FLAMES && element2 != EL_FLAMES)
8886         {
8887           ResetGfxAnimation(x, y);
8888           GfxAction[x][y] = ACTION_ATTACKING;
8889
8890           if (IS_PLAYER(x, y))
8891             DrawPlayerField(x, y);
8892           else
8893             TEST_DrawLevelField(x, y);
8894
8895           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8896
8897           MovDelay[x][y] = 50;
8898
8899           /* !!! */
8900 #if 0
8901           RemoveField(newx, newy);
8902 #endif
8903           Feld[newx][newy] = EL_FLAMES;
8904           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8905           {
8906 #if 0
8907             RemoveField(newx1, newy1);
8908 #endif
8909             Feld[newx1][newy1] = EL_FLAMES;
8910           }
8911           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8912           {
8913 #if 0
8914             RemoveField(newx2, newy2);
8915 #endif
8916             Feld[newx2][newy2] = EL_FLAMES;
8917           }
8918
8919           return;
8920         }
8921       }
8922     }
8923     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8924              Feld[newx][newy] == EL_DIAMOND)
8925     {
8926       if (IS_MOVING(newx, newy))
8927         RemoveMovingField(newx, newy);
8928       else
8929       {
8930         Feld[newx][newy] = EL_EMPTY;
8931         TEST_DrawLevelField(newx, newy);
8932       }
8933
8934       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8935     }
8936     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8937              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8938     {
8939       if (AmoebaNr[newx][newy])
8940       {
8941         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8942         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8943             Feld[newx][newy] == EL_BD_AMOEBA)
8944           AmoebaCnt[AmoebaNr[newx][newy]]--;
8945       }
8946
8947 #if 0
8948       /* !!! test !!! */
8949       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8950       {
8951         RemoveMovingField(newx, newy);
8952       }
8953 #else
8954       if (IS_MOVING(newx, newy))
8955       {
8956         RemoveMovingField(newx, newy);
8957       }
8958 #endif
8959       else
8960       {
8961         Feld[newx][newy] = EL_EMPTY;
8962         TEST_DrawLevelField(newx, newy);
8963       }
8964
8965       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8966     }
8967     else if ((element == EL_PACMAN || element == EL_MOLE)
8968              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8969     {
8970       if (AmoebaNr[newx][newy])
8971       {
8972         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8973         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8974             Feld[newx][newy] == EL_BD_AMOEBA)
8975           AmoebaCnt[AmoebaNr[newx][newy]]--;
8976       }
8977
8978       if (element == EL_MOLE)
8979       {
8980         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8981         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8982
8983         ResetGfxAnimation(x, y);
8984         GfxAction[x][y] = ACTION_DIGGING;
8985         TEST_DrawLevelField(x, y);
8986
8987         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8988
8989         return;                         /* wait for shrinking amoeba */
8990       }
8991       else      /* element == EL_PACMAN */
8992       {
8993         Feld[newx][newy] = EL_EMPTY;
8994         TEST_DrawLevelField(newx, newy);
8995         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8996       }
8997     }
8998     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8999              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9000               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9001     {
9002       /* wait for shrinking amoeba to completely disappear */
9003       return;
9004     }
9005     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9006     {
9007       /* object was running against a wall */
9008
9009       TurnRound(x, y);
9010
9011 #if 0
9012       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9013       if (move_pattern & MV_ANY_DIRECTION &&
9014           move_pattern == MovDir[x][y])
9015       {
9016         int blocking_element =
9017           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9018
9019         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9020                                  MovDir[x][y]);
9021
9022         element = Feld[x][y];   /* element might have changed */
9023       }
9024 #endif
9025
9026       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9027         DrawLevelElementAnimation(x, y, element);
9028
9029       if (DONT_TOUCH(element))
9030         TestIfBadThingTouchesPlayer(x, y);
9031
9032       return;
9033     }
9034
9035     InitMovingField(x, y, MovDir[x][y]);
9036
9037     PlayLevelSoundAction(x, y, ACTION_MOVING);
9038   }
9039
9040   if (MovDir[x][y])
9041     ContinueMoving(x, y);
9042 }
9043
9044 void ContinueMoving(int x, int y)
9045 {
9046   int element = Feld[x][y];
9047   struct ElementInfo *ei = &element_info[element];
9048   int direction = MovDir[x][y];
9049   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9050   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9051   int newx = x + dx, newy = y + dy;
9052   int stored = Store[x][y];
9053   int stored_new = Store[newx][newy];
9054   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9055   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9056   boolean last_line = (newy == lev_fieldy - 1);
9057
9058   MovPos[x][y] += getElementMoveStepsize(x, y);
9059
9060   if (pushed_by_player) /* special case: moving object pushed by player */
9061     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9062
9063   if (ABS(MovPos[x][y]) < TILEX)
9064   {
9065 #if 0
9066     int ee = Feld[x][y];
9067     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9068     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9069
9070     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9071            x, y, ABS(MovPos[x][y]),
9072            ee, gg, ff,
9073            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9074 #endif
9075
9076     TEST_DrawLevelField(x, y);
9077
9078     return;     /* element is still moving */
9079   }
9080
9081   /* element reached destination field */
9082
9083   Feld[x][y] = EL_EMPTY;
9084   Feld[newx][newy] = element;
9085   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9086
9087   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9088   {
9089     element = Feld[newx][newy] = EL_ACID;
9090   }
9091   else if (element == EL_MOLE)
9092   {
9093     Feld[x][y] = EL_SAND;
9094
9095     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9096   }
9097   else if (element == EL_QUICKSAND_FILLING)
9098   {
9099     element = Feld[newx][newy] = get_next_element(element);
9100     Store[newx][newy] = Store[x][y];
9101   }
9102   else if (element == EL_QUICKSAND_EMPTYING)
9103   {
9104     Feld[x][y] = get_next_element(element);
9105     element = Feld[newx][newy] = Store[x][y];
9106   }
9107   else if (element == EL_QUICKSAND_FAST_FILLING)
9108   {
9109     element = Feld[newx][newy] = get_next_element(element);
9110     Store[newx][newy] = Store[x][y];
9111   }
9112   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9113   {
9114     Feld[x][y] = get_next_element(element);
9115     element = Feld[newx][newy] = Store[x][y];
9116   }
9117   else if (element == EL_MAGIC_WALL_FILLING)
9118   {
9119     element = Feld[newx][newy] = get_next_element(element);
9120     if (!game.magic_wall_active)
9121       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9122     Store[newx][newy] = Store[x][y];
9123   }
9124   else if (element == EL_MAGIC_WALL_EMPTYING)
9125   {
9126     Feld[x][y] = get_next_element(element);
9127     if (!game.magic_wall_active)
9128       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9129     element = Feld[newx][newy] = Store[x][y];
9130
9131 #if USE_NEW_CUSTOM_VALUE
9132     InitField(newx, newy, FALSE);
9133 #endif
9134   }
9135   else if (element == EL_BD_MAGIC_WALL_FILLING)
9136   {
9137     element = Feld[newx][newy] = get_next_element(element);
9138     if (!game.magic_wall_active)
9139       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9140     Store[newx][newy] = Store[x][y];
9141   }
9142   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9143   {
9144     Feld[x][y] = get_next_element(element);
9145     if (!game.magic_wall_active)
9146       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9147     element = Feld[newx][newy] = Store[x][y];
9148
9149 #if USE_NEW_CUSTOM_VALUE
9150     InitField(newx, newy, FALSE);
9151 #endif
9152   }
9153   else if (element == EL_DC_MAGIC_WALL_FILLING)
9154   {
9155     element = Feld[newx][newy] = get_next_element(element);
9156     if (!game.magic_wall_active)
9157       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9158     Store[newx][newy] = Store[x][y];
9159   }
9160   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9161   {
9162     Feld[x][y] = get_next_element(element);
9163     if (!game.magic_wall_active)
9164       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9165     element = Feld[newx][newy] = Store[x][y];
9166
9167 #if USE_NEW_CUSTOM_VALUE
9168     InitField(newx, newy, FALSE);
9169 #endif
9170   }
9171   else if (element == EL_AMOEBA_DROPPING)
9172   {
9173     Feld[x][y] = get_next_element(element);
9174     element = Feld[newx][newy] = Store[x][y];
9175   }
9176   else if (element == EL_SOKOBAN_OBJECT)
9177   {
9178     if (Back[x][y])
9179       Feld[x][y] = Back[x][y];
9180
9181     if (Back[newx][newy])
9182       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9183
9184     Back[x][y] = Back[newx][newy] = 0;
9185   }
9186
9187   Store[x][y] = EL_EMPTY;
9188   MovPos[x][y] = 0;
9189   MovDir[x][y] = 0;
9190   MovDelay[x][y] = 0;
9191
9192   MovDelay[newx][newy] = 0;
9193
9194   if (CAN_CHANGE_OR_HAS_ACTION(element))
9195   {
9196     /* copy element change control values to new field */
9197     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9198     ChangePage[newx][newy]  = ChangePage[x][y];
9199     ChangeCount[newx][newy] = ChangeCount[x][y];
9200     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9201   }
9202
9203 #if USE_NEW_CUSTOM_VALUE
9204   CustomValue[newx][newy] = CustomValue[x][y];
9205 #endif
9206
9207   ChangeDelay[x][y] = 0;
9208   ChangePage[x][y] = -1;
9209   ChangeCount[x][y] = 0;
9210   ChangeEvent[x][y] = -1;
9211
9212 #if USE_NEW_CUSTOM_VALUE
9213   CustomValue[x][y] = 0;
9214 #endif
9215
9216   /* copy animation control values to new field */
9217   GfxFrame[newx][newy]  = GfxFrame[x][y];
9218   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9219   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9220   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9221
9222   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9223
9224   /* some elements can leave other elements behind after moving */
9225 #if 1
9226   if (ei->move_leave_element != EL_EMPTY &&
9227       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9228       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9229 #else
9230   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9231       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9232       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9233 #endif
9234   {
9235     int move_leave_element = ei->move_leave_element;
9236
9237 #if 1
9238 #if 1
9239     /* this makes it possible to leave the removed element again */
9240     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9241       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9242 #else
9243     /* this makes it possible to leave the removed element again */
9244     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9245       move_leave_element = stored;
9246 #endif
9247 #else
9248     /* this makes it possible to leave the removed element again */
9249     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9250         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9251       move_leave_element = stored;
9252 #endif
9253
9254     Feld[x][y] = move_leave_element;
9255
9256     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9257       MovDir[x][y] = direction;
9258
9259     InitField(x, y, FALSE);
9260
9261     if (GFX_CRUMBLED(Feld[x][y]))
9262       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9263
9264     if (ELEM_IS_PLAYER(move_leave_element))
9265       RelocatePlayer(x, y, move_leave_element);
9266   }
9267
9268   /* do this after checking for left-behind element */
9269   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9270
9271   if (!CAN_MOVE(element) ||
9272       (CAN_FALL(element) && direction == MV_DOWN &&
9273        (element == EL_SPRING ||
9274         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9275         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9276     GfxDir[x][y] = MovDir[newx][newy] = 0;
9277
9278   TEST_DrawLevelField(x, y);
9279   TEST_DrawLevelField(newx, newy);
9280
9281   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9282
9283   /* prevent pushed element from moving on in pushed direction */
9284   if (pushed_by_player && CAN_MOVE(element) &&
9285       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9286       !(element_info[element].move_pattern & direction))
9287     TurnRound(newx, newy);
9288
9289   /* prevent elements on conveyor belt from moving on in last direction */
9290   if (pushed_by_conveyor && CAN_FALL(element) &&
9291       direction & MV_HORIZONTAL)
9292     MovDir[newx][newy] = 0;
9293
9294   if (!pushed_by_player)
9295   {
9296     int nextx = newx + dx, nexty = newy + dy;
9297     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9298
9299     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9300
9301     if (CAN_FALL(element) && direction == MV_DOWN)
9302       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9303
9304     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9305       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9306
9307 #if USE_FIX_IMPACT_COLLISION
9308     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9309       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9310 #endif
9311   }
9312
9313   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9314   {
9315     TestIfBadThingTouchesPlayer(newx, newy);
9316     TestIfBadThingTouchesFriend(newx, newy);
9317
9318     if (!IS_CUSTOM_ELEMENT(element))
9319       TestIfBadThingTouchesOtherBadThing(newx, newy);
9320   }
9321   else if (element == EL_PENGUIN)
9322     TestIfFriendTouchesBadThing(newx, newy);
9323
9324   if (DONT_GET_HIT_BY(element))
9325   {
9326     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9327   }
9328
9329   /* give the player one last chance (one more frame) to move away */
9330   if (CAN_FALL(element) && direction == MV_DOWN &&
9331       (last_line || (!IS_FREE(x, newy + 1) &&
9332                      (!IS_PLAYER(x, newy + 1) ||
9333                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9334     Impact(x, newy);
9335
9336   if (pushed_by_player && !game.use_change_when_pushing_bug)
9337   {
9338     int push_side = MV_DIR_OPPOSITE(direction);
9339     struct PlayerInfo *player = PLAYERINFO(x, y);
9340
9341     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9342                                player->index_bit, push_side);
9343     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9344                                         player->index_bit, push_side);
9345   }
9346
9347   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9348     MovDelay[newx][newy] = 1;
9349
9350   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9351
9352   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9353
9354 #if 0
9355   if (ChangePage[newx][newy] != -1)             /* delayed change */
9356   {
9357     int page = ChangePage[newx][newy];
9358     struct ElementChangeInfo *change = &ei->change_page[page];
9359
9360     ChangePage[newx][newy] = -1;
9361
9362     if (change->can_change)
9363     {
9364       if (ChangeElement(newx, newy, element, page))
9365       {
9366         if (change->post_change_function)
9367           change->post_change_function(newx, newy);
9368       }
9369     }
9370
9371     if (change->has_action)
9372       ExecuteCustomElementAction(newx, newy, element, page);
9373   }
9374 #endif
9375
9376   TestIfElementHitsCustomElement(newx, newy, direction);
9377   TestIfPlayerTouchesCustomElement(newx, newy);
9378   TestIfElementTouchesCustomElement(newx, newy);
9379
9380   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9381       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9382     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9383                              MV_DIR_OPPOSITE(direction));
9384 }
9385
9386 int AmoebeNachbarNr(int ax, int ay)
9387 {
9388   int i;
9389   int element = Feld[ax][ay];
9390   int group_nr = 0;
9391   static int xy[4][2] =
9392   {
9393     { 0, -1 },
9394     { -1, 0 },
9395     { +1, 0 },
9396     { 0, +1 }
9397   };
9398
9399   for (i = 0; i < NUM_DIRECTIONS; i++)
9400   {
9401     int x = ax + xy[i][0];
9402     int y = ay + xy[i][1];
9403
9404     if (!IN_LEV_FIELD(x, y))
9405       continue;
9406
9407     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9408       group_nr = AmoebaNr[x][y];
9409   }
9410
9411   return group_nr;
9412 }
9413
9414 void AmoebenVereinigen(int ax, int ay)
9415 {
9416   int i, x, y, xx, yy;
9417   int new_group_nr = AmoebaNr[ax][ay];
9418   static int xy[4][2] =
9419   {
9420     { 0, -1 },
9421     { -1, 0 },
9422     { +1, 0 },
9423     { 0, +1 }
9424   };
9425
9426   if (new_group_nr == 0)
9427     return;
9428
9429   for (i = 0; i < NUM_DIRECTIONS; i++)
9430   {
9431     x = ax + xy[i][0];
9432     y = ay + xy[i][1];
9433
9434     if (!IN_LEV_FIELD(x, y))
9435       continue;
9436
9437     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9438          Feld[x][y] == EL_BD_AMOEBA ||
9439          Feld[x][y] == EL_AMOEBA_DEAD) &&
9440         AmoebaNr[x][y] != new_group_nr)
9441     {
9442       int old_group_nr = AmoebaNr[x][y];
9443
9444       if (old_group_nr == 0)
9445         return;
9446
9447       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9448       AmoebaCnt[old_group_nr] = 0;
9449       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9450       AmoebaCnt2[old_group_nr] = 0;
9451
9452       SCAN_PLAYFIELD(xx, yy)
9453       {
9454         if (AmoebaNr[xx][yy] == old_group_nr)
9455           AmoebaNr[xx][yy] = new_group_nr;
9456       }
9457     }
9458   }
9459 }
9460
9461 void AmoebeUmwandeln(int ax, int ay)
9462 {
9463   int i, x, y;
9464
9465   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9466   {
9467     int group_nr = AmoebaNr[ax][ay];
9468
9469 #ifdef DEBUG
9470     if (group_nr == 0)
9471     {
9472       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9473       printf("AmoebeUmwandeln(): This should never happen!\n");
9474       return;
9475     }
9476 #endif
9477
9478     SCAN_PLAYFIELD(x, y)
9479     {
9480       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9481       {
9482         AmoebaNr[x][y] = 0;
9483         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9484       }
9485     }
9486
9487     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9488                             SND_AMOEBA_TURNING_TO_GEM :
9489                             SND_AMOEBA_TURNING_TO_ROCK));
9490     Bang(ax, ay);
9491   }
9492   else
9493   {
9494     static int xy[4][2] =
9495     {
9496       { 0, -1 },
9497       { -1, 0 },
9498       { +1, 0 },
9499       { 0, +1 }
9500     };
9501
9502     for (i = 0; i < NUM_DIRECTIONS; i++)
9503     {
9504       x = ax + xy[i][0];
9505       y = ay + xy[i][1];
9506
9507       if (!IN_LEV_FIELD(x, y))
9508         continue;
9509
9510       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9511       {
9512         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9513                               SND_AMOEBA_TURNING_TO_GEM :
9514                               SND_AMOEBA_TURNING_TO_ROCK));
9515         Bang(x, y);
9516       }
9517     }
9518   }
9519 }
9520
9521 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9522 {
9523   int x, y;
9524   int group_nr = AmoebaNr[ax][ay];
9525   boolean done = FALSE;
9526
9527 #ifdef DEBUG
9528   if (group_nr == 0)
9529   {
9530     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9531     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9532     return;
9533   }
9534 #endif
9535
9536   SCAN_PLAYFIELD(x, y)
9537   {
9538     if (AmoebaNr[x][y] == group_nr &&
9539         (Feld[x][y] == EL_AMOEBA_DEAD ||
9540          Feld[x][y] == EL_BD_AMOEBA ||
9541          Feld[x][y] == EL_AMOEBA_GROWING))
9542     {
9543       AmoebaNr[x][y] = 0;
9544       Feld[x][y] = new_element;
9545       InitField(x, y, FALSE);
9546       TEST_DrawLevelField(x, y);
9547       done = TRUE;
9548     }
9549   }
9550
9551   if (done)
9552     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9553                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9554                             SND_BD_AMOEBA_TURNING_TO_GEM));
9555 }
9556
9557 void AmoebeWaechst(int x, int y)
9558 {
9559   static unsigned int sound_delay = 0;
9560   static unsigned int sound_delay_value = 0;
9561
9562   if (!MovDelay[x][y])          /* start new growing cycle */
9563   {
9564     MovDelay[x][y] = 7;
9565
9566     if (DelayReached(&sound_delay, sound_delay_value))
9567     {
9568       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9569       sound_delay_value = 30;
9570     }
9571   }
9572
9573   if (MovDelay[x][y])           /* wait some time before growing bigger */
9574   {
9575     MovDelay[x][y]--;
9576     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9577     {
9578       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9579                                            6 - MovDelay[x][y]);
9580
9581       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9582     }
9583
9584     if (!MovDelay[x][y])
9585     {
9586       Feld[x][y] = Store[x][y];
9587       Store[x][y] = 0;
9588       TEST_DrawLevelField(x, y);
9589     }
9590   }
9591 }
9592
9593 void AmoebaDisappearing(int x, int y)
9594 {
9595   static unsigned int sound_delay = 0;
9596   static unsigned int sound_delay_value = 0;
9597
9598   if (!MovDelay[x][y])          /* start new shrinking cycle */
9599   {
9600     MovDelay[x][y] = 7;
9601
9602     if (DelayReached(&sound_delay, sound_delay_value))
9603       sound_delay_value = 30;
9604   }
9605
9606   if (MovDelay[x][y])           /* wait some time before shrinking */
9607   {
9608     MovDelay[x][y]--;
9609     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9610     {
9611       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9612                                            6 - MovDelay[x][y]);
9613
9614       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9615     }
9616
9617     if (!MovDelay[x][y])
9618     {
9619       Feld[x][y] = EL_EMPTY;
9620       TEST_DrawLevelField(x, y);
9621
9622       /* don't let mole enter this field in this cycle;
9623          (give priority to objects falling to this field from above) */
9624       Stop[x][y] = TRUE;
9625     }
9626   }
9627 }
9628
9629 void AmoebeAbleger(int ax, int ay)
9630 {
9631   int i;
9632   int element = Feld[ax][ay];
9633   int graphic = el2img(element);
9634   int newax = ax, neway = ay;
9635   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9636   static int xy[4][2] =
9637   {
9638     { 0, -1 },
9639     { -1, 0 },
9640     { +1, 0 },
9641     { 0, +1 }
9642   };
9643
9644   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9645   {
9646     Feld[ax][ay] = EL_AMOEBA_DEAD;
9647     TEST_DrawLevelField(ax, ay);
9648     return;
9649   }
9650
9651   if (IS_ANIMATED(graphic))
9652     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9653
9654   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9655     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9656
9657   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9658   {
9659     MovDelay[ax][ay]--;
9660     if (MovDelay[ax][ay])
9661       return;
9662   }
9663
9664   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9665   {
9666     int start = RND(4);
9667     int x = ax + xy[start][0];
9668     int y = ay + xy[start][1];
9669
9670     if (!IN_LEV_FIELD(x, y))
9671       return;
9672
9673     if (IS_FREE(x, y) ||
9674         CAN_GROW_INTO(Feld[x][y]) ||
9675         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9676         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9677     {
9678       newax = x;
9679       neway = y;
9680     }
9681
9682     if (newax == ax && neway == ay)
9683       return;
9684   }
9685   else                          /* normal or "filled" (BD style) amoeba */
9686   {
9687     int start = RND(4);
9688     boolean waiting_for_player = FALSE;
9689
9690     for (i = 0; i < NUM_DIRECTIONS; i++)
9691     {
9692       int j = (start + i) % 4;
9693       int x = ax + xy[j][0];
9694       int y = ay + xy[j][1];
9695
9696       if (!IN_LEV_FIELD(x, y))
9697         continue;
9698
9699       if (IS_FREE(x, y) ||
9700           CAN_GROW_INTO(Feld[x][y]) ||
9701           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9702           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9703       {
9704         newax = x;
9705         neway = y;
9706         break;
9707       }
9708       else if (IS_PLAYER(x, y))
9709         waiting_for_player = TRUE;
9710     }
9711
9712     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9713     {
9714       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9715       {
9716         Feld[ax][ay] = EL_AMOEBA_DEAD;
9717         TEST_DrawLevelField(ax, ay);
9718         AmoebaCnt[AmoebaNr[ax][ay]]--;
9719
9720         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9721         {
9722           if (element == EL_AMOEBA_FULL)
9723             AmoebeUmwandeln(ax, ay);
9724           else if (element == EL_BD_AMOEBA)
9725             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9726         }
9727       }
9728       return;
9729     }
9730     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9731     {
9732       /* amoeba gets larger by growing in some direction */
9733
9734       int new_group_nr = AmoebaNr[ax][ay];
9735
9736 #ifdef DEBUG
9737   if (new_group_nr == 0)
9738   {
9739     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9740     printf("AmoebeAbleger(): This should never happen!\n");
9741     return;
9742   }
9743 #endif
9744
9745       AmoebaNr[newax][neway] = new_group_nr;
9746       AmoebaCnt[new_group_nr]++;
9747       AmoebaCnt2[new_group_nr]++;
9748
9749       /* if amoeba touches other amoeba(s) after growing, unify them */
9750       AmoebenVereinigen(newax, neway);
9751
9752       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9753       {
9754         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9755         return;
9756       }
9757     }
9758   }
9759
9760   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9761       (neway == lev_fieldy - 1 && newax != ax))
9762   {
9763     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9764     Store[newax][neway] = element;
9765   }
9766   else if (neway == ay || element == EL_EMC_DRIPPER)
9767   {
9768     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9769
9770     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9771   }
9772   else
9773   {
9774     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9775     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9776     Store[ax][ay] = EL_AMOEBA_DROP;
9777     ContinueMoving(ax, ay);
9778     return;
9779   }
9780
9781   TEST_DrawLevelField(newax, neway);
9782 }
9783
9784 void Life(int ax, int ay)
9785 {
9786   int x1, y1, x2, y2;
9787   int life_time = 40;
9788   int element = Feld[ax][ay];
9789   int graphic = el2img(element);
9790   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9791                          level.biomaze);
9792   boolean changed = FALSE;
9793
9794   if (IS_ANIMATED(graphic))
9795     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9796
9797   if (Stop[ax][ay])
9798     return;
9799
9800   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9801     MovDelay[ax][ay] = life_time;
9802
9803   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9804   {
9805     MovDelay[ax][ay]--;
9806     if (MovDelay[ax][ay])
9807       return;
9808   }
9809
9810   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9811   {
9812     int xx = ax+x1, yy = ay+y1;
9813     int nachbarn = 0;
9814
9815     if (!IN_LEV_FIELD(xx, yy))
9816       continue;
9817
9818     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9819     {
9820       int x = xx+x2, y = yy+y2;
9821
9822       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9823         continue;
9824
9825       if (((Feld[x][y] == element ||
9826             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9827            !Stop[x][y]) ||
9828           (IS_FREE(x, y) && Stop[x][y]))
9829         nachbarn++;
9830     }
9831
9832     if (xx == ax && yy == ay)           /* field in the middle */
9833     {
9834       if (nachbarn < life_parameter[0] ||
9835           nachbarn > life_parameter[1])
9836       {
9837         Feld[xx][yy] = EL_EMPTY;
9838         if (!Stop[xx][yy])
9839           TEST_DrawLevelField(xx, yy);
9840         Stop[xx][yy] = TRUE;
9841         changed = TRUE;
9842       }
9843     }
9844     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9845     {                                   /* free border field */
9846       if (nachbarn >= life_parameter[2] &&
9847           nachbarn <= life_parameter[3])
9848       {
9849         Feld[xx][yy] = element;
9850         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9851         if (!Stop[xx][yy])
9852           TEST_DrawLevelField(xx, yy);
9853         Stop[xx][yy] = TRUE;
9854         changed = TRUE;
9855       }
9856     }
9857   }
9858
9859   if (changed)
9860     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9861                    SND_GAME_OF_LIFE_GROWING);
9862 }
9863
9864 static void InitRobotWheel(int x, int y)
9865 {
9866   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9867 }
9868
9869 static void RunRobotWheel(int x, int y)
9870 {
9871   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9872 }
9873
9874 static void StopRobotWheel(int x, int y)
9875 {
9876   if (ZX == x && ZY == y)
9877   {
9878     ZX = ZY = -1;
9879
9880     game.robot_wheel_active = FALSE;
9881   }
9882 }
9883
9884 static void InitTimegateWheel(int x, int y)
9885 {
9886   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9887 }
9888
9889 static void RunTimegateWheel(int x, int y)
9890 {
9891   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9892 }
9893
9894 static void InitMagicBallDelay(int x, int y)
9895 {
9896 #if 1
9897   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9898 #else
9899   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9900 #endif
9901 }
9902
9903 static void ActivateMagicBall(int bx, int by)
9904 {
9905   int x, y;
9906
9907   if (level.ball_random)
9908   {
9909     int pos_border = RND(8);    /* select one of the eight border elements */
9910     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9911     int xx = pos_content % 3;
9912     int yy = pos_content / 3;
9913
9914     x = bx - 1 + xx;
9915     y = by - 1 + yy;
9916
9917     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9918       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9919   }
9920   else
9921   {
9922     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9923     {
9924       int xx = x - bx + 1;
9925       int yy = y - by + 1;
9926
9927       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9928         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9929     }
9930   }
9931
9932   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9933 }
9934
9935 void CheckExit(int x, int y)
9936 {
9937   if (local_player->gems_still_needed > 0 ||
9938       local_player->sokobanfields_still_needed > 0 ||
9939       local_player->lights_still_needed > 0)
9940   {
9941     int element = Feld[x][y];
9942     int graphic = el2img(element);
9943
9944     if (IS_ANIMATED(graphic))
9945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9946
9947     return;
9948   }
9949
9950   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9951     return;
9952
9953   Feld[x][y] = EL_EXIT_OPENING;
9954
9955   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9956 }
9957
9958 void CheckExitEM(int x, int y)
9959 {
9960   if (local_player->gems_still_needed > 0 ||
9961       local_player->sokobanfields_still_needed > 0 ||
9962       local_player->lights_still_needed > 0)
9963   {
9964     int element = Feld[x][y];
9965     int graphic = el2img(element);
9966
9967     if (IS_ANIMATED(graphic))
9968       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9969
9970     return;
9971   }
9972
9973   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9974     return;
9975
9976   Feld[x][y] = EL_EM_EXIT_OPENING;
9977
9978   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9979 }
9980
9981 void CheckExitSteel(int x, int y)
9982 {
9983   if (local_player->gems_still_needed > 0 ||
9984       local_player->sokobanfields_still_needed > 0 ||
9985       local_player->lights_still_needed > 0)
9986   {
9987     int element = Feld[x][y];
9988     int graphic = el2img(element);
9989
9990     if (IS_ANIMATED(graphic))
9991       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9992
9993     return;
9994   }
9995
9996   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9997     return;
9998
9999   Feld[x][y] = EL_STEEL_EXIT_OPENING;
10000
10001   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10002 }
10003
10004 void CheckExitSteelEM(int x, int y)
10005 {
10006   if (local_player->gems_still_needed > 0 ||
10007       local_player->sokobanfields_still_needed > 0 ||
10008       local_player->lights_still_needed > 0)
10009   {
10010     int element = Feld[x][y];
10011     int graphic = el2img(element);
10012
10013     if (IS_ANIMATED(graphic))
10014       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10015
10016     return;
10017   }
10018
10019   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10020     return;
10021
10022   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10023
10024   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10025 }
10026
10027 void CheckExitSP(int x, int y)
10028 {
10029   if (local_player->gems_still_needed > 0)
10030   {
10031     int element = Feld[x][y];
10032     int graphic = el2img(element);
10033
10034     if (IS_ANIMATED(graphic))
10035       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10036
10037     return;
10038   }
10039
10040   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10041     return;
10042
10043   Feld[x][y] = EL_SP_EXIT_OPENING;
10044
10045   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10046 }
10047
10048 static void CloseAllOpenTimegates()
10049 {
10050   int x, y;
10051
10052   SCAN_PLAYFIELD(x, y)
10053   {
10054     int element = Feld[x][y];
10055
10056     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10057     {
10058       Feld[x][y] = EL_TIMEGATE_CLOSING;
10059
10060       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10061     }
10062   }
10063 }
10064
10065 void DrawTwinkleOnField(int x, int y)
10066 {
10067   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10068     return;
10069
10070   if (Feld[x][y] == EL_BD_DIAMOND)
10071     return;
10072
10073   if (MovDelay[x][y] == 0)      /* next animation frame */
10074     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10075
10076   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10077   {
10078     MovDelay[x][y]--;
10079
10080     DrawLevelElementAnimation(x, y, Feld[x][y]);
10081
10082     if (MovDelay[x][y] != 0)
10083     {
10084       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10085                                            10 - MovDelay[x][y]);
10086
10087       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10088     }
10089   }
10090 }
10091
10092 void MauerWaechst(int x, int y)
10093 {
10094   int delay = 6;
10095
10096   if (!MovDelay[x][y])          /* next animation frame */
10097     MovDelay[x][y] = 3 * delay;
10098
10099   if (MovDelay[x][y])           /* wait some time before next frame */
10100   {
10101     MovDelay[x][y]--;
10102
10103     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10104     {
10105       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10106       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10107
10108       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10109     }
10110
10111     if (!MovDelay[x][y])
10112     {
10113       if (MovDir[x][y] == MV_LEFT)
10114       {
10115         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10116           TEST_DrawLevelField(x - 1, y);
10117       }
10118       else if (MovDir[x][y] == MV_RIGHT)
10119       {
10120         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10121           TEST_DrawLevelField(x + 1, y);
10122       }
10123       else if (MovDir[x][y] == MV_UP)
10124       {
10125         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10126           TEST_DrawLevelField(x, y - 1);
10127       }
10128       else
10129       {
10130         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10131           TEST_DrawLevelField(x, y + 1);
10132       }
10133
10134       Feld[x][y] = Store[x][y];
10135       Store[x][y] = 0;
10136       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10137       TEST_DrawLevelField(x, y);
10138     }
10139   }
10140 }
10141
10142 void MauerAbleger(int ax, int ay)
10143 {
10144   int element = Feld[ax][ay];
10145   int graphic = el2img(element);
10146   boolean oben_frei = FALSE, unten_frei = FALSE;
10147   boolean links_frei = FALSE, rechts_frei = FALSE;
10148   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10149   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10150   boolean new_wall = FALSE;
10151
10152   if (IS_ANIMATED(graphic))
10153     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10154
10155   if (!MovDelay[ax][ay])        /* start building new wall */
10156     MovDelay[ax][ay] = 6;
10157
10158   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10159   {
10160     MovDelay[ax][ay]--;
10161     if (MovDelay[ax][ay])
10162       return;
10163   }
10164
10165   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10166     oben_frei = TRUE;
10167   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10168     unten_frei = TRUE;
10169   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10170     links_frei = TRUE;
10171   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10172     rechts_frei = TRUE;
10173
10174   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10175       element == EL_EXPANDABLE_WALL_ANY)
10176   {
10177     if (oben_frei)
10178     {
10179       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10180       Store[ax][ay-1] = element;
10181       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10182       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10183         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10184                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10185       new_wall = TRUE;
10186     }
10187     if (unten_frei)
10188     {
10189       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10190       Store[ax][ay+1] = element;
10191       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10192       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10193         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10194                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10195       new_wall = TRUE;
10196     }
10197   }
10198
10199   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10200       element == EL_EXPANDABLE_WALL_ANY ||
10201       element == EL_EXPANDABLE_WALL ||
10202       element == EL_BD_EXPANDABLE_WALL)
10203   {
10204     if (links_frei)
10205     {
10206       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10207       Store[ax-1][ay] = element;
10208       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10209       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10210         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10211                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10212       new_wall = TRUE;
10213     }
10214
10215     if (rechts_frei)
10216     {
10217       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10218       Store[ax+1][ay] = element;
10219       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10220       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10221         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10222                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10223       new_wall = TRUE;
10224     }
10225   }
10226
10227   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10228     TEST_DrawLevelField(ax, ay);
10229
10230   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10231     oben_massiv = TRUE;
10232   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10233     unten_massiv = TRUE;
10234   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10235     links_massiv = TRUE;
10236   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10237     rechts_massiv = TRUE;
10238
10239   if (((oben_massiv && unten_massiv) ||
10240        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10241        element == EL_EXPANDABLE_WALL) &&
10242       ((links_massiv && rechts_massiv) ||
10243        element == EL_EXPANDABLE_WALL_VERTICAL))
10244     Feld[ax][ay] = EL_WALL;
10245
10246   if (new_wall)
10247     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10248 }
10249
10250 void MauerAblegerStahl(int ax, int ay)
10251 {
10252   int element = Feld[ax][ay];
10253   int graphic = el2img(element);
10254   boolean oben_frei = FALSE, unten_frei = FALSE;
10255   boolean links_frei = FALSE, rechts_frei = FALSE;
10256   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10257   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10258   boolean new_wall = FALSE;
10259
10260   if (IS_ANIMATED(graphic))
10261     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10262
10263   if (!MovDelay[ax][ay])        /* start building new wall */
10264     MovDelay[ax][ay] = 6;
10265
10266   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10267   {
10268     MovDelay[ax][ay]--;
10269     if (MovDelay[ax][ay])
10270       return;
10271   }
10272
10273   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10274     oben_frei = TRUE;
10275   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10276     unten_frei = TRUE;
10277   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10278     links_frei = TRUE;
10279   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10280     rechts_frei = TRUE;
10281
10282   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10283       element == EL_EXPANDABLE_STEELWALL_ANY)
10284   {
10285     if (oben_frei)
10286     {
10287       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10288       Store[ax][ay-1] = element;
10289       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10290       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10291         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10292                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10293       new_wall = TRUE;
10294     }
10295     if (unten_frei)
10296     {
10297       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10298       Store[ax][ay+1] = element;
10299       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10300       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10301         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10302                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10303       new_wall = TRUE;
10304     }
10305   }
10306
10307   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10308       element == EL_EXPANDABLE_STEELWALL_ANY)
10309   {
10310     if (links_frei)
10311     {
10312       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10313       Store[ax-1][ay] = element;
10314       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10315       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10316         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10317                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10318       new_wall = TRUE;
10319     }
10320
10321     if (rechts_frei)
10322     {
10323       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10324       Store[ax+1][ay] = element;
10325       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10326       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10327         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10328                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10329       new_wall = TRUE;
10330     }
10331   }
10332
10333   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10334     oben_massiv = TRUE;
10335   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10336     unten_massiv = TRUE;
10337   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10338     links_massiv = TRUE;
10339   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10340     rechts_massiv = TRUE;
10341
10342   if (((oben_massiv && unten_massiv) ||
10343        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10344       ((links_massiv && rechts_massiv) ||
10345        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10346     Feld[ax][ay] = EL_STEELWALL;
10347
10348   if (new_wall)
10349     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10350 }
10351
10352 void CheckForDragon(int x, int y)
10353 {
10354   int i, j;
10355   boolean dragon_found = FALSE;
10356   static int xy[4][2] =
10357   {
10358     { 0, -1 },
10359     { -1, 0 },
10360     { +1, 0 },
10361     { 0, +1 }
10362   };
10363
10364   for (i = 0; i < NUM_DIRECTIONS; i++)
10365   {
10366     for (j = 0; j < 4; j++)
10367     {
10368       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10369
10370       if (IN_LEV_FIELD(xx, yy) &&
10371           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10372       {
10373         if (Feld[xx][yy] == EL_DRAGON)
10374           dragon_found = TRUE;
10375       }
10376       else
10377         break;
10378     }
10379   }
10380
10381   if (!dragon_found)
10382   {
10383     for (i = 0; i < NUM_DIRECTIONS; i++)
10384     {
10385       for (j = 0; j < 3; j++)
10386       {
10387         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10388   
10389         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10390         {
10391           Feld[xx][yy] = EL_EMPTY;
10392           TEST_DrawLevelField(xx, yy);
10393         }
10394         else
10395           break;
10396       }
10397     }
10398   }
10399 }
10400
10401 static void InitBuggyBase(int x, int y)
10402 {
10403   int element = Feld[x][y];
10404   int activating_delay = FRAMES_PER_SECOND / 4;
10405
10406   ChangeDelay[x][y] =
10407     (element == EL_SP_BUGGY_BASE ?
10408      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10409      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10410      activating_delay :
10411      element == EL_SP_BUGGY_BASE_ACTIVE ?
10412      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10413 }
10414
10415 static void WarnBuggyBase(int x, int y)
10416 {
10417   int i;
10418   static int xy[4][2] =
10419   {
10420     { 0, -1 },
10421     { -1, 0 },
10422     { +1, 0 },
10423     { 0, +1 }
10424   };
10425
10426   for (i = 0; i < NUM_DIRECTIONS; i++)
10427   {
10428     int xx = x + xy[i][0];
10429     int yy = y + xy[i][1];
10430
10431     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10432     {
10433       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10434
10435       break;
10436     }
10437   }
10438 }
10439
10440 static void InitTrap(int x, int y)
10441 {
10442   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10443 }
10444
10445 static void ActivateTrap(int x, int y)
10446 {
10447   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10448 }
10449
10450 static void ChangeActiveTrap(int x, int y)
10451 {
10452   int graphic = IMG_TRAP_ACTIVE;
10453
10454   /* if new animation frame was drawn, correct crumbled sand border */
10455   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10456     TEST_DrawLevelFieldCrumbled(x, y);
10457 }
10458
10459 static int getSpecialActionElement(int element, int number, int base_element)
10460 {
10461   return (element != EL_EMPTY ? element :
10462           number != -1 ? base_element + number - 1 :
10463           EL_EMPTY);
10464 }
10465
10466 static int getModifiedActionNumber(int value_old, int operator, int operand,
10467                                    int value_min, int value_max)
10468 {
10469   int value_new = (operator == CA_MODE_SET      ? operand :
10470                    operator == CA_MODE_ADD      ? value_old + operand :
10471                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10472                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10473                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10474                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10475                    value_old);
10476
10477   return (value_new < value_min ? value_min :
10478           value_new > value_max ? value_max :
10479           value_new);
10480 }
10481
10482 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10483 {
10484   struct ElementInfo *ei = &element_info[element];
10485   struct ElementChangeInfo *change = &ei->change_page[page];
10486   int target_element = change->target_element;
10487   int action_type = change->action_type;
10488   int action_mode = change->action_mode;
10489   int action_arg = change->action_arg;
10490   int action_element = change->action_element;
10491   int i;
10492
10493   if (!change->has_action)
10494     return;
10495
10496   /* ---------- determine action paramater values -------------------------- */
10497
10498   int level_time_value =
10499     (level.time > 0 ? TimeLeft :
10500      TimePlayed);
10501
10502   int action_arg_element_raw =
10503     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10504      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10505      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10506      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10507      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10508      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10509      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10510      EL_EMPTY);
10511   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10512
10513 #if 0
10514   if (action_arg_element_raw == EL_GROUP_START)
10515     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10516 #endif
10517
10518   int action_arg_direction =
10519     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10520      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10521      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10522      change->actual_trigger_side :
10523      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10524      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10525      MV_NONE);
10526
10527   int action_arg_number_min =
10528     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10529      CA_ARG_MIN);
10530
10531   int action_arg_number_max =
10532     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10533      action_type == CA_SET_LEVEL_GEMS ? 999 :
10534      action_type == CA_SET_LEVEL_TIME ? 9999 :
10535      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10536      action_type == CA_SET_CE_VALUE ? 9999 :
10537      action_type == CA_SET_CE_SCORE ? 9999 :
10538      CA_ARG_MAX);
10539
10540   int action_arg_number_reset =
10541     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10542      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10543      action_type == CA_SET_LEVEL_TIME ? level.time :
10544      action_type == CA_SET_LEVEL_SCORE ? 0 :
10545 #if USE_NEW_CUSTOM_VALUE
10546      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10547 #else
10548      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10549 #endif
10550      action_type == CA_SET_CE_SCORE ? 0 :
10551      0);
10552
10553   int action_arg_number =
10554     (action_arg <= CA_ARG_MAX ? action_arg :
10555      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10556      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10557      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10558      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10559      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10560      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10561 #if USE_NEW_CUSTOM_VALUE
10562      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10563 #else
10564      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10565 #endif
10566      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10567      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10568      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10569      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10570      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10571      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10572      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10573      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10574      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10575      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10576      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10577      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10578      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10579      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10580      -1);
10581
10582   int action_arg_number_old =
10583     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10584      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10585      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10586      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10587      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10588      0);
10589
10590   int action_arg_number_new =
10591     getModifiedActionNumber(action_arg_number_old,
10592                             action_mode, action_arg_number,
10593                             action_arg_number_min, action_arg_number_max);
10594
10595 #if 1
10596   int trigger_player_bits =
10597     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10598      change->actual_trigger_player_bits : change->trigger_player);
10599 #else
10600   int trigger_player_bits =
10601     (change->actual_trigger_player >= EL_PLAYER_1 &&
10602      change->actual_trigger_player <= EL_PLAYER_4 ?
10603      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10604      PLAYER_BITS_ANY);
10605 #endif
10606
10607   int action_arg_player_bits =
10608     (action_arg >= CA_ARG_PLAYER_1 &&
10609      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10610      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10611      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10612      PLAYER_BITS_ANY);
10613
10614   /* ---------- execute action  -------------------------------------------- */
10615
10616   switch (action_type)
10617   {
10618     case CA_NO_ACTION:
10619     {
10620       return;
10621     }
10622
10623     /* ---------- level actions  ------------------------------------------- */
10624
10625     case CA_RESTART_LEVEL:
10626     {
10627       game.restart_level = TRUE;
10628
10629       break;
10630     }
10631
10632     case CA_SHOW_ENVELOPE:
10633     {
10634       int element = getSpecialActionElement(action_arg_element,
10635                                             action_arg_number, EL_ENVELOPE_1);
10636
10637       if (IS_ENVELOPE(element))
10638         local_player->show_envelope = element;
10639
10640       break;
10641     }
10642
10643     case CA_SET_LEVEL_TIME:
10644     {
10645       if (level.time > 0)       /* only modify limited time value */
10646       {
10647         TimeLeft = action_arg_number_new;
10648
10649 #if 1
10650         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10651
10652         DisplayGameControlValues();
10653 #else
10654         DrawGameValue_Time(TimeLeft);
10655 #endif
10656
10657         if (!TimeLeft && setup.time_limit)
10658           for (i = 0; i < MAX_PLAYERS; i++)
10659             KillPlayer(&stored_player[i]);
10660       }
10661
10662       break;
10663     }
10664
10665     case CA_SET_LEVEL_SCORE:
10666     {
10667       local_player->score = action_arg_number_new;
10668
10669 #if 1
10670       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10671
10672       DisplayGameControlValues();
10673 #else
10674       DrawGameValue_Score(local_player->score);
10675 #endif
10676
10677       break;
10678     }
10679
10680     case CA_SET_LEVEL_GEMS:
10681     {
10682       local_player->gems_still_needed = action_arg_number_new;
10683
10684 #if 1
10685       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10686
10687       DisplayGameControlValues();
10688 #else
10689       DrawGameValue_Emeralds(local_player->gems_still_needed);
10690 #endif
10691
10692       break;
10693     }
10694
10695 #if !USE_PLAYER_GRAVITY
10696     case CA_SET_LEVEL_GRAVITY:
10697     {
10698       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10699                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10700                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10701                       game.gravity);
10702       break;
10703     }
10704 #endif
10705
10706     case CA_SET_LEVEL_WIND:
10707     {
10708       game.wind_direction = action_arg_direction;
10709
10710       break;
10711     }
10712
10713     case CA_SET_LEVEL_RANDOM_SEED:
10714     {
10715 #if 1
10716       /* ensure that setting a new random seed while playing is predictable */
10717       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10718 #else
10719       InitRND(action_arg_number_new);
10720 #endif
10721
10722 #if 0
10723       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10724 #endif
10725
10726 #if 0
10727       {
10728         int i;
10729
10730         printf("::: ");
10731         for (i = 0; i < 9; i++)
10732           printf("%d, ", RND(2));
10733         printf("\n");
10734       }
10735 #endif
10736
10737       break;
10738     }
10739
10740     /* ---------- player actions  ------------------------------------------ */
10741
10742     case CA_MOVE_PLAYER:
10743     {
10744       /* automatically move to the next field in specified direction */
10745       for (i = 0; i < MAX_PLAYERS; i++)
10746         if (trigger_player_bits & (1 << i))
10747           stored_player[i].programmed_action = action_arg_direction;
10748
10749       break;
10750     }
10751
10752     case CA_EXIT_PLAYER:
10753     {
10754       for (i = 0; i < MAX_PLAYERS; i++)
10755         if (action_arg_player_bits & (1 << i))
10756           PlayerWins(&stored_player[i]);
10757
10758       break;
10759     }
10760
10761     case CA_KILL_PLAYER:
10762     {
10763       for (i = 0; i < MAX_PLAYERS; i++)
10764         if (action_arg_player_bits & (1 << i))
10765           KillPlayer(&stored_player[i]);
10766
10767       break;
10768     }
10769
10770     case CA_SET_PLAYER_KEYS:
10771     {
10772       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10773       int element = getSpecialActionElement(action_arg_element,
10774                                             action_arg_number, EL_KEY_1);
10775
10776       if (IS_KEY(element))
10777       {
10778         for (i = 0; i < MAX_PLAYERS; i++)
10779         {
10780           if (trigger_player_bits & (1 << i))
10781           {
10782             stored_player[i].key[KEY_NR(element)] = key_state;
10783
10784             DrawGameDoorValues();
10785           }
10786         }
10787       }
10788
10789       break;
10790     }
10791
10792     case CA_SET_PLAYER_SPEED:
10793     {
10794 #if 0
10795       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10796 #endif
10797
10798       for (i = 0; i < MAX_PLAYERS; i++)
10799       {
10800         if (trigger_player_bits & (1 << i))
10801         {
10802           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10803
10804           if (action_arg == CA_ARG_SPEED_FASTER &&
10805               stored_player[i].cannot_move)
10806           {
10807             action_arg_number = STEPSIZE_VERY_SLOW;
10808           }
10809           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10810                    action_arg == CA_ARG_SPEED_FASTER)
10811           {
10812             action_arg_number = 2;
10813             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10814                            CA_MODE_MULTIPLY);
10815           }
10816           else if (action_arg == CA_ARG_NUMBER_RESET)
10817           {
10818             action_arg_number = level.initial_player_stepsize[i];
10819           }
10820
10821           move_stepsize =
10822             getModifiedActionNumber(move_stepsize,
10823                                     action_mode,
10824                                     action_arg_number,
10825                                     action_arg_number_min,
10826                                     action_arg_number_max);
10827
10828           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10829         }
10830       }
10831
10832       break;
10833     }
10834
10835     case CA_SET_PLAYER_SHIELD:
10836     {
10837       for (i = 0; i < MAX_PLAYERS; i++)
10838       {
10839         if (trigger_player_bits & (1 << i))
10840         {
10841           if (action_arg == CA_ARG_SHIELD_OFF)
10842           {
10843             stored_player[i].shield_normal_time_left = 0;
10844             stored_player[i].shield_deadly_time_left = 0;
10845           }
10846           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10847           {
10848             stored_player[i].shield_normal_time_left = 999999;
10849           }
10850           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10851           {
10852             stored_player[i].shield_normal_time_left = 999999;
10853             stored_player[i].shield_deadly_time_left = 999999;
10854           }
10855         }
10856       }
10857
10858       break;
10859     }
10860
10861 #if USE_PLAYER_GRAVITY
10862     case CA_SET_PLAYER_GRAVITY:
10863     {
10864       for (i = 0; i < MAX_PLAYERS; i++)
10865       {
10866         if (trigger_player_bits & (1 << i))
10867         {
10868           stored_player[i].gravity =
10869             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10870              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10871              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10872              stored_player[i].gravity);
10873         }
10874       }
10875
10876       break;
10877     }
10878 #endif
10879
10880     case CA_SET_PLAYER_ARTWORK:
10881     {
10882       for (i = 0; i < MAX_PLAYERS; i++)
10883       {
10884         if (trigger_player_bits & (1 << i))
10885         {
10886           int artwork_element = action_arg_element;
10887
10888           if (action_arg == CA_ARG_ELEMENT_RESET)
10889             artwork_element =
10890               (level.use_artwork_element[i] ? level.artwork_element[i] :
10891                stored_player[i].element_nr);
10892
10893 #if USE_GFX_RESET_PLAYER_ARTWORK
10894           if (stored_player[i].artwork_element != artwork_element)
10895             stored_player[i].Frame = 0;
10896 #endif
10897
10898           stored_player[i].artwork_element = artwork_element;
10899
10900           SetPlayerWaiting(&stored_player[i], FALSE);
10901
10902           /* set number of special actions for bored and sleeping animation */
10903           stored_player[i].num_special_action_bored =
10904             get_num_special_action(artwork_element,
10905                                    ACTION_BORING_1, ACTION_BORING_LAST);
10906           stored_player[i].num_special_action_sleeping =
10907             get_num_special_action(artwork_element,
10908                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10909         }
10910       }
10911
10912       break;
10913     }
10914
10915     case CA_SET_PLAYER_INVENTORY:
10916     {
10917       for (i = 0; i < MAX_PLAYERS; i++)
10918       {
10919         struct PlayerInfo *player = &stored_player[i];
10920         int j, k;
10921
10922         if (trigger_player_bits & (1 << i))
10923         {
10924           int inventory_element = action_arg_element;
10925
10926           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10927               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10928               action_arg == CA_ARG_ELEMENT_ACTION)
10929           {
10930             int element = inventory_element;
10931             int collect_count = element_info[element].collect_count_initial;
10932
10933             if (!IS_CUSTOM_ELEMENT(element))
10934               collect_count = 1;
10935
10936             if (collect_count == 0)
10937               player->inventory_infinite_element = element;
10938             else
10939               for (k = 0; k < collect_count; k++)
10940                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10941                   player->inventory_element[player->inventory_size++] =
10942                     element;
10943           }
10944           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10945                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10946                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10947           {
10948             if (player->inventory_infinite_element != EL_UNDEFINED &&
10949                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10950                                      action_arg_element_raw))
10951               player->inventory_infinite_element = EL_UNDEFINED;
10952
10953             for (k = 0, j = 0; j < player->inventory_size; j++)
10954             {
10955               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10956                                         action_arg_element_raw))
10957                 player->inventory_element[k++] = player->inventory_element[j];
10958             }
10959
10960             player->inventory_size = k;
10961           }
10962           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10963           {
10964             if (player->inventory_size > 0)
10965             {
10966               for (j = 0; j < player->inventory_size - 1; j++)
10967                 player->inventory_element[j] = player->inventory_element[j + 1];
10968
10969               player->inventory_size--;
10970             }
10971           }
10972           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10973           {
10974             if (player->inventory_size > 0)
10975               player->inventory_size--;
10976           }
10977           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10978           {
10979             player->inventory_infinite_element = EL_UNDEFINED;
10980             player->inventory_size = 0;
10981           }
10982           else if (action_arg == CA_ARG_INVENTORY_RESET)
10983           {
10984             player->inventory_infinite_element = EL_UNDEFINED;
10985             player->inventory_size = 0;
10986
10987             if (level.use_initial_inventory[i])
10988             {
10989               for (j = 0; j < level.initial_inventory_size[i]; j++)
10990               {
10991                 int element = level.initial_inventory_content[i][j];
10992                 int collect_count = element_info[element].collect_count_initial;
10993
10994                 if (!IS_CUSTOM_ELEMENT(element))
10995                   collect_count = 1;
10996
10997                 if (collect_count == 0)
10998                   player->inventory_infinite_element = element;
10999                 else
11000                   for (k = 0; k < collect_count; k++)
11001                     if (player->inventory_size < MAX_INVENTORY_SIZE)
11002                       player->inventory_element[player->inventory_size++] =
11003                         element;
11004               }
11005             }
11006           }
11007         }
11008       }
11009
11010       break;
11011     }
11012
11013     /* ---------- CE actions  ---------------------------------------------- */
11014
11015     case CA_SET_CE_VALUE:
11016     {
11017 #if USE_NEW_CUSTOM_VALUE
11018       int last_ce_value = CustomValue[x][y];
11019
11020       CustomValue[x][y] = action_arg_number_new;
11021
11022       if (CustomValue[x][y] != last_ce_value)
11023       {
11024         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11025         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11026
11027         if (CustomValue[x][y] == 0)
11028         {
11029           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11030           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11031         }
11032       }
11033 #endif
11034
11035       break;
11036     }
11037
11038     case CA_SET_CE_SCORE:
11039     {
11040 #if USE_NEW_CUSTOM_VALUE
11041       int last_ce_score = ei->collect_score;
11042
11043       ei->collect_score = action_arg_number_new;
11044
11045       if (ei->collect_score != last_ce_score)
11046       {
11047         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11048         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11049
11050         if (ei->collect_score == 0)
11051         {
11052           int xx, yy;
11053
11054           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11055           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11056
11057           /*
11058             This is a very special case that seems to be a mixture between
11059             CheckElementChange() and CheckTriggeredElementChange(): while
11060             the first one only affects single elements that are triggered
11061             directly, the second one affects multiple elements in the playfield
11062             that are triggered indirectly by another element. This is a third
11063             case: Changing the CE score always affects multiple identical CEs,
11064             so every affected CE must be checked, not only the single CE for
11065             which the CE score was changed in the first place (as every instance
11066             of that CE shares the same CE score, and therefore also can change)!
11067           */
11068           SCAN_PLAYFIELD(xx, yy)
11069           {
11070             if (Feld[xx][yy] == element)
11071               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11072                                  CE_SCORE_GETS_ZERO);
11073           }
11074         }
11075       }
11076 #endif
11077
11078       break;
11079     }
11080
11081     case CA_SET_CE_ARTWORK:
11082     {
11083       int artwork_element = action_arg_element;
11084       boolean reset_frame = FALSE;
11085       int xx, yy;
11086
11087       if (action_arg == CA_ARG_ELEMENT_RESET)
11088         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11089                            element);
11090
11091       if (ei->gfx_element != artwork_element)
11092         reset_frame = TRUE;
11093
11094       ei->gfx_element = artwork_element;
11095
11096       SCAN_PLAYFIELD(xx, yy)
11097       {
11098         if (Feld[xx][yy] == element)
11099         {
11100           if (reset_frame)
11101           {
11102             ResetGfxAnimation(xx, yy);
11103             ResetRandomAnimationValue(xx, yy);
11104           }
11105
11106           TEST_DrawLevelField(xx, yy);
11107         }
11108       }
11109
11110       break;
11111     }
11112
11113     /* ---------- engine actions  ------------------------------------------ */
11114
11115     case CA_SET_ENGINE_SCAN_MODE:
11116     {
11117       InitPlayfieldScanMode(action_arg);
11118
11119       break;
11120     }
11121
11122     default:
11123       break;
11124   }
11125 }
11126
11127 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11128 {
11129   int old_element = Feld[x][y];
11130   int new_element = GetElementFromGroupElement(element);
11131   int previous_move_direction = MovDir[x][y];
11132 #if USE_NEW_CUSTOM_VALUE
11133   int last_ce_value = CustomValue[x][y];
11134 #endif
11135   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11136   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11137   boolean add_player_onto_element = (new_element_is_player &&
11138 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11139                                      /* this breaks SnakeBite when a snake is
11140                                         halfway through a door that closes */
11141                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11142                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11143 #endif
11144                                      IS_WALKABLE(old_element));
11145
11146 #if 0
11147   /* check if element under the player changes from accessible to unaccessible
11148      (needed for special case of dropping element which then changes) */
11149   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11150       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11151   {
11152     Bang(x, y);
11153
11154     return;
11155   }
11156 #endif
11157
11158   if (!add_player_onto_element)
11159   {
11160     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11161       RemoveMovingField(x, y);
11162     else
11163       RemoveField(x, y);
11164
11165     Feld[x][y] = new_element;
11166
11167 #if !USE_GFX_RESET_GFX_ANIMATION
11168     ResetGfxAnimation(x, y);
11169     ResetRandomAnimationValue(x, y);
11170 #endif
11171
11172     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11173       MovDir[x][y] = previous_move_direction;
11174
11175 #if USE_NEW_CUSTOM_VALUE
11176     if (element_info[new_element].use_last_ce_value)
11177       CustomValue[x][y] = last_ce_value;
11178 #endif
11179
11180     InitField_WithBug1(x, y, FALSE);
11181
11182     new_element = Feld[x][y];   /* element may have changed */
11183
11184 #if USE_GFX_RESET_GFX_ANIMATION
11185     ResetGfxAnimation(x, y);
11186     ResetRandomAnimationValue(x, y);
11187 #endif
11188
11189     TEST_DrawLevelField(x, y);
11190
11191     if (GFX_CRUMBLED(new_element))
11192       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11193   }
11194
11195 #if 1
11196   /* check if element under the player changes from accessible to unaccessible
11197      (needed for special case of dropping element which then changes) */
11198   /* (must be checked after creating new element for walkable group elements) */
11199 #if USE_FIX_KILLED_BY_NON_WALKABLE
11200   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11201       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11202   {
11203     Bang(x, y);
11204
11205     return;
11206   }
11207 #else
11208   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11209       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11210   {
11211     Bang(x, y);
11212
11213     return;
11214   }
11215 #endif
11216 #endif
11217
11218   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11219   if (new_element_is_player)
11220     RelocatePlayer(x, y, new_element);
11221
11222   if (is_change)
11223     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11224
11225   TestIfBadThingTouchesPlayer(x, y);
11226   TestIfPlayerTouchesCustomElement(x, y);
11227   TestIfElementTouchesCustomElement(x, y);
11228 }
11229
11230 static void CreateField(int x, int y, int element)
11231 {
11232   CreateFieldExt(x, y, element, FALSE);
11233 }
11234
11235 static void CreateElementFromChange(int x, int y, int element)
11236 {
11237   element = GET_VALID_RUNTIME_ELEMENT(element);
11238
11239 #if USE_STOP_CHANGED_ELEMENTS
11240   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11241   {
11242     int old_element = Feld[x][y];
11243
11244     /* prevent changed element from moving in same engine frame
11245        unless both old and new element can either fall or move */
11246     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11247         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11248       Stop[x][y] = TRUE;
11249   }
11250 #endif
11251
11252   CreateFieldExt(x, y, element, TRUE);
11253 }
11254
11255 static boolean ChangeElement(int x, int y, int element, int page)
11256 {
11257   struct ElementInfo *ei = &element_info[element];
11258   struct ElementChangeInfo *change = &ei->change_page[page];
11259   int ce_value = CustomValue[x][y];
11260   int ce_score = ei->collect_score;
11261   int target_element;
11262   int old_element = Feld[x][y];
11263
11264   /* always use default change event to prevent running into a loop */
11265   if (ChangeEvent[x][y] == -1)
11266     ChangeEvent[x][y] = CE_DELAY;
11267
11268   if (ChangeEvent[x][y] == CE_DELAY)
11269   {
11270     /* reset actual trigger element, trigger player and action element */
11271     change->actual_trigger_element = EL_EMPTY;
11272     change->actual_trigger_player = EL_EMPTY;
11273     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11274     change->actual_trigger_side = CH_SIDE_NONE;
11275     change->actual_trigger_ce_value = 0;
11276     change->actual_trigger_ce_score = 0;
11277   }
11278
11279   /* do not change elements more than a specified maximum number of changes */
11280   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11281     return FALSE;
11282
11283   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11284
11285   if (change->explode)
11286   {
11287     Bang(x, y);
11288
11289     return TRUE;
11290   }
11291
11292   if (change->use_target_content)
11293   {
11294     boolean complete_replace = TRUE;
11295     boolean can_replace[3][3];
11296     int xx, yy;
11297
11298     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11299     {
11300       boolean is_empty;
11301       boolean is_walkable;
11302       boolean is_diggable;
11303       boolean is_collectible;
11304       boolean is_removable;
11305       boolean is_destructible;
11306       int ex = x + xx - 1;
11307       int ey = y + yy - 1;
11308       int content_element = change->target_content.e[xx][yy];
11309       int e;
11310
11311       can_replace[xx][yy] = TRUE;
11312
11313       if (ex == x && ey == y)   /* do not check changing element itself */
11314         continue;
11315
11316       if (content_element == EL_EMPTY_SPACE)
11317       {
11318         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11319
11320         continue;
11321       }
11322
11323       if (!IN_LEV_FIELD(ex, ey))
11324       {
11325         can_replace[xx][yy] = FALSE;
11326         complete_replace = FALSE;
11327
11328         continue;
11329       }
11330
11331       e = Feld[ex][ey];
11332
11333       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11334         e = MovingOrBlocked2Element(ex, ey);
11335
11336       is_empty = (IS_FREE(ex, ey) ||
11337                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11338
11339       is_walkable     = (is_empty || IS_WALKABLE(e));
11340       is_diggable     = (is_empty || IS_DIGGABLE(e));
11341       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11342       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11343       is_removable    = (is_diggable || is_collectible);
11344
11345       can_replace[xx][yy] =
11346         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11347           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11348           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11349           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11350           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11351           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11352          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11353
11354       if (!can_replace[xx][yy])
11355         complete_replace = FALSE;
11356     }
11357
11358     if (!change->only_if_complete || complete_replace)
11359     {
11360       boolean something_has_changed = FALSE;
11361
11362       if (change->only_if_complete && change->use_random_replace &&
11363           RND(100) < change->random_percentage)
11364         return FALSE;
11365
11366       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11367       {
11368         int ex = x + xx - 1;
11369         int ey = y + yy - 1;
11370         int content_element;
11371
11372         if (can_replace[xx][yy] && (!change->use_random_replace ||
11373                                     RND(100) < change->random_percentage))
11374         {
11375           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11376             RemoveMovingField(ex, ey);
11377
11378           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11379
11380           content_element = change->target_content.e[xx][yy];
11381           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11382                                               ce_value, ce_score);
11383
11384           CreateElementFromChange(ex, ey, target_element);
11385
11386           something_has_changed = TRUE;
11387
11388           /* for symmetry reasons, freeze newly created border elements */
11389           if (ex != x || ey != y)
11390             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11391         }
11392       }
11393
11394       if (something_has_changed)
11395       {
11396         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11397         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11398       }
11399     }
11400   }
11401   else
11402   {
11403     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11404                                         ce_value, ce_score);
11405
11406     if (element == EL_DIAGONAL_GROWING ||
11407         element == EL_DIAGONAL_SHRINKING)
11408     {
11409       target_element = Store[x][y];
11410
11411       Store[x][y] = EL_EMPTY;
11412     }
11413
11414     CreateElementFromChange(x, y, target_element);
11415
11416     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11417     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11418   }
11419
11420   /* this uses direct change before indirect change */
11421   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11422
11423   return TRUE;
11424 }
11425
11426 #if USE_NEW_DELAYED_ACTION
11427
11428 static void HandleElementChange(int x, int y, int page)
11429 {
11430   int element = MovingOrBlocked2Element(x, y);
11431   struct ElementInfo *ei = &element_info[element];
11432   struct ElementChangeInfo *change = &ei->change_page[page];
11433   boolean handle_action_before_change = FALSE;
11434
11435 #ifdef DEBUG
11436   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11437       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11438   {
11439     printf("\n\n");
11440     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11441            x, y, element, element_info[element].token_name);
11442     printf("HandleElementChange(): This should never happen!\n");
11443     printf("\n\n");
11444   }
11445 #endif
11446
11447   /* this can happen with classic bombs on walkable, changing elements */
11448   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11449   {
11450 #if 0
11451     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11452       ChangeDelay[x][y] = 0;
11453 #endif
11454
11455     return;
11456   }
11457
11458   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11459   {
11460     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11461
11462     if (change->can_change)
11463     {
11464 #if 1
11465       /* !!! not clear why graphic animation should be reset at all here !!! */
11466       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11467 #if USE_GFX_RESET_WHEN_NOT_MOVING
11468       /* when a custom element is about to change (for example by change delay),
11469          do not reset graphic animation when the custom element is moving */
11470       if (!IS_MOVING(x, y))
11471 #endif
11472       {
11473         ResetGfxAnimation(x, y);
11474         ResetRandomAnimationValue(x, y);
11475       }
11476 #endif
11477
11478       if (change->pre_change_function)
11479         change->pre_change_function(x, y);
11480     }
11481   }
11482
11483   ChangeDelay[x][y]--;
11484
11485   if (ChangeDelay[x][y] != 0)           /* continue element change */
11486   {
11487     if (change->can_change)
11488     {
11489       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11490
11491       if (IS_ANIMATED(graphic))
11492         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11493
11494       if (change->change_function)
11495         change->change_function(x, y);
11496     }
11497   }
11498   else                                  /* finish element change */
11499   {
11500     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11501     {
11502       page = ChangePage[x][y];
11503       ChangePage[x][y] = -1;
11504
11505       change = &ei->change_page[page];
11506     }
11507
11508     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11509     {
11510       ChangeDelay[x][y] = 1;            /* try change after next move step */
11511       ChangePage[x][y] = page;          /* remember page to use for change */
11512
11513       return;
11514     }
11515
11516 #if 1
11517     /* special case: set new level random seed before changing element */
11518     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11519       handle_action_before_change = TRUE;
11520
11521     if (change->has_action && handle_action_before_change)
11522       ExecuteCustomElementAction(x, y, element, page);
11523 #endif
11524
11525     if (change->can_change)
11526     {
11527       if (ChangeElement(x, y, element, page))
11528       {
11529         if (change->post_change_function)
11530           change->post_change_function(x, y);
11531       }
11532     }
11533
11534     if (change->has_action && !handle_action_before_change)
11535       ExecuteCustomElementAction(x, y, element, page);
11536   }
11537 }
11538
11539 #else
11540
11541 static void HandleElementChange(int x, int y, int page)
11542 {
11543   int element = MovingOrBlocked2Element(x, y);
11544   struct ElementInfo *ei = &element_info[element];
11545   struct ElementChangeInfo *change = &ei->change_page[page];
11546
11547 #ifdef DEBUG
11548   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11549   {
11550     printf("\n\n");
11551     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11552            x, y, element, element_info[element].token_name);
11553     printf("HandleElementChange(): This should never happen!\n");
11554     printf("\n\n");
11555   }
11556 #endif
11557
11558   /* this can happen with classic bombs on walkable, changing elements */
11559   if (!CAN_CHANGE(element))
11560   {
11561 #if 0
11562     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11563       ChangeDelay[x][y] = 0;
11564 #endif
11565
11566     return;
11567   }
11568
11569   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11570   {
11571     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11572
11573     ResetGfxAnimation(x, y);
11574     ResetRandomAnimationValue(x, y);
11575
11576     if (change->pre_change_function)
11577       change->pre_change_function(x, y);
11578   }
11579
11580   ChangeDelay[x][y]--;
11581
11582   if (ChangeDelay[x][y] != 0)           /* continue element change */
11583   {
11584     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11585
11586     if (IS_ANIMATED(graphic))
11587       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11588
11589     if (change->change_function)
11590       change->change_function(x, y);
11591   }
11592   else                                  /* finish element change */
11593   {
11594     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11595     {
11596       page = ChangePage[x][y];
11597       ChangePage[x][y] = -1;
11598
11599       change = &ei->change_page[page];
11600     }
11601
11602     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11603     {
11604       ChangeDelay[x][y] = 1;            /* try change after next move step */
11605       ChangePage[x][y] = page;          /* remember page to use for change */
11606
11607       return;
11608     }
11609
11610     if (ChangeElement(x, y, element, page))
11611     {
11612       if (change->post_change_function)
11613         change->post_change_function(x, y);
11614     }
11615   }
11616 }
11617
11618 #endif
11619
11620 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11621                                               int trigger_element,
11622                                               int trigger_event,
11623                                               int trigger_player,
11624                                               int trigger_side,
11625                                               int trigger_page)
11626 {
11627   boolean change_done_any = FALSE;
11628   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11629   int i;
11630
11631   if (!(trigger_events[trigger_element][trigger_event]))
11632     return FALSE;
11633
11634 #if 0
11635   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11636          trigger_event, recursion_loop_depth, recursion_loop_detected,
11637          recursion_loop_element, EL_NAME(recursion_loop_element));
11638 #endif
11639
11640   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11641
11642   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11643   {
11644     int element = EL_CUSTOM_START + i;
11645     boolean change_done = FALSE;
11646     int p;
11647
11648     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11649         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11650       continue;
11651
11652     for (p = 0; p < element_info[element].num_change_pages; p++)
11653     {
11654       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11655
11656       if (change->can_change_or_has_action &&
11657           change->has_event[trigger_event] &&
11658           change->trigger_side & trigger_side &&
11659           change->trigger_player & trigger_player &&
11660           change->trigger_page & trigger_page_bits &&
11661           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11662       {
11663         change->actual_trigger_element = trigger_element;
11664         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11665         change->actual_trigger_player_bits = trigger_player;
11666         change->actual_trigger_side = trigger_side;
11667         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11668         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11669
11670 #if 0
11671         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11672                element, EL_NAME(element), p);
11673 #endif
11674
11675         if ((change->can_change && !change_done) || change->has_action)
11676         {
11677           int x, y;
11678
11679           SCAN_PLAYFIELD(x, y)
11680           {
11681             if (Feld[x][y] == element)
11682             {
11683               if (change->can_change && !change_done)
11684               {
11685 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11686                 /* if element already changed in this frame, not only prevent
11687                    another element change (checked in ChangeElement()), but
11688                    also prevent additional element actions for this element */
11689
11690                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11691                     !level.use_action_after_change_bug)
11692                   continue;
11693 #endif
11694
11695 #if 0
11696                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11697                        element, EL_NAME(element), p);
11698 #endif
11699
11700                 ChangeDelay[x][y] = 1;
11701                 ChangeEvent[x][y] = trigger_event;
11702
11703                 HandleElementChange(x, y, p);
11704               }
11705 #if USE_NEW_DELAYED_ACTION
11706               else if (change->has_action)
11707               {
11708 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11709                 /* if element already changed in this frame, not only prevent
11710                    another element change (checked in ChangeElement()), but
11711                    also prevent additional element actions for this element */
11712
11713                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11714                     !level.use_action_after_change_bug)
11715                   continue;
11716 #endif
11717
11718
11719 #if 0
11720                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11721                        element, EL_NAME(element), p);
11722 #endif
11723
11724                 ExecuteCustomElementAction(x, y, element, p);
11725                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11726               }
11727 #else
11728               if (change->has_action)
11729               {
11730                 ExecuteCustomElementAction(x, y, element, p);
11731                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11732               }
11733 #endif
11734             }
11735           }
11736
11737           if (change->can_change)
11738           {
11739             change_done = TRUE;
11740             change_done_any = TRUE;
11741
11742 #if 0
11743             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11744                    element, EL_NAME(element), p);
11745 #endif
11746
11747           }
11748         }
11749       }
11750     }
11751   }
11752
11753   RECURSION_LOOP_DETECTION_END();
11754
11755   return change_done_any;
11756 }
11757
11758 static boolean CheckElementChangeExt(int x, int y,
11759                                      int element,
11760                                      int trigger_element,
11761                                      int trigger_event,
11762                                      int trigger_player,
11763                                      int trigger_side)
11764 {
11765   boolean change_done = FALSE;
11766   int p;
11767
11768   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11769       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11770     return FALSE;
11771
11772   if (Feld[x][y] == EL_BLOCKED)
11773   {
11774     Blocked2Moving(x, y, &x, &y);
11775     element = Feld[x][y];
11776   }
11777
11778 #if 0
11779   /* check if element has already changed */
11780   if (Feld[x][y] != element)
11781     return FALSE;
11782 #else
11783   /* check if element has already changed or is about to change after moving */
11784   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11785        Feld[x][y] != element) ||
11786
11787       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11788        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11789         ChangePage[x][y] != -1)))
11790     return FALSE;
11791 #endif
11792
11793 #if 0
11794   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11795          trigger_event, recursion_loop_depth, recursion_loop_detected,
11796          recursion_loop_element, EL_NAME(recursion_loop_element));
11797 #endif
11798
11799   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11800
11801 #if 0
11802   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11803 #endif
11804
11805   for (p = 0; p < element_info[element].num_change_pages; p++)
11806   {
11807     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11808
11809     /* check trigger element for all events where the element that is checked
11810        for changing interacts with a directly adjacent element -- this is
11811        different to element changes that affect other elements to change on the
11812        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11813     boolean check_trigger_element =
11814       (trigger_event == CE_TOUCHING_X ||
11815        trigger_event == CE_HITTING_X ||
11816        trigger_event == CE_HIT_BY_X ||
11817 #if 1
11818        /* this one was forgotten until 3.2.3 */
11819        trigger_event == CE_DIGGING_X);
11820 #endif
11821
11822     if (change->can_change_or_has_action &&
11823         change->has_event[trigger_event] &&
11824         change->trigger_side & trigger_side &&
11825         change->trigger_player & trigger_player &&
11826         (!check_trigger_element ||
11827          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11828     {
11829       change->actual_trigger_element = trigger_element;
11830       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11831       change->actual_trigger_player_bits = trigger_player;
11832       change->actual_trigger_side = trigger_side;
11833       change->actual_trigger_ce_value = CustomValue[x][y];
11834       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11835
11836       /* special case: trigger element not at (x,y) position for some events */
11837       if (check_trigger_element)
11838       {
11839         static struct
11840         {
11841           int dx, dy;
11842         } move_xy[] =
11843           {
11844             {  0,  0 },
11845             { -1,  0 },
11846             { +1,  0 },
11847             {  0,  0 },
11848             {  0, -1 },
11849             {  0,  0 }, { 0, 0 }, { 0, 0 },
11850             {  0, +1 }
11851           };
11852
11853         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11854         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11855
11856         change->actual_trigger_ce_value = CustomValue[xx][yy];
11857         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11858       }
11859
11860       if (change->can_change && !change_done)
11861       {
11862         ChangeDelay[x][y] = 1;
11863         ChangeEvent[x][y] = trigger_event;
11864
11865         HandleElementChange(x, y, p);
11866
11867         change_done = TRUE;
11868       }
11869 #if USE_NEW_DELAYED_ACTION
11870       else if (change->has_action)
11871       {
11872         ExecuteCustomElementAction(x, y, element, p);
11873         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11874       }
11875 #else
11876       if (change->has_action)
11877       {
11878         ExecuteCustomElementAction(x, y, element, p);
11879         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11880       }
11881 #endif
11882     }
11883   }
11884
11885   RECURSION_LOOP_DETECTION_END();
11886
11887   return change_done;
11888 }
11889
11890 static void PlayPlayerSound(struct PlayerInfo *player)
11891 {
11892   int jx = player->jx, jy = player->jy;
11893   int sound_element = player->artwork_element;
11894   int last_action = player->last_action_waiting;
11895   int action = player->action_waiting;
11896
11897   if (player->is_waiting)
11898   {
11899     if (action != last_action)
11900       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11901     else
11902       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11903   }
11904   else
11905   {
11906     if (action != last_action)
11907       StopSound(element_info[sound_element].sound[last_action]);
11908
11909     if (last_action == ACTION_SLEEPING)
11910       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11911   }
11912 }
11913
11914 static void PlayAllPlayersSound()
11915 {
11916   int i;
11917
11918   for (i = 0; i < MAX_PLAYERS; i++)
11919     if (stored_player[i].active)
11920       PlayPlayerSound(&stored_player[i]);
11921 }
11922
11923 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11924 {
11925   boolean last_waiting = player->is_waiting;
11926   int move_dir = player->MovDir;
11927
11928   player->dir_waiting = move_dir;
11929   player->last_action_waiting = player->action_waiting;
11930
11931   if (is_waiting)
11932   {
11933     if (!last_waiting)          /* not waiting -> waiting */
11934     {
11935       player->is_waiting = TRUE;
11936
11937       player->frame_counter_bored =
11938         FrameCounter +
11939         game.player_boring_delay_fixed +
11940         GetSimpleRandom(game.player_boring_delay_random);
11941       player->frame_counter_sleeping =
11942         FrameCounter +
11943         game.player_sleeping_delay_fixed +
11944         GetSimpleRandom(game.player_sleeping_delay_random);
11945
11946       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11947     }
11948
11949     if (game.player_sleeping_delay_fixed +
11950         game.player_sleeping_delay_random > 0 &&
11951         player->anim_delay_counter == 0 &&
11952         player->post_delay_counter == 0 &&
11953         FrameCounter >= player->frame_counter_sleeping)
11954       player->is_sleeping = TRUE;
11955     else if (game.player_boring_delay_fixed +
11956              game.player_boring_delay_random > 0 &&
11957              FrameCounter >= player->frame_counter_bored)
11958       player->is_bored = TRUE;
11959
11960     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11961                               player->is_bored ? ACTION_BORING :
11962                               ACTION_WAITING);
11963
11964     if (player->is_sleeping && player->use_murphy)
11965     {
11966       /* special case for sleeping Murphy when leaning against non-free tile */
11967
11968       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11969           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11970            !IS_MOVING(player->jx - 1, player->jy)))
11971         move_dir = MV_LEFT;
11972       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11973                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11974                 !IS_MOVING(player->jx + 1, player->jy)))
11975         move_dir = MV_RIGHT;
11976       else
11977         player->is_sleeping = FALSE;
11978
11979       player->dir_waiting = move_dir;
11980     }
11981
11982     if (player->is_sleeping)
11983     {
11984       if (player->num_special_action_sleeping > 0)
11985       {
11986         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11987         {
11988           int last_special_action = player->special_action_sleeping;
11989           int num_special_action = player->num_special_action_sleeping;
11990           int special_action =
11991             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11992              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11993              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11994              last_special_action + 1 : ACTION_SLEEPING);
11995           int special_graphic =
11996             el_act_dir2img(player->artwork_element, special_action, move_dir);
11997
11998           player->anim_delay_counter =
11999             graphic_info[special_graphic].anim_delay_fixed +
12000             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12001           player->post_delay_counter =
12002             graphic_info[special_graphic].post_delay_fixed +
12003             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12004
12005           player->special_action_sleeping = special_action;
12006         }
12007
12008         if (player->anim_delay_counter > 0)
12009         {
12010           player->action_waiting = player->special_action_sleeping;
12011           player->anim_delay_counter--;
12012         }
12013         else if (player->post_delay_counter > 0)
12014         {
12015           player->post_delay_counter--;
12016         }
12017       }
12018     }
12019     else if (player->is_bored)
12020     {
12021       if (player->num_special_action_bored > 0)
12022       {
12023         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12024         {
12025           int special_action =
12026             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12027           int special_graphic =
12028             el_act_dir2img(player->artwork_element, special_action, move_dir);
12029
12030           player->anim_delay_counter =
12031             graphic_info[special_graphic].anim_delay_fixed +
12032             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12033           player->post_delay_counter =
12034             graphic_info[special_graphic].post_delay_fixed +
12035             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12036
12037           player->special_action_bored = special_action;
12038         }
12039
12040         if (player->anim_delay_counter > 0)
12041         {
12042           player->action_waiting = player->special_action_bored;
12043           player->anim_delay_counter--;
12044         }
12045         else if (player->post_delay_counter > 0)
12046         {
12047           player->post_delay_counter--;
12048         }
12049       }
12050     }
12051   }
12052   else if (last_waiting)        /* waiting -> not waiting */
12053   {
12054     player->is_waiting = FALSE;
12055     player->is_bored = FALSE;
12056     player->is_sleeping = FALSE;
12057
12058     player->frame_counter_bored = -1;
12059     player->frame_counter_sleeping = -1;
12060
12061     player->anim_delay_counter = 0;
12062     player->post_delay_counter = 0;
12063
12064     player->dir_waiting = player->MovDir;
12065     player->action_waiting = ACTION_DEFAULT;
12066
12067     player->special_action_bored = ACTION_DEFAULT;
12068     player->special_action_sleeping = ACTION_DEFAULT;
12069   }
12070 }
12071
12072 static void CheckSingleStepMode(struct PlayerInfo *player)
12073 {
12074   if (tape.single_step && tape.recording && !tape.pausing)
12075   {
12076     /* as it is called "single step mode", just return to pause mode when the
12077        player stopped moving after one tile (or never starts moving at all) */
12078     if (!player->is_moving && !player->is_pushing)
12079     {
12080       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12081       SnapField(player, 0, 0);                  /* stop snapping */
12082     }
12083   }
12084 }
12085
12086 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12087 {
12088   int left      = player_action & JOY_LEFT;
12089   int right     = player_action & JOY_RIGHT;
12090   int up        = player_action & JOY_UP;
12091   int down      = player_action & JOY_DOWN;
12092   int button1   = player_action & JOY_BUTTON_1;
12093   int button2   = player_action & JOY_BUTTON_2;
12094   int dx        = (left ? -1 : right ? 1 : 0);
12095   int dy        = (up   ? -1 : down  ? 1 : 0);
12096
12097   if (!player->active || tape.pausing)
12098     return 0;
12099
12100   if (player_action)
12101   {
12102     if (button1)
12103       SnapField(player, dx, dy);
12104     else
12105     {
12106       if (button2)
12107         DropElement(player);
12108
12109       MovePlayer(player, dx, dy);
12110     }
12111
12112     CheckSingleStepMode(player);
12113
12114     SetPlayerWaiting(player, FALSE);
12115
12116     return player_action;
12117   }
12118   else
12119   {
12120     /* no actions for this player (no input at player's configured device) */
12121
12122     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12123     SnapField(player, 0, 0);
12124     CheckGravityMovementWhenNotMoving(player);
12125
12126     if (player->MovPos == 0)
12127       SetPlayerWaiting(player, TRUE);
12128
12129     if (player->MovPos == 0)    /* needed for tape.playing */
12130       player->is_moving = FALSE;
12131
12132     player->is_dropping = FALSE;
12133     player->is_dropping_pressed = FALSE;
12134     player->drop_pressed_delay = 0;
12135
12136     CheckSingleStepMode(player);
12137
12138     return 0;
12139   }
12140 }
12141
12142 static void CheckLevelTime()
12143 {
12144   int i;
12145
12146   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12148   {
12149     if (level.native_em_level->lev->home == 0)  /* all players at home */
12150     {
12151       PlayerWins(local_player);
12152
12153       AllPlayersGone = TRUE;
12154
12155       level.native_em_level->lev->home = -1;
12156     }
12157
12158     if (level.native_em_level->ply[0]->alive == 0 &&
12159         level.native_em_level->ply[1]->alive == 0 &&
12160         level.native_em_level->ply[2]->alive == 0 &&
12161         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12162       AllPlayersGone = TRUE;
12163   }
12164   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12165   {
12166     if (game_sp.LevelSolved &&
12167         !game_sp.GameOver)                              /* game won */
12168     {
12169       PlayerWins(local_player);
12170
12171       game_sp.GameOver = TRUE;
12172
12173       AllPlayersGone = TRUE;
12174     }
12175
12176     if (game_sp.GameOver)                               /* game lost */
12177       AllPlayersGone = TRUE;
12178   }
12179
12180   if (TimeFrames >= FRAMES_PER_SECOND)
12181   {
12182     TimeFrames = 0;
12183     TapeTime++;
12184
12185     for (i = 0; i < MAX_PLAYERS; i++)
12186     {
12187       struct PlayerInfo *player = &stored_player[i];
12188
12189       if (SHIELD_ON(player))
12190       {
12191         player->shield_normal_time_left--;
12192
12193         if (player->shield_deadly_time_left > 0)
12194           player->shield_deadly_time_left--;
12195       }
12196     }
12197
12198     if (!local_player->LevelSolved && !level.use_step_counter)
12199     {
12200       TimePlayed++;
12201
12202       if (TimeLeft > 0)
12203       {
12204         TimeLeft--;
12205
12206         if (TimeLeft <= 10 && setup.time_limit)
12207           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12208
12209 #if 1
12210         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12211            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12212
12213         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12214
12215         /* (already called by UpdateAndDisplayGameControlValues() below) */
12216         // DisplayGameControlValues();
12217 #else
12218         DrawGameValue_Time(TimeLeft);
12219 #endif
12220
12221         if (!TimeLeft && setup.time_limit)
12222         {
12223           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12224             level.native_em_level->lev->killed_out_of_time = TRUE;
12225           else
12226             for (i = 0; i < MAX_PLAYERS; i++)
12227               KillPlayer(&stored_player[i]);
12228         }
12229       }
12230 #if 1
12231       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12232       {
12233         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12234
12235         /* (already called by UpdateAndDisplayGameControlValues() below) */
12236         // DisplayGameControlValues();
12237       }
12238 #else
12239       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12240         DrawGameValue_Time(TimePlayed);
12241 #endif
12242
12243       level.native_em_level->lev->time =
12244         (game.no_time_limit ? TimePlayed : TimeLeft);
12245     }
12246
12247     if (tape.recording || tape.playing)
12248       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12249   }
12250
12251 #if 1
12252   UpdateAndDisplayGameControlValues();
12253 #else
12254   UpdateGameDoorValues();
12255   DrawGameDoorValues();
12256 #endif
12257 }
12258
12259 void AdvanceFrameAndPlayerCounters(int player_nr)
12260 {
12261   int i;
12262
12263   /* advance frame counters (global frame counter and time frame counter) */
12264   FrameCounter++;
12265   TimeFrames++;
12266
12267   /* advance player counters (counters for move delay, move animation etc.) */
12268   for (i = 0; i < MAX_PLAYERS; i++)
12269   {
12270     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12271     int move_delay_value = stored_player[i].move_delay_value;
12272     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12273
12274     if (!advance_player_counters)       /* not all players may be affected */
12275       continue;
12276
12277 #if USE_NEW_PLAYER_ANIM
12278     if (move_frames == 0)       /* less than one move per game frame */
12279     {
12280       int stepsize = TILEX / move_delay_value;
12281       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12282       int count = (stored_player[i].is_moving ?
12283                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12284
12285       if (count % delay == 0)
12286         move_frames = 1;
12287     }
12288 #endif
12289
12290     stored_player[i].Frame += move_frames;
12291
12292     if (stored_player[i].MovPos != 0)
12293       stored_player[i].StepFrame += move_frames;
12294
12295     if (stored_player[i].move_delay > 0)
12296       stored_player[i].move_delay--;
12297
12298     /* due to bugs in previous versions, counter must count up, not down */
12299     if (stored_player[i].push_delay != -1)
12300       stored_player[i].push_delay++;
12301
12302     if (stored_player[i].drop_delay > 0)
12303       stored_player[i].drop_delay--;
12304
12305     if (stored_player[i].is_dropping_pressed)
12306       stored_player[i].drop_pressed_delay++;
12307   }
12308 }
12309
12310 void StartGameActions(boolean init_network_game, boolean record_tape,
12311                       int random_seed)
12312 {
12313   unsigned int new_random_seed = InitRND(random_seed);
12314
12315   if (record_tape)
12316     TapeStartRecording(new_random_seed);
12317
12318 #if defined(NETWORK_AVALIABLE)
12319   if (init_network_game)
12320   {
12321     SendToServer_StartPlaying();
12322
12323     return;
12324   }
12325 #endif
12326
12327   InitGame();
12328 }
12329
12330 void GameActions()
12331 {
12332   static unsigned int game_frame_delay = 0;
12333   unsigned int game_frame_delay_value;
12334   byte *recorded_player_action;
12335   byte summarized_player_action = 0;
12336   byte tape_action[MAX_PLAYERS];
12337   int i;
12338
12339   /* detect endless loops, caused by custom element programming */
12340   if (recursion_loop_detected && recursion_loop_depth == 0)
12341   {
12342     char *message = getStringCat3("Internal Error! Element ",
12343                                   EL_NAME(recursion_loop_element),
12344                                   " caused endless loop! Quit the game?");
12345
12346     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12347           EL_NAME(recursion_loop_element));
12348
12349     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12350
12351     recursion_loop_detected = FALSE;    /* if game should be continued */
12352
12353     free(message);
12354
12355     return;
12356   }
12357
12358   if (game.restart_level)
12359     StartGameActions(options.network, setup.autorecord, level.random_seed);
12360
12361   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12362   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12363   {
12364     if (level.native_em_level->lev->home == 0)  /* all players at home */
12365     {
12366       PlayerWins(local_player);
12367
12368       AllPlayersGone = TRUE;
12369
12370       level.native_em_level->lev->home = -1;
12371     }
12372
12373     if (level.native_em_level->ply[0]->alive == 0 &&
12374         level.native_em_level->ply[1]->alive == 0 &&
12375         level.native_em_level->ply[2]->alive == 0 &&
12376         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12377       AllPlayersGone = TRUE;
12378   }
12379   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12380   {
12381     if (game_sp.LevelSolved &&
12382         !game_sp.GameOver)                              /* game won */
12383     {
12384       PlayerWins(local_player);
12385
12386       game_sp.GameOver = TRUE;
12387
12388       AllPlayersGone = TRUE;
12389     }
12390
12391     if (game_sp.GameOver)                               /* game lost */
12392       AllPlayersGone = TRUE;
12393   }
12394
12395   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12396     GameWon();
12397
12398   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12399     TapeStop();
12400
12401   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12402     return;
12403
12404   game_frame_delay_value =
12405     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12406
12407   if (tape.playing && tape.warp_forward && !tape.pausing)
12408     game_frame_delay_value = 0;
12409
12410   /* ---------- main game synchronization point ---------- */
12411
12412   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12413
12414   if (network_playing && !network_player_action_received)
12415   {
12416     /* try to get network player actions in time */
12417
12418 #if defined(NETWORK_AVALIABLE)
12419     /* last chance to get network player actions without main loop delay */
12420     HandleNetworking();
12421 #endif
12422
12423     /* game was quit by network peer */
12424     if (game_status != GAME_MODE_PLAYING)
12425       return;
12426
12427     if (!network_player_action_received)
12428       return;           /* failed to get network player actions in time */
12429
12430     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12431   }
12432
12433   if (tape.pausing)
12434     return;
12435
12436   /* at this point we know that we really continue executing the game */
12437
12438   network_player_action_received = FALSE;
12439
12440   /* when playing tape, read previously recorded player input from tape data */
12441   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12442
12443 #if 1
12444   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12445   if (tape.pausing)
12446     return;
12447 #endif
12448
12449   if (tape.set_centered_player)
12450   {
12451     game.centered_player_nr_next = tape.centered_player_nr_next;
12452     game.set_centered_player = TRUE;
12453   }
12454
12455   for (i = 0; i < MAX_PLAYERS; i++)
12456   {
12457     summarized_player_action |= stored_player[i].action;
12458
12459 #if 1
12460     if (!network_playing && (game.team_mode || tape.playing))
12461       stored_player[i].effective_action = stored_player[i].action;
12462 #else
12463     if (!network_playing)
12464       stored_player[i].effective_action = stored_player[i].action;
12465 #endif
12466   }
12467
12468 #if defined(NETWORK_AVALIABLE)
12469   if (network_playing)
12470     SendToServer_MovePlayer(summarized_player_action);
12471 #endif
12472
12473   if (!options.network && !game.team_mode)
12474     local_player->effective_action = summarized_player_action;
12475
12476   if (tape.recording &&
12477       setup.team_mode &&
12478       setup.input_on_focus &&
12479       game.centered_player_nr != -1)
12480   {
12481     for (i = 0; i < MAX_PLAYERS; i++)
12482       stored_player[i].effective_action =
12483         (i == game.centered_player_nr ? summarized_player_action : 0);
12484   }
12485
12486   if (recorded_player_action != NULL)
12487     for (i = 0; i < MAX_PLAYERS; i++)
12488       stored_player[i].effective_action = recorded_player_action[i];
12489
12490   for (i = 0; i < MAX_PLAYERS; i++)
12491   {
12492     tape_action[i] = stored_player[i].effective_action;
12493
12494 #if 1
12495     /* (this may happen in the RND game engine if a player was not present on
12496        the playfield on level start, but appeared later from a custom element */
12497     if (tape.recording &&
12498         setup.team_mode &&
12499         tape_action[i] &&
12500         !tape.player_participates[i])
12501       tape.player_participates[i] = TRUE;
12502 #else
12503     /* (this can only happen in the R'n'D game engine) */
12504     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12505       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12506 #endif
12507   }
12508
12509   /* only record actions from input devices, but not programmed actions */
12510   if (tape.recording)
12511     TapeRecordAction(tape_action);
12512
12513 #if USE_NEW_PLAYER_ASSIGNMENTS
12514 #if 1
12515   if (game.team_mode)
12516 #endif
12517   {
12518     byte mapped_action[MAX_PLAYERS];
12519
12520 #if DEBUG_PLAYER_ACTIONS
12521     printf(":::");
12522     for (i = 0; i < MAX_PLAYERS; i++)
12523       printf(" %d, ", stored_player[i].effective_action);
12524 #endif
12525
12526     for (i = 0; i < MAX_PLAYERS; i++)
12527       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12528
12529     for (i = 0; i < MAX_PLAYERS; i++)
12530       stored_player[i].effective_action = mapped_action[i];
12531
12532 #if DEBUG_PLAYER_ACTIONS
12533     printf(" =>");
12534     for (i = 0; i < MAX_PLAYERS; i++)
12535       printf(" %d, ", stored_player[i].effective_action);
12536     printf("\n");
12537 #endif
12538   }
12539 #if DEBUG_PLAYER_ACTIONS
12540   else
12541   {
12542     printf(":::");
12543     for (i = 0; i < MAX_PLAYERS; i++)
12544       printf(" %d, ", stored_player[i].effective_action);
12545     printf("\n");
12546   }
12547 #endif
12548 #endif
12549
12550 #if 0
12551   printf("::: summarized_player_action == %d\n",
12552          local_player->effective_action);
12553 #endif
12554
12555
12556
12557
12558 #if 0
12559 #if DEBUG_INIT_PLAYER
12560     if (options.debug)
12561     {
12562       printf("Player status (final):\n");
12563
12564       for (i = 0; i < MAX_PLAYERS; i++)
12565       {
12566         struct PlayerInfo *player = &stored_player[i];
12567
12568         printf("- player %d: present == %d, connected == %d, active == %d",
12569                i + 1,
12570                player->present,
12571                player->connected,
12572                player->active);
12573
12574         if (local_player == player)
12575           printf(" (local player)");
12576
12577         printf("\n");
12578       }
12579     }
12580 #endif
12581 #endif
12582
12583
12584
12585   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12586   {
12587     GameActions_EM_Main();
12588   }
12589   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12590   {
12591     GameActions_SP_Main();
12592   }
12593   else
12594   {
12595     GameActions_RND();
12596   }
12597 }
12598
12599 void GameActions_EM_Main()
12600 {
12601   byte effective_action[MAX_PLAYERS];
12602   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12603   int i;
12604
12605   for (i = 0; i < MAX_PLAYERS; i++)
12606     effective_action[i] = stored_player[i].effective_action;
12607
12608   GameActions_EM(effective_action, warp_mode);
12609
12610   CheckLevelTime();
12611
12612   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12613 }
12614
12615 void GameActions_SP_Main()
12616 {
12617   byte effective_action[MAX_PLAYERS];
12618   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12619   int i;
12620
12621   for (i = 0; i < MAX_PLAYERS; i++)
12622     effective_action[i] = stored_player[i].effective_action;
12623
12624   GameActions_SP(effective_action, warp_mode);
12625
12626   CheckLevelTime();
12627
12628   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12629 }
12630
12631 void GameActions_RND()
12632 {
12633   int magic_wall_x = 0, magic_wall_y = 0;
12634   int i, x, y, element, graphic;
12635
12636   InitPlayfieldScanModeVars();
12637
12638 #if USE_ONE_MORE_CHANGE_PER_FRAME
12639   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12640   {
12641     SCAN_PLAYFIELD(x, y)
12642     {
12643       ChangeCount[x][y] = 0;
12644       ChangeEvent[x][y] = -1;
12645     }
12646   }
12647 #endif
12648
12649   if (game.set_centered_player)
12650   {
12651     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12652
12653     /* switching to "all players" only possible if all players fit to screen */
12654     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12655     {
12656       game.centered_player_nr_next = game.centered_player_nr;
12657       game.set_centered_player = FALSE;
12658     }
12659
12660     /* do not switch focus to non-existing (or non-active) player */
12661     if (game.centered_player_nr_next >= 0 &&
12662         !stored_player[game.centered_player_nr_next].active)
12663     {
12664       game.centered_player_nr_next = game.centered_player_nr;
12665       game.set_centered_player = FALSE;
12666     }
12667   }
12668
12669   if (game.set_centered_player &&
12670       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12671   {
12672     int sx, sy;
12673
12674     if (game.centered_player_nr_next == -1)
12675     {
12676       setScreenCenteredToAllPlayers(&sx, &sy);
12677     }
12678     else
12679     {
12680       sx = stored_player[game.centered_player_nr_next].jx;
12681       sy = stored_player[game.centered_player_nr_next].jy;
12682     }
12683
12684     game.centered_player_nr = game.centered_player_nr_next;
12685     game.set_centered_player = FALSE;
12686
12687     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12688     DrawGameDoorValues();
12689   }
12690
12691   for (i = 0; i < MAX_PLAYERS; i++)
12692   {
12693     int actual_player_action = stored_player[i].effective_action;
12694
12695 #if 1
12696     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12697        - rnd_equinox_tetrachloride 048
12698        - rnd_equinox_tetrachloride_ii 096
12699        - rnd_emanuel_schmieg 002
12700        - doctor_sloan_ww 001, 020
12701     */
12702     if (stored_player[i].MovPos == 0)
12703       CheckGravityMovement(&stored_player[i]);
12704 #endif
12705
12706     /* overwrite programmed action with tape action */
12707     if (stored_player[i].programmed_action)
12708       actual_player_action = stored_player[i].programmed_action;
12709
12710     PlayerActions(&stored_player[i], actual_player_action);
12711
12712     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12713   }
12714
12715   ScrollScreen(NULL, SCROLL_GO_ON);
12716
12717   /* for backwards compatibility, the following code emulates a fixed bug that
12718      occured when pushing elements (causing elements that just made their last
12719      pushing step to already (if possible) make their first falling step in the
12720      same game frame, which is bad); this code is also needed to use the famous
12721      "spring push bug" which is used in older levels and might be wanted to be
12722      used also in newer levels, but in this case the buggy pushing code is only
12723      affecting the "spring" element and no other elements */
12724
12725   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12726   {
12727     for (i = 0; i < MAX_PLAYERS; i++)
12728     {
12729       struct PlayerInfo *player = &stored_player[i];
12730       int x = player->jx;
12731       int y = player->jy;
12732
12733       if (player->active && player->is_pushing && player->is_moving &&
12734           IS_MOVING(x, y) &&
12735           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12736            Feld[x][y] == EL_SPRING))
12737       {
12738         ContinueMoving(x, y);
12739
12740         /* continue moving after pushing (this is actually a bug) */
12741         if (!IS_MOVING(x, y))
12742           Stop[x][y] = FALSE;
12743       }
12744     }
12745   }
12746
12747 #if 0
12748   debug_print_timestamp(0, "start main loop profiling");
12749 #endif
12750
12751   SCAN_PLAYFIELD(x, y)
12752   {
12753     ChangeCount[x][y] = 0;
12754     ChangeEvent[x][y] = -1;
12755
12756     /* this must be handled before main playfield loop */
12757     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12758     {
12759       MovDelay[x][y]--;
12760       if (MovDelay[x][y] <= 0)
12761         RemoveField(x, y);
12762     }
12763
12764 #if USE_NEW_SNAP_DELAY
12765     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12766     {
12767       MovDelay[x][y]--;
12768       if (MovDelay[x][y] <= 0)
12769       {
12770         RemoveField(x, y);
12771         TEST_DrawLevelField(x, y);
12772
12773         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12774       }
12775     }
12776 #endif
12777
12778 #if DEBUG
12779     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12780     {
12781       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12782       printf("GameActions(): This should never happen!\n");
12783
12784       ChangePage[x][y] = -1;
12785     }
12786 #endif
12787
12788     Stop[x][y] = FALSE;
12789     if (WasJustMoving[x][y] > 0)
12790       WasJustMoving[x][y]--;
12791     if (WasJustFalling[x][y] > 0)
12792       WasJustFalling[x][y]--;
12793     if (CheckCollision[x][y] > 0)
12794       CheckCollision[x][y]--;
12795     if (CheckImpact[x][y] > 0)
12796       CheckImpact[x][y]--;
12797
12798     GfxFrame[x][y]++;
12799
12800     /* reset finished pushing action (not done in ContinueMoving() to allow
12801        continuous pushing animation for elements with zero push delay) */
12802     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12803     {
12804       ResetGfxAnimation(x, y);
12805       TEST_DrawLevelField(x, y);
12806     }
12807
12808 #if DEBUG
12809     if (IS_BLOCKED(x, y))
12810     {
12811       int oldx, oldy;
12812
12813       Blocked2Moving(x, y, &oldx, &oldy);
12814       if (!IS_MOVING(oldx, oldy))
12815       {
12816         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12817         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12818         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12819         printf("GameActions(): This should never happen!\n");
12820       }
12821     }
12822 #endif
12823   }
12824
12825 #if 0
12826   debug_print_timestamp(0, "- time for pre-main loop:");
12827 #endif
12828
12829 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12830   SCAN_PLAYFIELD(x, y)
12831   {
12832     element = Feld[x][y];
12833     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12834
12835 #if 1
12836     {
12837 #if 1
12838       int element2 = element;
12839       int graphic2 = graphic;
12840 #else
12841       int element2 = Feld[x][y];
12842       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12843 #endif
12844       int last_gfx_frame = GfxFrame[x][y];
12845
12846       if (graphic_info[graphic2].anim_global_sync)
12847         GfxFrame[x][y] = FrameCounter;
12848       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12849         GfxFrame[x][y] = CustomValue[x][y];
12850       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12851         GfxFrame[x][y] = element_info[element2].collect_score;
12852       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12853         GfxFrame[x][y] = ChangeDelay[x][y];
12854
12855       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12856         DrawLevelGraphicAnimation(x, y, graphic2);
12857     }
12858 #else
12859     ResetGfxFrame(x, y, TRUE);
12860 #endif
12861
12862 #if 1
12863     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12864         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12865       ResetRandomAnimationValue(x, y);
12866 #endif
12867
12868 #if 1
12869     SetRandomAnimationValue(x, y);
12870 #endif
12871
12872 #if 1
12873     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12874 #endif
12875   }
12876 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12877
12878 #if 0
12879   debug_print_timestamp(0, "- time for TEST loop:     -->");
12880 #endif
12881
12882   SCAN_PLAYFIELD(x, y)
12883   {
12884     element = Feld[x][y];
12885     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12886
12887     ResetGfxFrame(x, y, TRUE);
12888
12889     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12890         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12891       ResetRandomAnimationValue(x, y);
12892
12893     SetRandomAnimationValue(x, y);
12894
12895     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12896
12897     if (IS_INACTIVE(element))
12898     {
12899       if (IS_ANIMATED(graphic))
12900         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12901
12902       continue;
12903     }
12904
12905     /* this may take place after moving, so 'element' may have changed */
12906     if (IS_CHANGING(x, y) &&
12907         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12908     {
12909       int page = element_info[element].event_page_nr[CE_DELAY];
12910
12911 #if 1
12912       HandleElementChange(x, y, page);
12913 #else
12914       if (CAN_CHANGE(element))
12915         HandleElementChange(x, y, page);
12916
12917       if (HAS_ACTION(element))
12918         ExecuteCustomElementAction(x, y, element, page);
12919 #endif
12920
12921       element = Feld[x][y];
12922       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12923     }
12924
12925 #if 0   // ---------------------------------------------------------------------
12926
12927     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12928     {
12929       StartMoving(x, y);
12930
12931       element = Feld[x][y];
12932       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12933
12934       if (IS_ANIMATED(graphic) &&
12935           !IS_MOVING(x, y) &&
12936           !Stop[x][y])
12937         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12938
12939       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12940         TEST_DrawTwinkleOnField(x, y);
12941     }
12942     else if (IS_MOVING(x, y))
12943       ContinueMoving(x, y);
12944     else
12945     {
12946       switch (element)
12947       {
12948         case EL_ACID:
12949         case EL_EXIT_OPEN:
12950         case EL_EM_EXIT_OPEN:
12951         case EL_SP_EXIT_OPEN:
12952         case EL_STEEL_EXIT_OPEN:
12953         case EL_EM_STEEL_EXIT_OPEN:
12954         case EL_SP_TERMINAL:
12955         case EL_SP_TERMINAL_ACTIVE:
12956         case EL_EXTRA_TIME:
12957         case EL_SHIELD_NORMAL:
12958         case EL_SHIELD_DEADLY:
12959           if (IS_ANIMATED(graphic))
12960             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12961           break;
12962
12963         case EL_DYNAMITE_ACTIVE:
12964         case EL_EM_DYNAMITE_ACTIVE:
12965         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12966         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12967         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12968         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12969         case EL_SP_DISK_RED_ACTIVE:
12970           CheckDynamite(x, y);
12971           break;
12972
12973         case EL_AMOEBA_GROWING:
12974           AmoebeWaechst(x, y);
12975           break;
12976
12977         case EL_AMOEBA_SHRINKING:
12978           AmoebaDisappearing(x, y);
12979           break;
12980
12981 #if !USE_NEW_AMOEBA_CODE
12982         case EL_AMOEBA_WET:
12983         case EL_AMOEBA_DRY:
12984         case EL_AMOEBA_FULL:
12985         case EL_BD_AMOEBA:
12986         case EL_EMC_DRIPPER:
12987           AmoebeAbleger(x, y);
12988           break;
12989 #endif
12990
12991         case EL_GAME_OF_LIFE:
12992         case EL_BIOMAZE:
12993           Life(x, y);
12994           break;
12995
12996         case EL_EXIT_CLOSED:
12997           CheckExit(x, y);
12998           break;
12999
13000         case EL_EM_EXIT_CLOSED:
13001           CheckExitEM(x, y);
13002           break;
13003
13004         case EL_STEEL_EXIT_CLOSED:
13005           CheckExitSteel(x, y);
13006           break;
13007
13008         case EL_EM_STEEL_EXIT_CLOSED:
13009           CheckExitSteelEM(x, y);
13010           break;
13011
13012         case EL_SP_EXIT_CLOSED:
13013           CheckExitSP(x, y);
13014           break;
13015
13016         case EL_EXPANDABLE_WALL_GROWING:
13017         case EL_EXPANDABLE_STEELWALL_GROWING:
13018           MauerWaechst(x, y);
13019           break;
13020
13021         case EL_EXPANDABLE_WALL:
13022         case EL_EXPANDABLE_WALL_HORIZONTAL:
13023         case EL_EXPANDABLE_WALL_VERTICAL:
13024         case EL_EXPANDABLE_WALL_ANY:
13025         case EL_BD_EXPANDABLE_WALL:
13026           MauerAbleger(x, y);
13027           break;
13028
13029         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13030         case EL_EXPANDABLE_STEELWALL_VERTICAL:
13031         case EL_EXPANDABLE_STEELWALL_ANY:
13032           MauerAblegerStahl(x, y);
13033           break;
13034
13035         case EL_FLAMES:
13036           CheckForDragon(x, y);
13037           break;
13038
13039         case EL_EXPLOSION:
13040           break;
13041
13042         case EL_ELEMENT_SNAPPING:
13043         case EL_DIAGONAL_SHRINKING:
13044         case EL_DIAGONAL_GROWING:
13045         {
13046           graphic =
13047             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13048
13049           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13050           break;
13051         }
13052
13053         default:
13054           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13055             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13056           break;
13057       }
13058     }
13059
13060 #else   // ---------------------------------------------------------------------
13061
13062     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13063     {
13064       StartMoving(x, y);
13065
13066       element = Feld[x][y];
13067       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13068
13069       if (IS_ANIMATED(graphic) &&
13070           !IS_MOVING(x, y) &&
13071           !Stop[x][y])
13072         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13073
13074       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13075         TEST_DrawTwinkleOnField(x, y);
13076     }
13077     else if ((element == EL_ACID ||
13078               element == EL_EXIT_OPEN ||
13079               element == EL_EM_EXIT_OPEN ||
13080               element == EL_SP_EXIT_OPEN ||
13081               element == EL_STEEL_EXIT_OPEN ||
13082               element == EL_EM_STEEL_EXIT_OPEN ||
13083               element == EL_SP_TERMINAL ||
13084               element == EL_SP_TERMINAL_ACTIVE ||
13085               element == EL_EXTRA_TIME ||
13086               element == EL_SHIELD_NORMAL ||
13087               element == EL_SHIELD_DEADLY) &&
13088              IS_ANIMATED(graphic))
13089       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13090     else if (IS_MOVING(x, y))
13091       ContinueMoving(x, y);
13092     else if (IS_ACTIVE_BOMB(element))
13093       CheckDynamite(x, y);
13094     else if (element == EL_AMOEBA_GROWING)
13095       AmoebeWaechst(x, y);
13096     else if (element == EL_AMOEBA_SHRINKING)
13097       AmoebaDisappearing(x, y);
13098
13099 #if !USE_NEW_AMOEBA_CODE
13100     else if (IS_AMOEBALIVE(element))
13101       AmoebeAbleger(x, y);
13102 #endif
13103
13104     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13105       Life(x, y);
13106     else if (element == EL_EXIT_CLOSED)
13107       CheckExit(x, y);
13108     else if (element == EL_EM_EXIT_CLOSED)
13109       CheckExitEM(x, y);
13110     else if (element == EL_STEEL_EXIT_CLOSED)
13111       CheckExitSteel(x, y);
13112     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13113       CheckExitSteelEM(x, y);
13114     else if (element == EL_SP_EXIT_CLOSED)
13115       CheckExitSP(x, y);
13116     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13117              element == EL_EXPANDABLE_STEELWALL_GROWING)
13118       MauerWaechst(x, y);
13119     else if (element == EL_EXPANDABLE_WALL ||
13120              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13121              element == EL_EXPANDABLE_WALL_VERTICAL ||
13122              element == EL_EXPANDABLE_WALL_ANY ||
13123              element == EL_BD_EXPANDABLE_WALL)
13124       MauerAbleger(x, y);
13125     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13126              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13127              element == EL_EXPANDABLE_STEELWALL_ANY)
13128       MauerAblegerStahl(x, y);
13129     else if (element == EL_FLAMES)
13130       CheckForDragon(x, y);
13131     else if (element == EL_EXPLOSION)
13132       ; /* drawing of correct explosion animation is handled separately */
13133     else if (element == EL_ELEMENT_SNAPPING ||
13134              element == EL_DIAGONAL_SHRINKING ||
13135              element == EL_DIAGONAL_GROWING)
13136     {
13137       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13138
13139       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13140     }
13141     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13142       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13143
13144 #endif  // ---------------------------------------------------------------------
13145
13146     if (IS_BELT_ACTIVE(element))
13147       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13148
13149     if (game.magic_wall_active)
13150     {
13151       int jx = local_player->jx, jy = local_player->jy;
13152
13153       /* play the element sound at the position nearest to the player */
13154       if ((element == EL_MAGIC_WALL_FULL ||
13155            element == EL_MAGIC_WALL_ACTIVE ||
13156            element == EL_MAGIC_WALL_EMPTYING ||
13157            element == EL_BD_MAGIC_WALL_FULL ||
13158            element == EL_BD_MAGIC_WALL_ACTIVE ||
13159            element == EL_BD_MAGIC_WALL_EMPTYING ||
13160            element == EL_DC_MAGIC_WALL_FULL ||
13161            element == EL_DC_MAGIC_WALL_ACTIVE ||
13162            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13163           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13164       {
13165         magic_wall_x = x;
13166         magic_wall_y = y;
13167       }
13168     }
13169   }
13170
13171 #if 0
13172   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13173 #endif
13174
13175 #if USE_NEW_AMOEBA_CODE
13176   /* new experimental amoeba growth stuff */
13177   if (!(FrameCounter % 8))
13178   {
13179     static unsigned int random = 1684108901;
13180
13181     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13182     {
13183       x = RND(lev_fieldx);
13184       y = RND(lev_fieldy);
13185       element = Feld[x][y];
13186
13187       if (!IS_PLAYER(x,y) &&
13188           (element == EL_EMPTY ||
13189            CAN_GROW_INTO(element) ||
13190            element == EL_QUICKSAND_EMPTY ||
13191            element == EL_QUICKSAND_FAST_EMPTY ||
13192            element == EL_ACID_SPLASH_LEFT ||
13193            element == EL_ACID_SPLASH_RIGHT))
13194       {
13195         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13196             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13197             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13198             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13199           Feld[x][y] = EL_AMOEBA_DROP;
13200       }
13201
13202       random = random * 129 + 1;
13203     }
13204   }
13205 #endif
13206
13207 #if 0
13208   if (game.explosions_delayed)
13209 #endif
13210   {
13211     game.explosions_delayed = FALSE;
13212
13213     SCAN_PLAYFIELD(x, y)
13214     {
13215       element = Feld[x][y];
13216
13217       if (ExplodeField[x][y])
13218         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13219       else if (element == EL_EXPLOSION)
13220         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13221
13222       ExplodeField[x][y] = EX_TYPE_NONE;
13223     }
13224
13225     game.explosions_delayed = TRUE;
13226   }
13227
13228   if (game.magic_wall_active)
13229   {
13230     if (!(game.magic_wall_time_left % 4))
13231     {
13232       int element = Feld[magic_wall_x][magic_wall_y];
13233
13234       if (element == EL_BD_MAGIC_WALL_FULL ||
13235           element == EL_BD_MAGIC_WALL_ACTIVE ||
13236           element == EL_BD_MAGIC_WALL_EMPTYING)
13237         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13238       else if (element == EL_DC_MAGIC_WALL_FULL ||
13239                element == EL_DC_MAGIC_WALL_ACTIVE ||
13240                element == EL_DC_MAGIC_WALL_EMPTYING)
13241         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13242       else
13243         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13244     }
13245
13246     if (game.magic_wall_time_left > 0)
13247     {
13248       game.magic_wall_time_left--;
13249
13250       if (!game.magic_wall_time_left)
13251       {
13252         SCAN_PLAYFIELD(x, y)
13253         {
13254           element = Feld[x][y];
13255
13256           if (element == EL_MAGIC_WALL_ACTIVE ||
13257               element == EL_MAGIC_WALL_FULL)
13258           {
13259             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13260             TEST_DrawLevelField(x, y);
13261           }
13262           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13263                    element == EL_BD_MAGIC_WALL_FULL)
13264           {
13265             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13266             TEST_DrawLevelField(x, y);
13267           }
13268           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13269                    element == EL_DC_MAGIC_WALL_FULL)
13270           {
13271             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13272             TEST_DrawLevelField(x, y);
13273           }
13274         }
13275
13276         game.magic_wall_active = FALSE;
13277       }
13278     }
13279   }
13280
13281   if (game.light_time_left > 0)
13282   {
13283     game.light_time_left--;
13284
13285     if (game.light_time_left == 0)
13286       RedrawAllLightSwitchesAndInvisibleElements();
13287   }
13288
13289   if (game.timegate_time_left > 0)
13290   {
13291     game.timegate_time_left--;
13292
13293     if (game.timegate_time_left == 0)
13294       CloseAllOpenTimegates();
13295   }
13296
13297   if (game.lenses_time_left > 0)
13298   {
13299     game.lenses_time_left--;
13300
13301     if (game.lenses_time_left == 0)
13302       RedrawAllInvisibleElementsForLenses();
13303   }
13304
13305   if (game.magnify_time_left > 0)
13306   {
13307     game.magnify_time_left--;
13308
13309     if (game.magnify_time_left == 0)
13310       RedrawAllInvisibleElementsForMagnifier();
13311   }
13312
13313   for (i = 0; i < MAX_PLAYERS; i++)
13314   {
13315     struct PlayerInfo *player = &stored_player[i];
13316
13317     if (SHIELD_ON(player))
13318     {
13319       if (player->shield_deadly_time_left)
13320         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13321       else if (player->shield_normal_time_left)
13322         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13323     }
13324   }
13325
13326 #if USE_DELAYED_GFX_REDRAW
13327   SCAN_PLAYFIELD(x, y)
13328   {
13329 #if 1
13330     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13331 #else
13332     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13333         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13334 #endif
13335     {
13336       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13337          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13338
13339       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13340         DrawLevelField(x, y);
13341
13342       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13343         DrawLevelFieldCrumbled(x, y);
13344
13345       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13346         DrawLevelFieldCrumbledNeighbours(x, y);
13347
13348       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13349         DrawTwinkleOnField(x, y);
13350     }
13351
13352     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13353   }
13354 #endif
13355
13356   CheckLevelTime();
13357
13358   DrawAllPlayers();
13359   PlayAllPlayersSound();
13360
13361   if (options.debug)                    /* calculate frames per second */
13362   {
13363     static unsigned int fps_counter = 0;
13364     static int fps_frames = 0;
13365     unsigned int fps_delay_ms = Counter() - fps_counter;
13366
13367     fps_frames++;
13368
13369     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13370     {
13371       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13372
13373       fps_frames = 0;
13374       fps_counter = Counter();
13375     }
13376
13377     redraw_mask |= REDRAW_FPS;
13378   }
13379
13380   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13381
13382   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13383   {
13384     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13385
13386     local_player->show_envelope = 0;
13387   }
13388
13389 #if 0
13390   debug_print_timestamp(0, "stop main loop profiling ");
13391   printf("----------------------------------------------------------\n");
13392 #endif
13393
13394   /* use random number generator in every frame to make it less predictable */
13395   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13396     RND(1);
13397 }
13398
13399 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13400 {
13401   int min_x = x, min_y = y, max_x = x, max_y = y;
13402   int i;
13403
13404   for (i = 0; i < MAX_PLAYERS; i++)
13405   {
13406     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13407
13408     if (!stored_player[i].active || &stored_player[i] == player)
13409       continue;
13410
13411     min_x = MIN(min_x, jx);
13412     min_y = MIN(min_y, jy);
13413     max_x = MAX(max_x, jx);
13414     max_y = MAX(max_y, jy);
13415   }
13416
13417   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13418 }
13419
13420 static boolean AllPlayersInVisibleScreen()
13421 {
13422   int i;
13423
13424   for (i = 0; i < MAX_PLAYERS; i++)
13425   {
13426     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13427
13428     if (!stored_player[i].active)
13429       continue;
13430
13431     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13432       return FALSE;
13433   }
13434
13435   return TRUE;
13436 }
13437
13438 void ScrollLevel(int dx, int dy)
13439 {
13440 #if 0
13441   /* (directly solved in BlitBitmap() now) */
13442   static Bitmap *bitmap_db_field2 = NULL;
13443   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13444   int x, y;
13445 #else
13446   int x, y;
13447 #endif
13448
13449 #if 0
13450   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13451   /* only horizontal XOR vertical scroll direction allowed */
13452   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13453     return;
13454 #endif
13455
13456 #if 0
13457   /* (directly solved in BlitBitmap() now) */
13458   if (bitmap_db_field2 == NULL)
13459     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13460
13461   /* needed when blitting directly to same bitmap -- should not be needed with
13462      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13463   BlitBitmap(drawto_field, bitmap_db_field2,
13464              FX + TILEX * (dx == -1) - softscroll_offset,
13465              FY + TILEY * (dy == -1) - softscroll_offset,
13466              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13467              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13468              FX + TILEX * (dx == 1) - softscroll_offset,
13469              FY + TILEY * (dy == 1) - softscroll_offset);
13470   BlitBitmap(bitmap_db_field2, drawto_field,
13471              FX + TILEX * (dx == 1) - softscroll_offset,
13472              FY + TILEY * (dy == 1) - softscroll_offset,
13473              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13474              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13475              FX + TILEX * (dx == 1) - softscroll_offset,
13476              FY + TILEY * (dy == 1) - softscroll_offset);
13477
13478 #else
13479
13480 #if 0
13481   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13482   int xsize = (BX2 - BX1 + 1);
13483   int ysize = (BY2 - BY1 + 1);
13484   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13485   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13486   int step  = (start < end ? +1 : -1);
13487
13488   for (i = start; i != end; i += step)
13489   {
13490     BlitBitmap(drawto_field, drawto_field,
13491                FX + TILEX * (dx != 0 ? i + step : 0),
13492                FY + TILEY * (dy != 0 ? i + step : 0),
13493                TILEX * (dx != 0 ? 1 : xsize),
13494                TILEY * (dy != 0 ? 1 : ysize),
13495                FX + TILEX * (dx != 0 ? i : 0),
13496                FY + TILEY * (dy != 0 ? i : 0));
13497   }
13498
13499 #else
13500
13501 #if NEW_TILESIZE
13502 #if NEW_SCROLL
13503   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13504 #else
13505   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13506 #endif
13507 #else
13508 #if NEW_SCROLL
13509   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13510 #else
13511   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13512 #endif
13513 #endif
13514
13515 #if NEW_TILESIZE
13516   BlitBitmap(drawto_field, drawto_field,
13517              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13518              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13519              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13520              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13521              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13522              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13523 #else
13524   BlitBitmap(drawto_field, drawto_field,
13525              FX + TILEX * (dx == -1) - softscroll_offset,
13526              FY + TILEY * (dy == -1) - softscroll_offset,
13527              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13528              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13529              FX + TILEX * (dx == 1) - softscroll_offset,
13530              FY + TILEY * (dy == 1) - softscroll_offset);
13531 #endif
13532
13533 #endif
13534 #endif
13535
13536   if (dx != 0)
13537   {
13538     x = (dx == 1 ? BX1 : BX2);
13539     for (y = BY1; y <= BY2; y++)
13540       DrawScreenField(x, y);
13541   }
13542
13543   if (dy != 0)
13544   {
13545     y = (dy == 1 ? BY1 : BY2);
13546     for (x = BX1; x <= BX2; x++)
13547       DrawScreenField(x, y);
13548   }
13549
13550   redraw_mask |= REDRAW_FIELD;
13551 }
13552
13553 static boolean canFallDown(struct PlayerInfo *player)
13554 {
13555   int jx = player->jx, jy = player->jy;
13556
13557   return (IN_LEV_FIELD(jx, jy + 1) &&
13558           (IS_FREE(jx, jy + 1) ||
13559            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13560           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13561           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13562 }
13563
13564 static boolean canPassField(int x, int y, int move_dir)
13565 {
13566   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13567   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13568   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13569   int nextx = x + dx;
13570   int nexty = y + dy;
13571   int element = Feld[x][y];
13572
13573   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13574           !CAN_MOVE(element) &&
13575           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13576           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13577           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13578 }
13579
13580 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13581 {
13582   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13583   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13584   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13585   int newx = x + dx;
13586   int newy = y + dy;
13587
13588   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13589           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13590           (IS_DIGGABLE(Feld[newx][newy]) ||
13591            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13592            canPassField(newx, newy, move_dir)));
13593 }
13594
13595 static void CheckGravityMovement(struct PlayerInfo *player)
13596 {
13597 #if USE_PLAYER_GRAVITY
13598   if (player->gravity && !player->programmed_action)
13599 #else
13600   if (game.gravity && !player->programmed_action)
13601 #endif
13602   {
13603     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13604     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13605     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13606     int jx = player->jx, jy = player->jy;
13607     boolean player_is_moving_to_valid_field =
13608       (!player_is_snapping &&
13609        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13610         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13611     boolean player_can_fall_down = canFallDown(player);
13612
13613     if (player_can_fall_down &&
13614         !player_is_moving_to_valid_field)
13615       player->programmed_action = MV_DOWN;
13616   }
13617 }
13618
13619 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13620 {
13621   return CheckGravityMovement(player);
13622
13623 #if USE_PLAYER_GRAVITY
13624   if (player->gravity && !player->programmed_action)
13625 #else
13626   if (game.gravity && !player->programmed_action)
13627 #endif
13628   {
13629     int jx = player->jx, jy = player->jy;
13630     boolean field_under_player_is_free =
13631       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13632     boolean player_is_standing_on_valid_field =
13633       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13634        (IS_WALKABLE(Feld[jx][jy]) &&
13635         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13636
13637     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13638       player->programmed_action = MV_DOWN;
13639   }
13640 }
13641
13642 /*
13643   MovePlayerOneStep()
13644   -----------------------------------------------------------------------------
13645   dx, dy:               direction (non-diagonal) to try to move the player to
13646   real_dx, real_dy:     direction as read from input device (can be diagonal)
13647 */
13648
13649 boolean MovePlayerOneStep(struct PlayerInfo *player,
13650                           int dx, int dy, int real_dx, int real_dy)
13651 {
13652   int jx = player->jx, jy = player->jy;
13653   int new_jx = jx + dx, new_jy = jy + dy;
13654 #if !USE_FIXED_DONT_RUN_INTO
13655   int element;
13656 #endif
13657   int can_move;
13658   boolean player_can_move = !player->cannot_move;
13659
13660   if (!player->active || (!dx && !dy))
13661     return MP_NO_ACTION;
13662
13663   player->MovDir = (dx < 0 ? MV_LEFT :
13664                     dx > 0 ? MV_RIGHT :
13665                     dy < 0 ? MV_UP :
13666                     dy > 0 ? MV_DOWN :  MV_NONE);
13667
13668   if (!IN_LEV_FIELD(new_jx, new_jy))
13669     return MP_NO_ACTION;
13670
13671   if (!player_can_move)
13672   {
13673     if (player->MovPos == 0)
13674     {
13675       player->is_moving = FALSE;
13676       player->is_digging = FALSE;
13677       player->is_collecting = FALSE;
13678       player->is_snapping = FALSE;
13679       player->is_pushing = FALSE;
13680     }
13681   }
13682
13683 #if 1
13684   if (!options.network && game.centered_player_nr == -1 &&
13685       !AllPlayersInSight(player, new_jx, new_jy))
13686     return MP_NO_ACTION;
13687 #else
13688   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13689     return MP_NO_ACTION;
13690 #endif
13691
13692 #if !USE_FIXED_DONT_RUN_INTO
13693   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13694
13695   /* (moved to DigField()) */
13696   if (player_can_move && DONT_RUN_INTO(element))
13697   {
13698     if (element == EL_ACID && dx == 0 && dy == 1)
13699     {
13700       SplashAcid(new_jx, new_jy);
13701       Feld[jx][jy] = EL_PLAYER_1;
13702       InitMovingField(jx, jy, MV_DOWN);
13703       Store[jx][jy] = EL_ACID;
13704       ContinueMoving(jx, jy);
13705       BuryPlayer(player);
13706     }
13707     else
13708       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13709
13710     return MP_MOVING;
13711   }
13712 #endif
13713
13714   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13715   if (can_move != MP_MOVING)
13716     return can_move;
13717
13718   /* check if DigField() has caused relocation of the player */
13719   if (player->jx != jx || player->jy != jy)
13720     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13721
13722   StorePlayer[jx][jy] = 0;
13723   player->last_jx = jx;
13724   player->last_jy = jy;
13725   player->jx = new_jx;
13726   player->jy = new_jy;
13727   StorePlayer[new_jx][new_jy] = player->element_nr;
13728
13729   if (player->move_delay_value_next != -1)
13730   {
13731     player->move_delay_value = player->move_delay_value_next;
13732     player->move_delay_value_next = -1;
13733   }
13734
13735   player->MovPos =
13736     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13737
13738   player->step_counter++;
13739
13740   PlayerVisit[jx][jy] = FrameCounter;
13741
13742 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13743   player->is_moving = TRUE;
13744 #endif
13745
13746 #if 1
13747   /* should better be called in MovePlayer(), but this breaks some tapes */
13748   ScrollPlayer(player, SCROLL_INIT);
13749 #endif
13750
13751   return MP_MOVING;
13752 }
13753
13754 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13755 {
13756   int jx = player->jx, jy = player->jy;
13757   int old_jx = jx, old_jy = jy;
13758   int moved = MP_NO_ACTION;
13759
13760   if (!player->active)
13761     return FALSE;
13762
13763   if (!dx && !dy)
13764   {
13765     if (player->MovPos == 0)
13766     {
13767       player->is_moving = FALSE;
13768       player->is_digging = FALSE;
13769       player->is_collecting = FALSE;
13770       player->is_snapping = FALSE;
13771       player->is_pushing = FALSE;
13772     }
13773
13774     return FALSE;
13775   }
13776
13777   if (player->move_delay > 0)
13778     return FALSE;
13779
13780   player->move_delay = -1;              /* set to "uninitialized" value */
13781
13782   /* store if player is automatically moved to next field */
13783   player->is_auto_moving = (player->programmed_action != MV_NONE);
13784
13785   /* remove the last programmed player action */
13786   player->programmed_action = 0;
13787
13788   if (player->MovPos)
13789   {
13790     /* should only happen if pre-1.2 tape recordings are played */
13791     /* this is only for backward compatibility */
13792
13793     int original_move_delay_value = player->move_delay_value;
13794
13795 #if DEBUG
13796     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13797            tape.counter);
13798 #endif
13799
13800     /* scroll remaining steps with finest movement resolution */
13801     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13802
13803     while (player->MovPos)
13804     {
13805       ScrollPlayer(player, SCROLL_GO_ON);
13806       ScrollScreen(NULL, SCROLL_GO_ON);
13807
13808       AdvanceFrameAndPlayerCounters(player->index_nr);
13809
13810       DrawAllPlayers();
13811       BackToFront();
13812     }
13813
13814     player->move_delay_value = original_move_delay_value;
13815   }
13816
13817   player->is_active = FALSE;
13818
13819   if (player->last_move_dir & MV_HORIZONTAL)
13820   {
13821     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13822       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13823   }
13824   else
13825   {
13826     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13827       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13828   }
13829
13830 #if USE_FIXED_BORDER_RUNNING_GFX
13831   if (!moved && !player->is_active)
13832   {
13833     player->is_moving = FALSE;
13834     player->is_digging = FALSE;
13835     player->is_collecting = FALSE;
13836     player->is_snapping = FALSE;
13837     player->is_pushing = FALSE;
13838   }
13839 #endif
13840
13841   jx = player->jx;
13842   jy = player->jy;
13843
13844 #if 1
13845   if (moved & MP_MOVING && !ScreenMovPos &&
13846       (player->index_nr == game.centered_player_nr ||
13847        game.centered_player_nr == -1))
13848 #else
13849   if (moved & MP_MOVING && !ScreenMovPos &&
13850       (player == local_player || !options.network))
13851 #endif
13852   {
13853     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13854     int offset = game.scroll_delay_value;
13855
13856     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13857     {
13858       /* actual player has left the screen -- scroll in that direction */
13859       if (jx != old_jx)         /* player has moved horizontally */
13860         scroll_x += (jx - old_jx);
13861       else                      /* player has moved vertically */
13862         scroll_y += (jy - old_jy);
13863     }
13864     else
13865     {
13866       if (jx != old_jx)         /* player has moved horizontally */
13867       {
13868         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13869             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13870           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13871
13872         /* don't scroll over playfield boundaries */
13873         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13874           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13875
13876         /* don't scroll more than one field at a time */
13877         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13878
13879         /* don't scroll against the player's moving direction */
13880         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13881             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13882           scroll_x = old_scroll_x;
13883       }
13884       else                      /* player has moved vertically */
13885       {
13886         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13887             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13888           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13889
13890         /* don't scroll over playfield boundaries */
13891         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13892           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13893
13894         /* don't scroll more than one field at a time */
13895         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13896
13897         /* don't scroll against the player's moving direction */
13898         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13899             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13900           scroll_y = old_scroll_y;
13901       }
13902     }
13903
13904     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13905     {
13906 #if 1
13907       if (!options.network && game.centered_player_nr == -1 &&
13908           !AllPlayersInVisibleScreen())
13909       {
13910         scroll_x = old_scroll_x;
13911         scroll_y = old_scroll_y;
13912       }
13913       else
13914 #else
13915       if (!options.network && !AllPlayersInVisibleScreen())
13916       {
13917         scroll_x = old_scroll_x;
13918         scroll_y = old_scroll_y;
13919       }
13920       else
13921 #endif
13922       {
13923         ScrollScreen(player, SCROLL_INIT);
13924         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13925       }
13926     }
13927   }
13928
13929   player->StepFrame = 0;
13930
13931   if (moved & MP_MOVING)
13932   {
13933     if (old_jx != jx && old_jy == jy)
13934       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13935     else if (old_jx == jx && old_jy != jy)
13936       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13937
13938     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13939
13940     player->last_move_dir = player->MovDir;
13941     player->is_moving = TRUE;
13942     player->is_snapping = FALSE;
13943     player->is_switching = FALSE;
13944     player->is_dropping = FALSE;
13945     player->is_dropping_pressed = FALSE;
13946     player->drop_pressed_delay = 0;
13947
13948 #if 0
13949     /* should better be called here than above, but this breaks some tapes */
13950     ScrollPlayer(player, SCROLL_INIT);
13951 #endif
13952   }
13953   else
13954   {
13955     CheckGravityMovementWhenNotMoving(player);
13956
13957     player->is_moving = FALSE;
13958
13959     /* at this point, the player is allowed to move, but cannot move right now
13960        (e.g. because of something blocking the way) -- ensure that the player
13961        is also allowed to move in the next frame (in old versions before 3.1.1,
13962        the player was forced to wait again for eight frames before next try) */
13963
13964     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13965       player->move_delay = 0;   /* allow direct movement in the next frame */
13966   }
13967
13968   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13969     player->move_delay = player->move_delay_value;
13970
13971   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13972   {
13973     TestIfPlayerTouchesBadThing(jx, jy);
13974     TestIfPlayerTouchesCustomElement(jx, jy);
13975   }
13976
13977   if (!player->active)
13978     RemovePlayer(player);
13979
13980   return moved;
13981 }
13982
13983 void ScrollPlayer(struct PlayerInfo *player, int mode)
13984 {
13985   int jx = player->jx, jy = player->jy;
13986   int last_jx = player->last_jx, last_jy = player->last_jy;
13987   int move_stepsize = TILEX / player->move_delay_value;
13988
13989 #if USE_NEW_PLAYER_SPEED
13990   if (!player->active)
13991     return;
13992
13993   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13994     return;
13995 #else
13996   if (!player->active || player->MovPos == 0)
13997     return;
13998 #endif
13999
14000   if (mode == SCROLL_INIT)
14001   {
14002     player->actual_frame_counter = FrameCounter;
14003     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14004
14005     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14006         Feld[last_jx][last_jy] == EL_EMPTY)
14007     {
14008       int last_field_block_delay = 0;   /* start with no blocking at all */
14009       int block_delay_adjustment = player->block_delay_adjustment;
14010
14011       /* if player blocks last field, add delay for exactly one move */
14012       if (player->block_last_field)
14013       {
14014         last_field_block_delay += player->move_delay_value;
14015
14016         /* when blocking enabled, prevent moving up despite gravity */
14017 #if USE_PLAYER_GRAVITY
14018         if (player->gravity && player->MovDir == MV_UP)
14019           block_delay_adjustment = -1;
14020 #else
14021         if (game.gravity && player->MovDir == MV_UP)
14022           block_delay_adjustment = -1;
14023 #endif
14024       }
14025
14026       /* add block delay adjustment (also possible when not blocking) */
14027       last_field_block_delay += block_delay_adjustment;
14028
14029       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14030       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14031     }
14032
14033 #if USE_NEW_PLAYER_SPEED
14034     if (player->MovPos != 0)    /* player has not yet reached destination */
14035       return;
14036 #else
14037     return;
14038 #endif
14039   }
14040   else if (!FrameReached(&player->actual_frame_counter, 1))
14041     return;
14042
14043 #if USE_NEW_PLAYER_SPEED
14044   if (player->MovPos != 0)
14045   {
14046     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14047     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14048
14049     /* before DrawPlayer() to draw correct player graphic for this case */
14050     if (player->MovPos == 0)
14051       CheckGravityMovement(player);
14052   }
14053 #else
14054   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14055   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14056
14057   /* before DrawPlayer() to draw correct player graphic for this case */
14058   if (player->MovPos == 0)
14059     CheckGravityMovement(player);
14060 #endif
14061
14062   if (player->MovPos == 0)      /* player reached destination field */
14063   {
14064     if (player->move_delay_reset_counter > 0)
14065     {
14066       player->move_delay_reset_counter--;
14067
14068       if (player->move_delay_reset_counter == 0)
14069       {
14070         /* continue with normal speed after quickly moving through gate */
14071         HALVE_PLAYER_SPEED(player);
14072
14073         /* be able to make the next move without delay */
14074         player->move_delay = 0;
14075       }
14076     }
14077
14078     player->last_jx = jx;
14079     player->last_jy = jy;
14080
14081     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14082         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14083 #if 1
14084         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14085 #endif
14086         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14087         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14088 #if 1
14089         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14090 #endif
14091         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14092         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14093     {
14094       DrawPlayer(player);       /* needed here only to cleanup last field */
14095       RemovePlayer(player);
14096
14097       if (local_player->friends_still_needed == 0 ||
14098           IS_SP_ELEMENT(Feld[jx][jy]))
14099         PlayerWins(player);
14100     }
14101
14102     /* this breaks one level: "machine", level 000 */
14103     {
14104       int move_direction = player->MovDir;
14105       int enter_side = MV_DIR_OPPOSITE(move_direction);
14106       int leave_side = move_direction;
14107       int old_jx = last_jx;
14108       int old_jy = last_jy;
14109       int old_element = Feld[old_jx][old_jy];
14110       int new_element = Feld[jx][jy];
14111
14112       if (IS_CUSTOM_ELEMENT(old_element))
14113         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14114                                    CE_LEFT_BY_PLAYER,
14115                                    player->index_bit, leave_side);
14116
14117       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14118                                           CE_PLAYER_LEAVES_X,
14119                                           player->index_bit, leave_side);
14120
14121       if (IS_CUSTOM_ELEMENT(new_element))
14122         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14123                                    player->index_bit, enter_side);
14124
14125       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14126                                           CE_PLAYER_ENTERS_X,
14127                                           player->index_bit, enter_side);
14128
14129 #if USE_FIX_CE_ACTION_WITH_PLAYER
14130       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14131                                         CE_MOVE_OF_X, move_direction);
14132 #else
14133       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14134                                         CE_MOVE_OF_X, move_direction);
14135 #endif
14136     }
14137
14138     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14139     {
14140       TestIfPlayerTouchesBadThing(jx, jy);
14141       TestIfPlayerTouchesCustomElement(jx, jy);
14142
14143       /* needed because pushed element has not yet reached its destination,
14144          so it would trigger a change event at its previous field location */
14145       if (!player->is_pushing)
14146         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14147
14148       if (!player->active)
14149         RemovePlayer(player);
14150     }
14151
14152     if (!local_player->LevelSolved && level.use_step_counter)
14153     {
14154       int i;
14155
14156       TimePlayed++;
14157
14158       if (TimeLeft > 0)
14159       {
14160         TimeLeft--;
14161
14162         if (TimeLeft <= 10 && setup.time_limit)
14163           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14164
14165 #if 1
14166         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14167
14168         DisplayGameControlValues();
14169 #else
14170         DrawGameValue_Time(TimeLeft);
14171 #endif
14172
14173         if (!TimeLeft && setup.time_limit)
14174           for (i = 0; i < MAX_PLAYERS; i++)
14175             KillPlayer(&stored_player[i]);
14176       }
14177 #if 1
14178       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14179       {
14180         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14181
14182         DisplayGameControlValues();
14183       }
14184 #else
14185       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14186         DrawGameValue_Time(TimePlayed);
14187 #endif
14188     }
14189
14190     if (tape.single_step && tape.recording && !tape.pausing &&
14191         !player->programmed_action)
14192       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14193   }
14194 }
14195
14196 void ScrollScreen(struct PlayerInfo *player, int mode)
14197 {
14198   static unsigned int screen_frame_counter = 0;
14199
14200   if (mode == SCROLL_INIT)
14201   {
14202     /* set scrolling step size according to actual player's moving speed */
14203     ScrollStepSize = TILEX / player->move_delay_value;
14204
14205     screen_frame_counter = FrameCounter;
14206     ScreenMovDir = player->MovDir;
14207     ScreenMovPos = player->MovPos;
14208     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14209     return;
14210   }
14211   else if (!FrameReached(&screen_frame_counter, 1))
14212     return;
14213
14214   if (ScreenMovPos)
14215   {
14216     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14217     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14218     redraw_mask |= REDRAW_FIELD;
14219   }
14220   else
14221     ScreenMovDir = MV_NONE;
14222 }
14223
14224 void TestIfPlayerTouchesCustomElement(int x, int y)
14225 {
14226   static int xy[4][2] =
14227   {
14228     { 0, -1 },
14229     { -1, 0 },
14230     { +1, 0 },
14231     { 0, +1 }
14232   };
14233   static int trigger_sides[4][2] =
14234   {
14235     /* center side       border side */
14236     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14237     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14238     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14239     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14240   };
14241   static int touch_dir[4] =
14242   {
14243     MV_LEFT | MV_RIGHT,
14244     MV_UP   | MV_DOWN,
14245     MV_UP   | MV_DOWN,
14246     MV_LEFT | MV_RIGHT
14247   };
14248   int center_element = Feld[x][y];      /* should always be non-moving! */
14249   int i;
14250
14251   for (i = 0; i < NUM_DIRECTIONS; i++)
14252   {
14253     int xx = x + xy[i][0];
14254     int yy = y + xy[i][1];
14255     int center_side = trigger_sides[i][0];
14256     int border_side = trigger_sides[i][1];
14257     int border_element;
14258
14259     if (!IN_LEV_FIELD(xx, yy))
14260       continue;
14261
14262     if (IS_PLAYER(x, y))                /* player found at center element */
14263     {
14264       struct PlayerInfo *player = PLAYERINFO(x, y);
14265
14266       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14267         border_element = Feld[xx][yy];          /* may be moving! */
14268       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14269         border_element = Feld[xx][yy];
14270       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14271         border_element = MovingOrBlocked2Element(xx, yy);
14272       else
14273         continue;               /* center and border element do not touch */
14274
14275       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14276                                  player->index_bit, border_side);
14277       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14278                                           CE_PLAYER_TOUCHES_X,
14279                                           player->index_bit, border_side);
14280
14281 #if USE_FIX_CE_ACTION_WITH_PLAYER
14282       {
14283         /* use player element that is initially defined in the level playfield,
14284            not the player element that corresponds to the runtime player number
14285            (example: a level that contains EL_PLAYER_3 as the only player would
14286            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14287         int player_element = PLAYERINFO(x, y)->initial_element;
14288
14289         CheckElementChangeBySide(xx, yy, border_element, player_element,
14290                                  CE_TOUCHING_X, border_side);
14291       }
14292 #endif
14293     }
14294     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14295     {
14296       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14297
14298       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14299       {
14300         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14301           continue;             /* center and border element do not touch */
14302       }
14303
14304       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14305                                  player->index_bit, center_side);
14306       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14307                                           CE_PLAYER_TOUCHES_X,
14308                                           player->index_bit, center_side);
14309
14310 #if USE_FIX_CE_ACTION_WITH_PLAYER
14311       {
14312         /* use player element that is initially defined in the level playfield,
14313            not the player element that corresponds to the runtime player number
14314            (example: a level that contains EL_PLAYER_3 as the only player would
14315            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14316         int player_element = PLAYERINFO(xx, yy)->initial_element;
14317
14318         CheckElementChangeBySide(x, y, center_element, player_element,
14319                                  CE_TOUCHING_X, center_side);
14320       }
14321 #endif
14322
14323       break;
14324     }
14325   }
14326 }
14327
14328 #if USE_ELEMENT_TOUCHING_BUGFIX
14329
14330 void TestIfElementTouchesCustomElement(int x, int y)
14331 {
14332   static int xy[4][2] =
14333   {
14334     { 0, -1 },
14335     { -1, 0 },
14336     { +1, 0 },
14337     { 0, +1 }
14338   };
14339   static int trigger_sides[4][2] =
14340   {
14341     /* center side      border side */
14342     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14343     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14344     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14345     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14346   };
14347   static int touch_dir[4] =
14348   {
14349     MV_LEFT | MV_RIGHT,
14350     MV_UP   | MV_DOWN,
14351     MV_UP   | MV_DOWN,
14352     MV_LEFT | MV_RIGHT
14353   };
14354   boolean change_center_element = FALSE;
14355   int center_element = Feld[x][y];      /* should always be non-moving! */
14356   int border_element_old[NUM_DIRECTIONS];
14357   int i;
14358
14359   for (i = 0; i < NUM_DIRECTIONS; i++)
14360   {
14361     int xx = x + xy[i][0];
14362     int yy = y + xy[i][1];
14363     int border_element;
14364
14365     border_element_old[i] = -1;
14366
14367     if (!IN_LEV_FIELD(xx, yy))
14368       continue;
14369
14370     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14371       border_element = Feld[xx][yy];    /* may be moving! */
14372     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14373       border_element = Feld[xx][yy];
14374     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14375       border_element = MovingOrBlocked2Element(xx, yy);
14376     else
14377       continue;                 /* center and border element do not touch */
14378
14379     border_element_old[i] = border_element;
14380   }
14381
14382   for (i = 0; i < NUM_DIRECTIONS; i++)
14383   {
14384     int xx = x + xy[i][0];
14385     int yy = y + xy[i][1];
14386     int center_side = trigger_sides[i][0];
14387     int border_element = border_element_old[i];
14388
14389     if (border_element == -1)
14390       continue;
14391
14392     /* check for change of border element */
14393     CheckElementChangeBySide(xx, yy, border_element, center_element,
14394                              CE_TOUCHING_X, center_side);
14395
14396     /* (center element cannot be player, so we dont have to check this here) */
14397   }
14398
14399   for (i = 0; i < NUM_DIRECTIONS; i++)
14400   {
14401     int xx = x + xy[i][0];
14402     int yy = y + xy[i][1];
14403     int border_side = trigger_sides[i][1];
14404     int border_element = border_element_old[i];
14405
14406     if (border_element == -1)
14407       continue;
14408
14409     /* check for change of center element (but change it only once) */
14410     if (!change_center_element)
14411       change_center_element =
14412         CheckElementChangeBySide(x, y, center_element, border_element,
14413                                  CE_TOUCHING_X, border_side);
14414
14415 #if USE_FIX_CE_ACTION_WITH_PLAYER
14416     if (IS_PLAYER(xx, yy))
14417     {
14418       /* use player element that is initially defined in the level playfield,
14419          not the player element that corresponds to the runtime player number
14420          (example: a level that contains EL_PLAYER_3 as the only player would
14421          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14422       int player_element = PLAYERINFO(xx, yy)->initial_element;
14423
14424       CheckElementChangeBySide(x, y, center_element, player_element,
14425                                CE_TOUCHING_X, border_side);
14426     }
14427 #endif
14428   }
14429 }
14430
14431 #else
14432
14433 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14434 {
14435   static int xy[4][2] =
14436   {
14437     { 0, -1 },
14438     { -1, 0 },
14439     { +1, 0 },
14440     { 0, +1 }
14441   };
14442   static int trigger_sides[4][2] =
14443   {
14444     /* center side      border side */
14445     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14446     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14447     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14448     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14449   };
14450   static int touch_dir[4] =
14451   {
14452     MV_LEFT | MV_RIGHT,
14453     MV_UP   | MV_DOWN,
14454     MV_UP   | MV_DOWN,
14455     MV_LEFT | MV_RIGHT
14456   };
14457   boolean change_center_element = FALSE;
14458   int center_element = Feld[x][y];      /* should always be non-moving! */
14459   int i;
14460
14461   for (i = 0; i < NUM_DIRECTIONS; i++)
14462   {
14463     int xx = x + xy[i][0];
14464     int yy = y + xy[i][1];
14465     int center_side = trigger_sides[i][0];
14466     int border_side = trigger_sides[i][1];
14467     int border_element;
14468
14469     if (!IN_LEV_FIELD(xx, yy))
14470       continue;
14471
14472     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14473       border_element = Feld[xx][yy];    /* may be moving! */
14474     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14475       border_element = Feld[xx][yy];
14476     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14477       border_element = MovingOrBlocked2Element(xx, yy);
14478     else
14479       continue;                 /* center and border element do not touch */
14480
14481     /* check for change of center element (but change it only once) */
14482     if (!change_center_element)
14483       change_center_element =
14484         CheckElementChangeBySide(x, y, center_element, border_element,
14485                                  CE_TOUCHING_X, border_side);
14486
14487     /* check for change of border element */
14488     CheckElementChangeBySide(xx, yy, border_element, center_element,
14489                              CE_TOUCHING_X, center_side);
14490   }
14491 }
14492
14493 #endif
14494
14495 void TestIfElementHitsCustomElement(int x, int y, int direction)
14496 {
14497   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14498   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14499   int hitx = x + dx, hity = y + dy;
14500   int hitting_element = Feld[x][y];
14501   int touched_element;
14502
14503   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14504     return;
14505
14506   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14507                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14508
14509   if (IN_LEV_FIELD(hitx, hity))
14510   {
14511     int opposite_direction = MV_DIR_OPPOSITE(direction);
14512     int hitting_side = direction;
14513     int touched_side = opposite_direction;
14514     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14515                           MovDir[hitx][hity] != direction ||
14516                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14517
14518     object_hit = TRUE;
14519
14520     if (object_hit)
14521     {
14522       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14523                                CE_HITTING_X, touched_side);
14524
14525       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14526                                CE_HIT_BY_X, hitting_side);
14527
14528       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529                                CE_HIT_BY_SOMETHING, opposite_direction);
14530
14531 #if USE_FIX_CE_ACTION_WITH_PLAYER
14532       if (IS_PLAYER(hitx, hity))
14533       {
14534         /* use player element that is initially defined in the level playfield,
14535            not the player element that corresponds to the runtime player number
14536            (example: a level that contains EL_PLAYER_3 as the only player would
14537            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14538         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14539
14540         CheckElementChangeBySide(x, y, hitting_element, player_element,
14541                                  CE_HITTING_X, touched_side);
14542       }
14543 #endif
14544     }
14545   }
14546
14547   /* "hitting something" is also true when hitting the playfield border */
14548   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14549                            CE_HITTING_SOMETHING, direction);
14550 }
14551
14552 #if 0
14553 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14554 {
14555   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14556   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14557   int hitx = x + dx, hity = y + dy;
14558   int hitting_element = Feld[x][y];
14559   int touched_element;
14560 #if 0
14561   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14562                         !IS_FREE(hitx, hity) &&
14563                         (!IS_MOVING(hitx, hity) ||
14564                          MovDir[hitx][hity] != direction ||
14565                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14566 #endif
14567
14568   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14569     return;
14570
14571 #if 0
14572   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14573     return;
14574 #endif
14575
14576   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14577                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14578
14579   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14580                            EP_CAN_SMASH_EVERYTHING, direction);
14581
14582   if (IN_LEV_FIELD(hitx, hity))
14583   {
14584     int opposite_direction = MV_DIR_OPPOSITE(direction);
14585     int hitting_side = direction;
14586     int touched_side = opposite_direction;
14587 #if 0
14588     int touched_element = MovingOrBlocked2Element(hitx, hity);
14589 #endif
14590 #if 1
14591     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14592                           MovDir[hitx][hity] != direction ||
14593                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14594
14595     object_hit = TRUE;
14596 #endif
14597
14598     if (object_hit)
14599     {
14600       int i;
14601
14602       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14603                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14604
14605       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14606                                CE_OTHER_IS_SMASHING, touched_side);
14607
14608       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14609                                CE_OTHER_GETS_SMASHED, hitting_side);
14610     }
14611   }
14612 }
14613 #endif
14614
14615 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14616 {
14617   int i, kill_x = -1, kill_y = -1;
14618
14619   int bad_element = -1;
14620   static int test_xy[4][2] =
14621   {
14622     { 0, -1 },
14623     { -1, 0 },
14624     { +1, 0 },
14625     { 0, +1 }
14626   };
14627   static int test_dir[4] =
14628   {
14629     MV_UP,
14630     MV_LEFT,
14631     MV_RIGHT,
14632     MV_DOWN
14633   };
14634
14635   for (i = 0; i < NUM_DIRECTIONS; i++)
14636   {
14637     int test_x, test_y, test_move_dir, test_element;
14638
14639     test_x = good_x + test_xy[i][0];
14640     test_y = good_y + test_xy[i][1];
14641
14642     if (!IN_LEV_FIELD(test_x, test_y))
14643       continue;
14644
14645     test_move_dir =
14646       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14647
14648     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14649
14650     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14651        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14652     */
14653     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14654         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14655     {
14656       kill_x = test_x;
14657       kill_y = test_y;
14658       bad_element = test_element;
14659
14660       break;
14661     }
14662   }
14663
14664   if (kill_x != -1 || kill_y != -1)
14665   {
14666     if (IS_PLAYER(good_x, good_y))
14667     {
14668       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14669
14670       if (player->shield_deadly_time_left > 0 &&
14671           !IS_INDESTRUCTIBLE(bad_element))
14672         Bang(kill_x, kill_y);
14673       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14674         KillPlayer(player);
14675     }
14676     else
14677       Bang(good_x, good_y);
14678   }
14679 }
14680
14681 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14682 {
14683   int i, kill_x = -1, kill_y = -1;
14684   int bad_element = Feld[bad_x][bad_y];
14685   static int test_xy[4][2] =
14686   {
14687     { 0, -1 },
14688     { -1, 0 },
14689     { +1, 0 },
14690     { 0, +1 }
14691   };
14692   static int touch_dir[4] =
14693   {
14694     MV_LEFT | MV_RIGHT,
14695     MV_UP   | MV_DOWN,
14696     MV_UP   | MV_DOWN,
14697     MV_LEFT | MV_RIGHT
14698   };
14699   static int test_dir[4] =
14700   {
14701     MV_UP,
14702     MV_LEFT,
14703     MV_RIGHT,
14704     MV_DOWN
14705   };
14706
14707   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14708     return;
14709
14710   for (i = 0; i < NUM_DIRECTIONS; i++)
14711   {
14712     int test_x, test_y, test_move_dir, test_element;
14713
14714     test_x = bad_x + test_xy[i][0];
14715     test_y = bad_y + test_xy[i][1];
14716
14717     if (!IN_LEV_FIELD(test_x, test_y))
14718       continue;
14719
14720     test_move_dir =
14721       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14722
14723     test_element = Feld[test_x][test_y];
14724
14725     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14726        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14727     */
14728     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14729         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14730     {
14731       /* good thing is player or penguin that does not move away */
14732       if (IS_PLAYER(test_x, test_y))
14733       {
14734         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14735
14736         if (bad_element == EL_ROBOT && player->is_moving)
14737           continue;     /* robot does not kill player if he is moving */
14738
14739         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14740         {
14741           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14742             continue;           /* center and border element do not touch */
14743         }
14744
14745         kill_x = test_x;
14746         kill_y = test_y;
14747
14748         break;
14749       }
14750       else if (test_element == EL_PENGUIN)
14751       {
14752         kill_x = test_x;
14753         kill_y = test_y;
14754
14755         break;
14756       }
14757     }
14758   }
14759
14760   if (kill_x != -1 || kill_y != -1)
14761   {
14762     if (IS_PLAYER(kill_x, kill_y))
14763     {
14764       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14765
14766       if (player->shield_deadly_time_left > 0 &&
14767           !IS_INDESTRUCTIBLE(bad_element))
14768         Bang(bad_x, bad_y);
14769       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14770         KillPlayer(player);
14771     }
14772     else
14773       Bang(kill_x, kill_y);
14774   }
14775 }
14776
14777 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14778 {
14779   int bad_element = Feld[bad_x][bad_y];
14780   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14781   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14782   int test_x = bad_x + dx, test_y = bad_y + dy;
14783   int test_move_dir, test_element;
14784   int kill_x = -1, kill_y = -1;
14785
14786   if (!IN_LEV_FIELD(test_x, test_y))
14787     return;
14788
14789   test_move_dir =
14790     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14791
14792   test_element = Feld[test_x][test_y];
14793
14794   if (test_move_dir != bad_move_dir)
14795   {
14796     /* good thing can be player or penguin that does not move away */
14797     if (IS_PLAYER(test_x, test_y))
14798     {
14799       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14800
14801       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14802          player as being hit when he is moving towards the bad thing, because
14803          the "get hit by" condition would be lost after the player stops) */
14804       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14805         return;         /* player moves away from bad thing */
14806
14807       kill_x = test_x;
14808       kill_y = test_y;
14809     }
14810     else if (test_element == EL_PENGUIN)
14811     {
14812       kill_x = test_x;
14813       kill_y = test_y;
14814     }
14815   }
14816
14817   if (kill_x != -1 || kill_y != -1)
14818   {
14819     if (IS_PLAYER(kill_x, kill_y))
14820     {
14821       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14822
14823       if (player->shield_deadly_time_left > 0 &&
14824           !IS_INDESTRUCTIBLE(bad_element))
14825         Bang(bad_x, bad_y);
14826       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14827         KillPlayer(player);
14828     }
14829     else
14830       Bang(kill_x, kill_y);
14831   }
14832 }
14833
14834 void TestIfPlayerTouchesBadThing(int x, int y)
14835 {
14836   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14837 }
14838
14839 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14840 {
14841   TestIfGoodThingHitsBadThing(x, y, move_dir);
14842 }
14843
14844 void TestIfBadThingTouchesPlayer(int x, int y)
14845 {
14846   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14847 }
14848
14849 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14850 {
14851   TestIfBadThingHitsGoodThing(x, y, move_dir);
14852 }
14853
14854 void TestIfFriendTouchesBadThing(int x, int y)
14855 {
14856   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14857 }
14858
14859 void TestIfBadThingTouchesFriend(int x, int y)
14860 {
14861   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14862 }
14863
14864 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14865 {
14866   int i, kill_x = bad_x, kill_y = bad_y;
14867   static int xy[4][2] =
14868   {
14869     { 0, -1 },
14870     { -1, 0 },
14871     { +1, 0 },
14872     { 0, +1 }
14873   };
14874
14875   for (i = 0; i < NUM_DIRECTIONS; i++)
14876   {
14877     int x, y, element;
14878
14879     x = bad_x + xy[i][0];
14880     y = bad_y + xy[i][1];
14881     if (!IN_LEV_FIELD(x, y))
14882       continue;
14883
14884     element = Feld[x][y];
14885     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14886         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14887     {
14888       kill_x = x;
14889       kill_y = y;
14890       break;
14891     }
14892   }
14893
14894   if (kill_x != bad_x || kill_y != bad_y)
14895     Bang(bad_x, bad_y);
14896 }
14897
14898 void KillPlayer(struct PlayerInfo *player)
14899 {
14900   int jx = player->jx, jy = player->jy;
14901
14902   if (!player->active)
14903     return;
14904
14905 #if 0
14906   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14907          player->killed, player->active, player->reanimated);
14908 #endif
14909
14910   /* the following code was introduced to prevent an infinite loop when calling
14911      -> Bang()
14912      -> CheckTriggeredElementChangeExt()
14913      -> ExecuteCustomElementAction()
14914      -> KillPlayer()
14915      -> (infinitely repeating the above sequence of function calls)
14916      which occurs when killing the player while having a CE with the setting
14917      "kill player X when explosion of <player X>"; the solution using a new
14918      field "player->killed" was chosen for backwards compatibility, although
14919      clever use of the fields "player->active" etc. would probably also work */
14920 #if 1
14921   if (player->killed)
14922     return;
14923 #endif
14924
14925   player->killed = TRUE;
14926
14927   /* remove accessible field at the player's position */
14928   Feld[jx][jy] = EL_EMPTY;
14929
14930   /* deactivate shield (else Bang()/Explode() would not work right) */
14931   player->shield_normal_time_left = 0;
14932   player->shield_deadly_time_left = 0;
14933
14934 #if 0
14935   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14936          player->killed, player->active, player->reanimated);
14937 #endif
14938
14939   Bang(jx, jy);
14940
14941 #if 0
14942   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14943          player->killed, player->active, player->reanimated);
14944 #endif
14945
14946 #if USE_PLAYER_REANIMATION
14947 #if 1
14948   if (player->reanimated)       /* killed player may have been reanimated */
14949     player->killed = player->reanimated = FALSE;
14950   else
14951     BuryPlayer(player);
14952 #else
14953   if (player->killed)           /* player may have been reanimated */
14954     BuryPlayer(player);
14955 #endif
14956 #else
14957   BuryPlayer(player);
14958 #endif
14959 }
14960
14961 static void KillPlayerUnlessEnemyProtected(int x, int y)
14962 {
14963   if (!PLAYER_ENEMY_PROTECTED(x, y))
14964     KillPlayer(PLAYERINFO(x, y));
14965 }
14966
14967 static void KillPlayerUnlessExplosionProtected(int x, int y)
14968 {
14969   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14970     KillPlayer(PLAYERINFO(x, y));
14971 }
14972
14973 void BuryPlayer(struct PlayerInfo *player)
14974 {
14975   int jx = player->jx, jy = player->jy;
14976
14977   if (!player->active)
14978     return;
14979
14980   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14981   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14982
14983   player->GameOver = TRUE;
14984   RemovePlayer(player);
14985 }
14986
14987 void RemovePlayer(struct PlayerInfo *player)
14988 {
14989   int jx = player->jx, jy = player->jy;
14990   int i, found = FALSE;
14991
14992   player->present = FALSE;
14993   player->active = FALSE;
14994
14995   if (!ExplodeField[jx][jy])
14996     StorePlayer[jx][jy] = 0;
14997
14998   if (player->is_moving)
14999     TEST_DrawLevelField(player->last_jx, player->last_jy);
15000
15001   for (i = 0; i < MAX_PLAYERS; i++)
15002     if (stored_player[i].active)
15003       found = TRUE;
15004
15005   if (!found)
15006     AllPlayersGone = TRUE;
15007
15008   ExitX = ZX = jx;
15009   ExitY = ZY = jy;
15010 }
15011
15012 #if USE_NEW_SNAP_DELAY
15013 static void setFieldForSnapping(int x, int y, int element, int direction)
15014 {
15015   struct ElementInfo *ei = &element_info[element];
15016   int direction_bit = MV_DIR_TO_BIT(direction);
15017   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15018   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15019                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15020
15021   Feld[x][y] = EL_ELEMENT_SNAPPING;
15022   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15023
15024   ResetGfxAnimation(x, y);
15025
15026   GfxElement[x][y] = element;
15027   GfxAction[x][y] = action;
15028   GfxDir[x][y] = direction;
15029   GfxFrame[x][y] = -1;
15030 }
15031 #endif
15032
15033 /*
15034   =============================================================================
15035   checkDiagonalPushing()
15036   -----------------------------------------------------------------------------
15037   check if diagonal input device direction results in pushing of object
15038   (by checking if the alternative direction is walkable, diggable, ...)
15039   =============================================================================
15040 */
15041
15042 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15043                                     int x, int y, int real_dx, int real_dy)
15044 {
15045   int jx, jy, dx, dy, xx, yy;
15046
15047   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
15048     return TRUE;
15049
15050   /* diagonal direction: check alternative direction */
15051   jx = player->jx;
15052   jy = player->jy;
15053   dx = x - jx;
15054   dy = y - jy;
15055   xx = jx + (dx == 0 ? real_dx : 0);
15056   yy = jy + (dy == 0 ? real_dy : 0);
15057
15058   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15059 }
15060
15061 /*
15062   =============================================================================
15063   DigField()
15064   -----------------------------------------------------------------------------
15065   x, y:                 field next to player (non-diagonal) to try to dig to
15066   real_dx, real_dy:     direction as read from input device (can be diagonal)
15067   =============================================================================
15068 */
15069
15070 static int DigField(struct PlayerInfo *player,
15071                     int oldx, int oldy, int x, int y,
15072                     int real_dx, int real_dy, int mode)
15073 {
15074   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15075   boolean player_was_pushing = player->is_pushing;
15076   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15077   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15078   int jx = oldx, jy = oldy;
15079   int dx = x - jx, dy = y - jy;
15080   int nextx = x + dx, nexty = y + dy;
15081   int move_direction = (dx == -1 ? MV_LEFT  :
15082                         dx == +1 ? MV_RIGHT :
15083                         dy == -1 ? MV_UP    :
15084                         dy == +1 ? MV_DOWN  : MV_NONE);
15085   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15086   int dig_side = MV_DIR_OPPOSITE(move_direction);
15087   int old_element = Feld[jx][jy];
15088 #if USE_FIXED_DONT_RUN_INTO
15089   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15090 #else
15091   int element;
15092 #endif
15093   int collect_count;
15094
15095   if (is_player)                /* function can also be called by EL_PENGUIN */
15096   {
15097     if (player->MovPos == 0)
15098     {
15099       player->is_digging = FALSE;
15100       player->is_collecting = FALSE;
15101     }
15102
15103     if (player->MovPos == 0)    /* last pushing move finished */
15104       player->is_pushing = FALSE;
15105
15106     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15107     {
15108       player->is_switching = FALSE;
15109       player->push_delay = -1;
15110
15111       return MP_NO_ACTION;
15112     }
15113   }
15114
15115 #if !USE_FIXED_DONT_RUN_INTO
15116   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15117     return MP_NO_ACTION;
15118 #endif
15119
15120   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15121     old_element = Back[jx][jy];
15122
15123   /* in case of element dropped at player position, check background */
15124   else if (Back[jx][jy] != EL_EMPTY &&
15125            game.engine_version >= VERSION_IDENT(2,2,0,0))
15126     old_element = Back[jx][jy];
15127
15128   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15129     return MP_NO_ACTION;        /* field has no opening in this direction */
15130
15131   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15132     return MP_NO_ACTION;        /* field has no opening in this direction */
15133
15134 #if USE_FIXED_DONT_RUN_INTO
15135   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15136   {
15137     SplashAcid(x, y);
15138
15139     Feld[jx][jy] = player->artwork_element;
15140     InitMovingField(jx, jy, MV_DOWN);
15141     Store[jx][jy] = EL_ACID;
15142     ContinueMoving(jx, jy);
15143     BuryPlayer(player);
15144
15145     return MP_DONT_RUN_INTO;
15146   }
15147 #endif
15148
15149 #if USE_FIXED_DONT_RUN_INTO
15150   if (player_can_move && DONT_RUN_INTO(element))
15151   {
15152     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15153
15154     return MP_DONT_RUN_INTO;
15155   }
15156 #endif
15157
15158 #if USE_FIXED_DONT_RUN_INTO
15159   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15160     return MP_NO_ACTION;
15161 #endif
15162
15163 #if !USE_FIXED_DONT_RUN_INTO
15164   element = Feld[x][y];
15165 #endif
15166
15167   collect_count = element_info[element].collect_count_initial;
15168
15169   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15170     return MP_NO_ACTION;
15171
15172   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15173     player_can_move = player_can_move_or_snap;
15174
15175   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15176       game.engine_version >= VERSION_IDENT(2,2,0,0))
15177   {
15178     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15179                                player->index_bit, dig_side);
15180     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15181                                         player->index_bit, dig_side);
15182
15183     if (element == EL_DC_LANDMINE)
15184       Bang(x, y);
15185
15186     if (Feld[x][y] != element)          /* field changed by snapping */
15187       return MP_ACTION;
15188
15189     return MP_NO_ACTION;
15190   }
15191
15192 #if USE_PLAYER_GRAVITY
15193   if (player->gravity && is_player && !player->is_auto_moving &&
15194       canFallDown(player) && move_direction != MV_DOWN &&
15195       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15196     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15197 #else
15198   if (game.gravity && is_player && !player->is_auto_moving &&
15199       canFallDown(player) && move_direction != MV_DOWN &&
15200       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15201     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15202 #endif
15203
15204   if (player_can_move &&
15205       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15206   {
15207     int sound_element = SND_ELEMENT(element);
15208     int sound_action = ACTION_WALKING;
15209
15210     if (IS_RND_GATE(element))
15211     {
15212       if (!player->key[RND_GATE_NR(element)])
15213         return MP_NO_ACTION;
15214     }
15215     else if (IS_RND_GATE_GRAY(element))
15216     {
15217       if (!player->key[RND_GATE_GRAY_NR(element)])
15218         return MP_NO_ACTION;
15219     }
15220     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15221     {
15222       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15223         return MP_NO_ACTION;
15224     }
15225     else if (element == EL_EXIT_OPEN ||
15226              element == EL_EM_EXIT_OPEN ||
15227 #if 1
15228              element == EL_EM_EXIT_OPENING ||
15229 #endif
15230              element == EL_STEEL_EXIT_OPEN ||
15231              element == EL_EM_STEEL_EXIT_OPEN ||
15232 #if 1
15233              element == EL_EM_STEEL_EXIT_OPENING ||
15234 #endif
15235              element == EL_SP_EXIT_OPEN ||
15236              element == EL_SP_EXIT_OPENING)
15237     {
15238       sound_action = ACTION_PASSING;    /* player is passing exit */
15239     }
15240     else if (element == EL_EMPTY)
15241     {
15242       sound_action = ACTION_MOVING;             /* nothing to walk on */
15243     }
15244
15245     /* play sound from background or player, whatever is available */
15246     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15247       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15248     else
15249       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15250   }
15251   else if (player_can_move &&
15252            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15253   {
15254     if (!ACCESS_FROM(element, opposite_direction))
15255       return MP_NO_ACTION;      /* field not accessible from this direction */
15256
15257     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15258       return MP_NO_ACTION;
15259
15260     if (IS_EM_GATE(element))
15261     {
15262       if (!player->key[EM_GATE_NR(element)])
15263         return MP_NO_ACTION;
15264     }
15265     else if (IS_EM_GATE_GRAY(element))
15266     {
15267       if (!player->key[EM_GATE_GRAY_NR(element)])
15268         return MP_NO_ACTION;
15269     }
15270     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15271     {
15272       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15273         return MP_NO_ACTION;
15274     }
15275     else if (IS_EMC_GATE(element))
15276     {
15277       if (!player->key[EMC_GATE_NR(element)])
15278         return MP_NO_ACTION;
15279     }
15280     else if (IS_EMC_GATE_GRAY(element))
15281     {
15282       if (!player->key[EMC_GATE_GRAY_NR(element)])
15283         return MP_NO_ACTION;
15284     }
15285     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15286     {
15287       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15288         return MP_NO_ACTION;
15289     }
15290     else if (element == EL_DC_GATE_WHITE ||
15291              element == EL_DC_GATE_WHITE_GRAY ||
15292              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15293     {
15294       if (player->num_white_keys == 0)
15295         return MP_NO_ACTION;
15296
15297       player->num_white_keys--;
15298     }
15299     else if (IS_SP_PORT(element))
15300     {
15301       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15302           element == EL_SP_GRAVITY_PORT_RIGHT ||
15303           element == EL_SP_GRAVITY_PORT_UP ||
15304           element == EL_SP_GRAVITY_PORT_DOWN)
15305 #if USE_PLAYER_GRAVITY
15306         player->gravity = !player->gravity;
15307 #else
15308         game.gravity = !game.gravity;
15309 #endif
15310       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15311                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15312                element == EL_SP_GRAVITY_ON_PORT_UP ||
15313                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15314 #if USE_PLAYER_GRAVITY
15315         player->gravity = TRUE;
15316 #else
15317         game.gravity = TRUE;
15318 #endif
15319       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15320                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15321                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15322                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15323 #if USE_PLAYER_GRAVITY
15324         player->gravity = FALSE;
15325 #else
15326         game.gravity = FALSE;
15327 #endif
15328     }
15329
15330     /* automatically move to the next field with double speed */
15331     player->programmed_action = move_direction;
15332
15333     if (player->move_delay_reset_counter == 0)
15334     {
15335       player->move_delay_reset_counter = 2;     /* two double speed steps */
15336
15337       DOUBLE_PLAYER_SPEED(player);
15338     }
15339
15340     PlayLevelSoundAction(x, y, ACTION_PASSING);
15341   }
15342   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15343   {
15344     RemoveField(x, y);
15345
15346     if (mode != DF_SNAP)
15347     {
15348       GfxElement[x][y] = GFX_ELEMENT(element);
15349       player->is_digging = TRUE;
15350     }
15351
15352     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15353
15354     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15355                                         player->index_bit, dig_side);
15356
15357     if (mode == DF_SNAP)
15358     {
15359 #if USE_NEW_SNAP_DELAY
15360       if (level.block_snap_field)
15361         setFieldForSnapping(x, y, element, move_direction);
15362       else
15363         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15364 #else
15365       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15366 #endif
15367
15368       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15369                                           player->index_bit, dig_side);
15370     }
15371   }
15372   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15373   {
15374     RemoveField(x, y);
15375
15376     if (is_player && mode != DF_SNAP)
15377     {
15378       GfxElement[x][y] = element;
15379       player->is_collecting = TRUE;
15380     }
15381
15382     if (element == EL_SPEED_PILL)
15383     {
15384       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15385     }
15386     else if (element == EL_EXTRA_TIME && level.time > 0)
15387     {
15388       TimeLeft += level.extra_time;
15389
15390 #if 1
15391       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15392
15393       DisplayGameControlValues();
15394 #else
15395       DrawGameValue_Time(TimeLeft);
15396 #endif
15397     }
15398     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15399     {
15400       player->shield_normal_time_left += level.shield_normal_time;
15401       if (element == EL_SHIELD_DEADLY)
15402         player->shield_deadly_time_left += level.shield_deadly_time;
15403     }
15404     else if (element == EL_DYNAMITE ||
15405              element == EL_EM_DYNAMITE ||
15406              element == EL_SP_DISK_RED)
15407     {
15408       if (player->inventory_size < MAX_INVENTORY_SIZE)
15409         player->inventory_element[player->inventory_size++] = element;
15410
15411       DrawGameDoorValues();
15412     }
15413     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15414     {
15415       player->dynabomb_count++;
15416       player->dynabombs_left++;
15417     }
15418     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15419     {
15420       player->dynabomb_size++;
15421     }
15422     else if (element == EL_DYNABOMB_INCREASE_POWER)
15423     {
15424       player->dynabomb_xl = TRUE;
15425     }
15426     else if (IS_KEY(element))
15427     {
15428       player->key[KEY_NR(element)] = TRUE;
15429
15430       DrawGameDoorValues();
15431     }
15432     else if (element == EL_DC_KEY_WHITE)
15433     {
15434       player->num_white_keys++;
15435
15436       /* display white keys? */
15437       /* DrawGameDoorValues(); */
15438     }
15439     else if (IS_ENVELOPE(element))
15440     {
15441       player->show_envelope = element;
15442     }
15443     else if (element == EL_EMC_LENSES)
15444     {
15445       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15446
15447       RedrawAllInvisibleElementsForLenses();
15448     }
15449     else if (element == EL_EMC_MAGNIFIER)
15450     {
15451       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15452
15453       RedrawAllInvisibleElementsForMagnifier();
15454     }
15455     else if (IS_DROPPABLE(element) ||
15456              IS_THROWABLE(element))     /* can be collected and dropped */
15457     {
15458       int i;
15459
15460       if (collect_count == 0)
15461         player->inventory_infinite_element = element;
15462       else
15463         for (i = 0; i < collect_count; i++)
15464           if (player->inventory_size < MAX_INVENTORY_SIZE)
15465             player->inventory_element[player->inventory_size++] = element;
15466
15467       DrawGameDoorValues();
15468     }
15469     else if (collect_count > 0)
15470     {
15471       local_player->gems_still_needed -= collect_count;
15472       if (local_player->gems_still_needed < 0)
15473         local_player->gems_still_needed = 0;
15474
15475 #if 1
15476       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15477
15478       DisplayGameControlValues();
15479 #else
15480       DrawGameValue_Emeralds(local_player->gems_still_needed);
15481 #endif
15482     }
15483
15484     RaiseScoreElement(element);
15485     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15486
15487     if (is_player)
15488       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15489                                           player->index_bit, dig_side);
15490
15491     if (mode == DF_SNAP)
15492     {
15493 #if USE_NEW_SNAP_DELAY
15494       if (level.block_snap_field)
15495         setFieldForSnapping(x, y, element, move_direction);
15496       else
15497         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15498 #else
15499       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15500 #endif
15501
15502       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15503                                           player->index_bit, dig_side);
15504     }
15505   }
15506   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15507   {
15508     if (mode == DF_SNAP && element != EL_BD_ROCK)
15509       return MP_NO_ACTION;
15510
15511     if (CAN_FALL(element) && dy)
15512       return MP_NO_ACTION;
15513
15514     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15515         !(element == EL_SPRING && level.use_spring_bug))
15516       return MP_NO_ACTION;
15517
15518     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15519         ((move_direction & MV_VERTICAL &&
15520           ((element_info[element].move_pattern & MV_LEFT &&
15521             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15522            (element_info[element].move_pattern & MV_RIGHT &&
15523             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15524          (move_direction & MV_HORIZONTAL &&
15525           ((element_info[element].move_pattern & MV_UP &&
15526             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15527            (element_info[element].move_pattern & MV_DOWN &&
15528             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15529       return MP_NO_ACTION;
15530
15531     /* do not push elements already moving away faster than player */
15532     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15533         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15534       return MP_NO_ACTION;
15535
15536     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15537     {
15538       if (player->push_delay_value == -1 || !player_was_pushing)
15539         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15540     }
15541     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15542     {
15543       if (player->push_delay_value == -1)
15544         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15545     }
15546     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15547     {
15548       if (!player->is_pushing)
15549         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15550     }
15551
15552     player->is_pushing = TRUE;
15553     player->is_active = TRUE;
15554
15555     if (!(IN_LEV_FIELD(nextx, nexty) &&
15556           (IS_FREE(nextx, nexty) ||
15557            (IS_SB_ELEMENT(element) &&
15558             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15559            (IS_CUSTOM_ELEMENT(element) &&
15560             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15561       return MP_NO_ACTION;
15562
15563     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15564       return MP_NO_ACTION;
15565
15566     if (player->push_delay == -1)       /* new pushing; restart delay */
15567       player->push_delay = 0;
15568
15569     if (player->push_delay < player->push_delay_value &&
15570         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15571         element != EL_SPRING && element != EL_BALLOON)
15572     {
15573       /* make sure that there is no move delay before next try to push */
15574       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15575         player->move_delay = 0;
15576
15577       return MP_NO_ACTION;
15578     }
15579
15580     if (IS_CUSTOM_ELEMENT(element) &&
15581         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15582     {
15583       if (!DigFieldByCE(nextx, nexty, element))
15584         return MP_NO_ACTION;
15585     }
15586
15587     if (IS_SB_ELEMENT(element))
15588     {
15589       if (element == EL_SOKOBAN_FIELD_FULL)
15590       {
15591         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15592         local_player->sokobanfields_still_needed++;
15593       }
15594
15595       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15596       {
15597         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15598         local_player->sokobanfields_still_needed--;
15599       }
15600
15601       Feld[x][y] = EL_SOKOBAN_OBJECT;
15602
15603       if (Back[x][y] == Back[nextx][nexty])
15604         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15605       else if (Back[x][y] != 0)
15606         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15607                                     ACTION_EMPTYING);
15608       else
15609         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15610                                     ACTION_FILLING);
15611
15612 #if 1
15613       if (local_player->sokobanfields_still_needed == 0 &&
15614           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15615 #else
15616       if (local_player->sokobanfields_still_needed == 0 &&
15617           game.emulation == EMU_SOKOBAN)
15618 #endif
15619       {
15620         PlayerWins(player);
15621
15622         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15623       }
15624     }
15625     else
15626       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15627
15628     InitMovingField(x, y, move_direction);
15629     GfxAction[x][y] = ACTION_PUSHING;
15630
15631     if (mode == DF_SNAP)
15632       ContinueMoving(x, y);
15633     else
15634       MovPos[x][y] = (dx != 0 ? dx : dy);
15635
15636     Pushed[x][y] = TRUE;
15637     Pushed[nextx][nexty] = TRUE;
15638
15639     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15640       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15641     else
15642       player->push_delay_value = -1;    /* get new value later */
15643
15644     /* check for element change _after_ element has been pushed */
15645     if (game.use_change_when_pushing_bug)
15646     {
15647       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15648                                  player->index_bit, dig_side);
15649       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15650                                           player->index_bit, dig_side);
15651     }
15652   }
15653   else if (IS_SWITCHABLE(element))
15654   {
15655     if (PLAYER_SWITCHING(player, x, y))
15656     {
15657       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15658                                           player->index_bit, dig_side);
15659
15660       return MP_ACTION;
15661     }
15662
15663     player->is_switching = TRUE;
15664     player->switch_x = x;
15665     player->switch_y = y;
15666
15667     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15668
15669     if (element == EL_ROBOT_WHEEL)
15670     {
15671       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15672       ZX = x;
15673       ZY = y;
15674
15675       game.robot_wheel_active = TRUE;
15676
15677       TEST_DrawLevelField(x, y);
15678     }
15679     else if (element == EL_SP_TERMINAL)
15680     {
15681       int xx, yy;
15682
15683       SCAN_PLAYFIELD(xx, yy)
15684       {
15685         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15686           Bang(xx, yy);
15687         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15688           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15689       }
15690     }
15691     else if (IS_BELT_SWITCH(element))
15692     {
15693       ToggleBeltSwitch(x, y);
15694     }
15695     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15696              element == EL_SWITCHGATE_SWITCH_DOWN ||
15697              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15698              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15699     {
15700       ToggleSwitchgateSwitch(x, y);
15701     }
15702     else if (element == EL_LIGHT_SWITCH ||
15703              element == EL_LIGHT_SWITCH_ACTIVE)
15704     {
15705       ToggleLightSwitch(x, y);
15706     }
15707     else if (element == EL_TIMEGATE_SWITCH ||
15708              element == EL_DC_TIMEGATE_SWITCH)
15709     {
15710       ActivateTimegateSwitch(x, y);
15711     }
15712     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15713              element == EL_BALLOON_SWITCH_RIGHT ||
15714              element == EL_BALLOON_SWITCH_UP    ||
15715              element == EL_BALLOON_SWITCH_DOWN  ||
15716              element == EL_BALLOON_SWITCH_NONE  ||
15717              element == EL_BALLOON_SWITCH_ANY)
15718     {
15719       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15720                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15721                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15722                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15723                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15724                              move_direction);
15725     }
15726     else if (element == EL_LAMP)
15727     {
15728       Feld[x][y] = EL_LAMP_ACTIVE;
15729       local_player->lights_still_needed--;
15730
15731       ResetGfxAnimation(x, y);
15732       TEST_DrawLevelField(x, y);
15733     }
15734     else if (element == EL_TIME_ORB_FULL)
15735     {
15736       Feld[x][y] = EL_TIME_ORB_EMPTY;
15737
15738       if (level.time > 0 || level.use_time_orb_bug)
15739       {
15740         TimeLeft += level.time_orb_time;
15741         game.no_time_limit = FALSE;
15742
15743 #if 1
15744         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15745
15746         DisplayGameControlValues();
15747 #else
15748         DrawGameValue_Time(TimeLeft);
15749 #endif
15750       }
15751
15752       ResetGfxAnimation(x, y);
15753       TEST_DrawLevelField(x, y);
15754     }
15755     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15756              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15757     {
15758       int xx, yy;
15759
15760       game.ball_state = !game.ball_state;
15761
15762       SCAN_PLAYFIELD(xx, yy)
15763       {
15764         int e = Feld[xx][yy];
15765
15766         if (game.ball_state)
15767         {
15768           if (e == EL_EMC_MAGIC_BALL)
15769             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15770           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15771             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15772         }
15773         else
15774         {
15775           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15776             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15777           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15778             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15779         }
15780       }
15781     }
15782
15783     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15784                                         player->index_bit, dig_side);
15785
15786     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15787                                         player->index_bit, dig_side);
15788
15789     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15790                                         player->index_bit, dig_side);
15791
15792     return MP_ACTION;
15793   }
15794   else
15795   {
15796     if (!PLAYER_SWITCHING(player, x, y))
15797     {
15798       player->is_switching = TRUE;
15799       player->switch_x = x;
15800       player->switch_y = y;
15801
15802       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15803                                  player->index_bit, dig_side);
15804       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15805                                           player->index_bit, dig_side);
15806
15807       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15808                                  player->index_bit, dig_side);
15809       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15810                                           player->index_bit, dig_side);
15811     }
15812
15813     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15814                                player->index_bit, dig_side);
15815     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15816                                         player->index_bit, dig_side);
15817
15818     return MP_NO_ACTION;
15819   }
15820
15821   player->push_delay = -1;
15822
15823   if (is_player)                /* function can also be called by EL_PENGUIN */
15824   {
15825     if (Feld[x][y] != element)          /* really digged/collected something */
15826     {
15827       player->is_collecting = !player->is_digging;
15828       player->is_active = TRUE;
15829     }
15830   }
15831
15832   return MP_MOVING;
15833 }
15834
15835 static boolean DigFieldByCE(int x, int y, int digging_element)
15836 {
15837   int element = Feld[x][y];
15838
15839   if (!IS_FREE(x, y))
15840   {
15841     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15842                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15843                   ACTION_BREAKING);
15844
15845     /* no element can dig solid indestructible elements */
15846     if (IS_INDESTRUCTIBLE(element) &&
15847         !IS_DIGGABLE(element) &&
15848         !IS_COLLECTIBLE(element))
15849       return FALSE;
15850
15851     if (AmoebaNr[x][y] &&
15852         (element == EL_AMOEBA_FULL ||
15853          element == EL_BD_AMOEBA ||
15854          element == EL_AMOEBA_GROWING))
15855     {
15856       AmoebaCnt[AmoebaNr[x][y]]--;
15857       AmoebaCnt2[AmoebaNr[x][y]]--;
15858     }
15859
15860     if (IS_MOVING(x, y))
15861       RemoveMovingField(x, y);
15862     else
15863     {
15864       RemoveField(x, y);
15865       TEST_DrawLevelField(x, y);
15866     }
15867
15868     /* if digged element was about to explode, prevent the explosion */
15869     ExplodeField[x][y] = EX_TYPE_NONE;
15870
15871     PlayLevelSoundAction(x, y, action);
15872   }
15873
15874   Store[x][y] = EL_EMPTY;
15875
15876 #if 1
15877   /* this makes it possible to leave the removed element again */
15878   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15879     Store[x][y] = element;
15880 #else
15881   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15882   {
15883     int move_leave_element = element_info[digging_element].move_leave_element;
15884
15885     /* this makes it possible to leave the removed element again */
15886     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15887                    element : move_leave_element);
15888   }
15889 #endif
15890
15891   return TRUE;
15892 }
15893
15894 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15895 {
15896   int jx = player->jx, jy = player->jy;
15897   int x = jx + dx, y = jy + dy;
15898   int snap_direction = (dx == -1 ? MV_LEFT  :
15899                         dx == +1 ? MV_RIGHT :
15900                         dy == -1 ? MV_UP    :
15901                         dy == +1 ? MV_DOWN  : MV_NONE);
15902   boolean can_continue_snapping = (level.continuous_snapping &&
15903                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15904
15905   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15906     return FALSE;
15907
15908   if (!player->active || !IN_LEV_FIELD(x, y))
15909     return FALSE;
15910
15911   if (dx && dy)
15912     return FALSE;
15913
15914   if (!dx && !dy)
15915   {
15916     if (player->MovPos == 0)
15917       player->is_pushing = FALSE;
15918
15919     player->is_snapping = FALSE;
15920
15921     if (player->MovPos == 0)
15922     {
15923       player->is_moving = FALSE;
15924       player->is_digging = FALSE;
15925       player->is_collecting = FALSE;
15926     }
15927
15928     return FALSE;
15929   }
15930
15931 #if USE_NEW_CONTINUOUS_SNAPPING
15932   /* prevent snapping with already pressed snap key when not allowed */
15933   if (player->is_snapping && !can_continue_snapping)
15934     return FALSE;
15935 #else
15936   if (player->is_snapping)
15937     return FALSE;
15938 #endif
15939
15940   player->MovDir = snap_direction;
15941
15942   if (player->MovPos == 0)
15943   {
15944     player->is_moving = FALSE;
15945     player->is_digging = FALSE;
15946     player->is_collecting = FALSE;
15947   }
15948
15949   player->is_dropping = FALSE;
15950   player->is_dropping_pressed = FALSE;
15951   player->drop_pressed_delay = 0;
15952
15953   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15954     return FALSE;
15955
15956   player->is_snapping = TRUE;
15957   player->is_active = TRUE;
15958
15959   if (player->MovPos == 0)
15960   {
15961     player->is_moving = FALSE;
15962     player->is_digging = FALSE;
15963     player->is_collecting = FALSE;
15964   }
15965
15966   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15967     TEST_DrawLevelField(player->last_jx, player->last_jy);
15968
15969   TEST_DrawLevelField(x, y);
15970
15971   return TRUE;
15972 }
15973
15974 static boolean DropElement(struct PlayerInfo *player)
15975 {
15976   int old_element, new_element;
15977   int dropx = player->jx, dropy = player->jy;
15978   int drop_direction = player->MovDir;
15979   int drop_side = drop_direction;
15980 #if 1
15981   int drop_element = get_next_dropped_element(player);
15982 #else
15983   int drop_element = (player->inventory_size > 0 ?
15984                       player->inventory_element[player->inventory_size - 1] :
15985                       player->inventory_infinite_element != EL_UNDEFINED ?
15986                       player->inventory_infinite_element :
15987                       player->dynabombs_left > 0 ?
15988                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15989                       EL_UNDEFINED);
15990 #endif
15991
15992   player->is_dropping_pressed = TRUE;
15993
15994   /* do not drop an element on top of another element; when holding drop key
15995      pressed without moving, dropped element must move away before the next
15996      element can be dropped (this is especially important if the next element
15997      is dynamite, which can be placed on background for historical reasons) */
15998   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15999     return MP_ACTION;
16000
16001   if (IS_THROWABLE(drop_element))
16002   {
16003     dropx += GET_DX_FROM_DIR(drop_direction);
16004     dropy += GET_DY_FROM_DIR(drop_direction);
16005
16006     if (!IN_LEV_FIELD(dropx, dropy))
16007       return FALSE;
16008   }
16009
16010   old_element = Feld[dropx][dropy];     /* old element at dropping position */
16011   new_element = drop_element;           /* default: no change when dropping */
16012
16013   /* check if player is active, not moving and ready to drop */
16014   if (!player->active || player->MovPos || player->drop_delay > 0)
16015     return FALSE;
16016
16017   /* check if player has anything that can be dropped */
16018   if (new_element == EL_UNDEFINED)
16019     return FALSE;
16020
16021   /* check if drop key was pressed long enough for EM style dynamite */
16022   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16023     return FALSE;
16024
16025   /* check if anything can be dropped at the current position */
16026   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16027     return FALSE;
16028
16029   /* collected custom elements can only be dropped on empty fields */
16030   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16031     return FALSE;
16032
16033   if (old_element != EL_EMPTY)
16034     Back[dropx][dropy] = old_element;   /* store old element on this field */
16035
16036   ResetGfxAnimation(dropx, dropy);
16037   ResetRandomAnimationValue(dropx, dropy);
16038
16039   if (player->inventory_size > 0 ||
16040       player->inventory_infinite_element != EL_UNDEFINED)
16041   {
16042     if (player->inventory_size > 0)
16043     {
16044       player->inventory_size--;
16045
16046       DrawGameDoorValues();
16047
16048       if (new_element == EL_DYNAMITE)
16049         new_element = EL_DYNAMITE_ACTIVE;
16050       else if (new_element == EL_EM_DYNAMITE)
16051         new_element = EL_EM_DYNAMITE_ACTIVE;
16052       else if (new_element == EL_SP_DISK_RED)
16053         new_element = EL_SP_DISK_RED_ACTIVE;
16054     }
16055
16056     Feld[dropx][dropy] = new_element;
16057
16058     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16059       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16060                           el2img(Feld[dropx][dropy]), 0);
16061
16062     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16063
16064     /* needed if previous element just changed to "empty" in the last frame */
16065     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16066
16067     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16068                                player->index_bit, drop_side);
16069     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16070                                         CE_PLAYER_DROPS_X,
16071                                         player->index_bit, drop_side);
16072
16073     TestIfElementTouchesCustomElement(dropx, dropy);
16074   }
16075   else          /* player is dropping a dyna bomb */
16076   {
16077     player->dynabombs_left--;
16078
16079     Feld[dropx][dropy] = new_element;
16080
16081     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16082       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16083                           el2img(Feld[dropx][dropy]), 0);
16084
16085     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16086   }
16087
16088   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16089     InitField_WithBug1(dropx, dropy, FALSE);
16090
16091   new_element = Feld[dropx][dropy];     /* element might have changed */
16092
16093   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16094       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16095   {
16096 #if 0
16097     int move_direction;
16098     int nextx, nexty;
16099 #endif
16100
16101     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16102       MovDir[dropx][dropy] = drop_direction;
16103
16104 #if 0
16105     move_direction = MovDir[dropx][dropy];
16106     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16107     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16108 #endif
16109
16110     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16111
16112 #if USE_FIX_IMPACT_COLLISION
16113     /* do not cause impact style collision by dropping elements that can fall */
16114     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16115 #else
16116     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16117 #endif
16118   }
16119
16120   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16121   player->is_dropping = TRUE;
16122
16123   player->drop_pressed_delay = 0;
16124   player->is_dropping_pressed = FALSE;
16125
16126   player->drop_x = dropx;
16127   player->drop_y = dropy;
16128
16129   return TRUE;
16130 }
16131
16132 /* ------------------------------------------------------------------------- */
16133 /* game sound playing functions                                              */
16134 /* ------------------------------------------------------------------------- */
16135
16136 static int *loop_sound_frame = NULL;
16137 static int *loop_sound_volume = NULL;
16138
16139 void InitPlayLevelSound()
16140 {
16141   int num_sounds = getSoundListSize();
16142
16143   checked_free(loop_sound_frame);
16144   checked_free(loop_sound_volume);
16145
16146   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16147   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16148 }
16149
16150 static void PlayLevelSound(int x, int y, int nr)
16151 {
16152   int sx = SCREENX(x), sy = SCREENY(y);
16153   int volume, stereo_position;
16154   int max_distance = 8;
16155   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16156
16157   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16158       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16159     return;
16160
16161   if (!IN_LEV_FIELD(x, y) ||
16162       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16163       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16164     return;
16165
16166   volume = SOUND_MAX_VOLUME;
16167
16168   if (!IN_SCR_FIELD(sx, sy))
16169   {
16170     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16171     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16172
16173     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16174   }
16175
16176   stereo_position = (SOUND_MAX_LEFT +
16177                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16178                      (SCR_FIELDX + 2 * max_distance));
16179
16180   if (IS_LOOP_SOUND(nr))
16181   {
16182     /* This assures that quieter loop sounds do not overwrite louder ones,
16183        while restarting sound volume comparison with each new game frame. */
16184
16185     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16186       return;
16187
16188     loop_sound_volume[nr] = volume;
16189     loop_sound_frame[nr] = FrameCounter;
16190   }
16191
16192   PlaySoundExt(nr, volume, stereo_position, type);
16193 }
16194
16195 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16196 {
16197   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16198                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16199                  y < LEVELY(BY1) ? LEVELY(BY1) :
16200                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16201                  sound_action);
16202 }
16203
16204 static void PlayLevelSoundAction(int x, int y, int action)
16205 {
16206   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16207 }
16208
16209 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16210 {
16211   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16212
16213   if (sound_effect != SND_UNDEFINED)
16214     PlayLevelSound(x, y, sound_effect);
16215 }
16216
16217 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16218                                               int action)
16219 {
16220   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16221
16222   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16223     PlayLevelSound(x, y, sound_effect);
16224 }
16225
16226 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16227 {
16228   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16229
16230   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16231     PlayLevelSound(x, y, sound_effect);
16232 }
16233
16234 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16235 {
16236   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16237
16238   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16239     StopSound(sound_effect);
16240 }
16241
16242 static void PlayLevelMusic()
16243 {
16244   if (levelset.music[level_nr] != MUS_UNDEFINED)
16245     PlayMusic(levelset.music[level_nr]);        /* from config file */
16246   else
16247     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16248 }
16249
16250 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16251 {
16252   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16253   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16254   int x = xx - 1 - offset;
16255   int y = yy - 1 - offset;
16256
16257   switch (sample)
16258   {
16259     case SAMPLE_blank:
16260       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16261       break;
16262
16263     case SAMPLE_roll:
16264       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16265       break;
16266
16267     case SAMPLE_stone:
16268       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16269       break;
16270
16271     case SAMPLE_nut:
16272       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16273       break;
16274
16275     case SAMPLE_crack:
16276       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16277       break;
16278
16279     case SAMPLE_bug:
16280       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16281       break;
16282
16283     case SAMPLE_tank:
16284       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16285       break;
16286
16287     case SAMPLE_android_clone:
16288       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16289       break;
16290
16291     case SAMPLE_android_move:
16292       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16293       break;
16294
16295     case SAMPLE_spring:
16296       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16297       break;
16298
16299     case SAMPLE_slurp:
16300       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16301       break;
16302
16303     case SAMPLE_eater:
16304       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16305       break;
16306
16307     case SAMPLE_eater_eat:
16308       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16309       break;
16310
16311     case SAMPLE_alien:
16312       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16313       break;
16314
16315     case SAMPLE_collect:
16316       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16317       break;
16318
16319     case SAMPLE_diamond:
16320       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16321       break;
16322
16323     case SAMPLE_squash:
16324       /* !!! CHECK THIS !!! */
16325 #if 1
16326       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16327 #else
16328       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16329 #endif
16330       break;
16331
16332     case SAMPLE_wonderfall:
16333       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16334       break;
16335
16336     case SAMPLE_drip:
16337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16338       break;
16339
16340     case SAMPLE_push:
16341       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16342       break;
16343
16344     case SAMPLE_dirt:
16345       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16346       break;
16347
16348     case SAMPLE_acid:
16349       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16350       break;
16351
16352     case SAMPLE_ball:
16353       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16354       break;
16355
16356     case SAMPLE_grow:
16357       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16358       break;
16359
16360     case SAMPLE_wonder:
16361       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16362       break;
16363
16364     case SAMPLE_door:
16365       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16366       break;
16367
16368     case SAMPLE_exit_open:
16369       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16370       break;
16371
16372     case SAMPLE_exit_leave:
16373       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16374       break;
16375
16376     case SAMPLE_dynamite:
16377       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16378       break;
16379
16380     case SAMPLE_tick:
16381       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16382       break;
16383
16384     case SAMPLE_press:
16385       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16386       break;
16387
16388     case SAMPLE_wheel:
16389       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16390       break;
16391
16392     case SAMPLE_boom:
16393       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16394       break;
16395
16396     case SAMPLE_die:
16397       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16398       break;
16399
16400     case SAMPLE_time:
16401       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16402       break;
16403
16404     default:
16405       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16406       break;
16407   }
16408 }
16409
16410 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16411 {
16412   int element = map_element_SP_to_RND(element_sp);
16413   int action = map_action_SP_to_RND(action_sp);
16414   int offset = (setup.sp_show_border_elements ? 0 : 1);
16415   int x = xx - offset;
16416   int y = yy - offset;
16417
16418 #if 0
16419   printf("::: %d -> %d\n", element_sp, action_sp);
16420 #endif
16421
16422   PlayLevelSoundElementAction(x, y, element, action);
16423 }
16424
16425 void RaiseScore(int value)
16426 {
16427   local_player->score += value;
16428
16429 #if 1
16430   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16431
16432   DisplayGameControlValues();
16433 #else
16434   DrawGameValue_Score(local_player->score);
16435 #endif
16436 }
16437
16438 void RaiseScoreElement(int element)
16439 {
16440   switch (element)
16441   {
16442     case EL_EMERALD:
16443     case EL_BD_DIAMOND:
16444     case EL_EMERALD_YELLOW:
16445     case EL_EMERALD_RED:
16446     case EL_EMERALD_PURPLE:
16447     case EL_SP_INFOTRON:
16448       RaiseScore(level.score[SC_EMERALD]);
16449       break;
16450     case EL_DIAMOND:
16451       RaiseScore(level.score[SC_DIAMOND]);
16452       break;
16453     case EL_CRYSTAL:
16454       RaiseScore(level.score[SC_CRYSTAL]);
16455       break;
16456     case EL_PEARL:
16457       RaiseScore(level.score[SC_PEARL]);
16458       break;
16459     case EL_BUG:
16460     case EL_BD_BUTTERFLY:
16461     case EL_SP_ELECTRON:
16462       RaiseScore(level.score[SC_BUG]);
16463       break;
16464     case EL_SPACESHIP:
16465     case EL_BD_FIREFLY:
16466     case EL_SP_SNIKSNAK:
16467       RaiseScore(level.score[SC_SPACESHIP]);
16468       break;
16469     case EL_YAMYAM:
16470     case EL_DARK_YAMYAM:
16471       RaiseScore(level.score[SC_YAMYAM]);
16472       break;
16473     case EL_ROBOT:
16474       RaiseScore(level.score[SC_ROBOT]);
16475       break;
16476     case EL_PACMAN:
16477       RaiseScore(level.score[SC_PACMAN]);
16478       break;
16479     case EL_NUT:
16480       RaiseScore(level.score[SC_NUT]);
16481       break;
16482     case EL_DYNAMITE:
16483     case EL_EM_DYNAMITE:
16484     case EL_SP_DISK_RED:
16485     case EL_DYNABOMB_INCREASE_NUMBER:
16486     case EL_DYNABOMB_INCREASE_SIZE:
16487     case EL_DYNABOMB_INCREASE_POWER:
16488       RaiseScore(level.score[SC_DYNAMITE]);
16489       break;
16490     case EL_SHIELD_NORMAL:
16491     case EL_SHIELD_DEADLY:
16492       RaiseScore(level.score[SC_SHIELD]);
16493       break;
16494     case EL_EXTRA_TIME:
16495       RaiseScore(level.extra_time_score);
16496       break;
16497     case EL_KEY_1:
16498     case EL_KEY_2:
16499     case EL_KEY_3:
16500     case EL_KEY_4:
16501     case EL_EM_KEY_1:
16502     case EL_EM_KEY_2:
16503     case EL_EM_KEY_3:
16504     case EL_EM_KEY_4:
16505     case EL_EMC_KEY_5:
16506     case EL_EMC_KEY_6:
16507     case EL_EMC_KEY_7:
16508     case EL_EMC_KEY_8:
16509     case EL_DC_KEY_WHITE:
16510       RaiseScore(level.score[SC_KEY]);
16511       break;
16512     default:
16513       RaiseScore(element_info[element].collect_score);
16514       break;
16515   }
16516 }
16517
16518 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16519 {
16520   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16521   {
16522 #if 1
16523     /* closing door required in case of envelope style request dialogs */
16524     if (!skip_request)
16525       CloseDoor(DOOR_CLOSE_1);
16526 #endif
16527
16528 #if defined(NETWORK_AVALIABLE)
16529     if (options.network)
16530       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16531     else
16532 #endif
16533     {
16534       if (quick_quit)
16535       {
16536 #if 1
16537
16538 #if 1
16539         FadeSkipNextFadeIn();
16540 #else
16541         fading = fading_none;
16542 #endif
16543
16544 #else
16545         OpenDoor(DOOR_CLOSE_1);
16546 #endif
16547
16548         game_status = GAME_MODE_MAIN;
16549
16550 #if 1
16551         DrawAndFadeInMainMenu(REDRAW_FIELD);
16552 #else
16553         DrawMainMenu();
16554 #endif
16555       }
16556       else
16557       {
16558 #if 0
16559         FadeOut(REDRAW_FIELD);
16560 #endif
16561
16562         game_status = GAME_MODE_MAIN;
16563
16564         DrawAndFadeInMainMenu(REDRAW_FIELD);
16565       }
16566     }
16567   }
16568   else          /* continue playing the game */
16569   {
16570     if (tape.playing && tape.deactivate_display)
16571       TapeDeactivateDisplayOff(TRUE);
16572
16573     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16574
16575     if (tape.playing && tape.deactivate_display)
16576       TapeDeactivateDisplayOn();
16577   }
16578 }
16579
16580 void RequestQuitGame(boolean ask_if_really_quit)
16581 {
16582   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16583   boolean skip_request = AllPlayersGone || quick_quit;
16584
16585   RequestQuitGameExt(skip_request, quick_quit,
16586                      "Do you really want to quit the game?");
16587 }
16588
16589
16590 /* ------------------------------------------------------------------------- */
16591 /* random generator functions                                                */
16592 /* ------------------------------------------------------------------------- */
16593
16594 unsigned int InitEngineRandom_RND(int seed)
16595 {
16596   game.num_random_calls = 0;
16597
16598 #if 0
16599   unsigned int rnd_seed = InitEngineRandom(seed);
16600
16601   printf("::: START RND: %d\n", rnd_seed);
16602
16603   return rnd_seed;
16604 #else
16605
16606   return InitEngineRandom(seed);
16607
16608 #endif
16609
16610 }
16611
16612 unsigned int RND(int max)
16613 {
16614   if (max > 0)
16615   {
16616     game.num_random_calls++;
16617
16618     return GetEngineRandom(max);
16619   }
16620
16621   return 0;
16622 }
16623
16624
16625 /* ------------------------------------------------------------------------- */
16626 /* game engine snapshot handling functions                                   */
16627 /* ------------------------------------------------------------------------- */
16628
16629 struct EngineSnapshotInfo
16630 {
16631   /* runtime values for custom element collect score */
16632   int collect_score[NUM_CUSTOM_ELEMENTS];
16633
16634   /* runtime values for group element choice position */
16635   int choice_pos[NUM_GROUP_ELEMENTS];
16636
16637   /* runtime values for belt position animations */
16638   int belt_graphic[4][NUM_BELT_PARTS];
16639   int belt_anim_mode[4][NUM_BELT_PARTS];
16640 };
16641
16642 static struct EngineSnapshotInfo engine_snapshot_rnd;
16643 static char *snapshot_level_identifier = NULL;
16644 static int snapshot_level_nr = -1;
16645
16646 static void SaveEngineSnapshotValues_RND()
16647 {
16648   static int belt_base_active_element[4] =
16649   {
16650     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16651     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16652     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16653     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16654   };
16655   int i, j;
16656
16657   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16658   {
16659     int element = EL_CUSTOM_START + i;
16660
16661     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16662   }
16663
16664   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16665   {
16666     int element = EL_GROUP_START + i;
16667
16668     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16669   }
16670
16671   for (i = 0; i < 4; i++)
16672   {
16673     for (j = 0; j < NUM_BELT_PARTS; j++)
16674     {
16675       int element = belt_base_active_element[i] + j;
16676       int graphic = el2img(element);
16677       int anim_mode = graphic_info[graphic].anim_mode;
16678
16679       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16680       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16681     }
16682   }
16683 }
16684
16685 static void LoadEngineSnapshotValues_RND()
16686 {
16687   unsigned int num_random_calls = game.num_random_calls;
16688   int i, j;
16689
16690   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16691   {
16692     int element = EL_CUSTOM_START + i;
16693
16694     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16695   }
16696
16697   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16698   {
16699     int element = EL_GROUP_START + i;
16700
16701     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16702   }
16703
16704   for (i = 0; i < 4; i++)
16705   {
16706     for (j = 0; j < NUM_BELT_PARTS; j++)
16707     {
16708       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16709       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16710
16711       graphic_info[graphic].anim_mode = anim_mode;
16712     }
16713   }
16714
16715   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16716   {
16717     InitRND(tape.random_seed);
16718     for (i = 0; i < num_random_calls; i++)
16719       RND(1);
16720   }
16721
16722   if (game.num_random_calls != num_random_calls)
16723   {
16724     Error(ERR_INFO, "number of random calls out of sync");
16725     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16726     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16727     Error(ERR_EXIT, "this should not happen -- please debug");
16728   }
16729 }
16730
16731 void FreeEngineSnapshot()
16732 {
16733   FreeEngineSnapshotBuffers();
16734
16735   setString(&snapshot_level_identifier, NULL);
16736   snapshot_level_nr = -1;
16737 }
16738
16739 void SaveEngineSnapshot()
16740 {
16741   /* do not save snapshots from editor */
16742   if (level_editor_test_game)
16743     return;
16744
16745   /* free previous snapshot buffers, if needed */
16746   FreeEngineSnapshotBuffers();
16747
16748 #if 1
16749   /* copy some special values to a structure better suited for the snapshot */
16750
16751   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16752     SaveEngineSnapshotValues_RND();
16753   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16754     SaveEngineSnapshotValues_EM();
16755   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16756     SaveEngineSnapshotValues_SP();
16757
16758   /* save values stored in special snapshot structure */
16759
16760   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16761     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16762   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16763     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16764   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16765     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16766 #else
16767   /* copy some special values to a structure better suited for the snapshot */
16768
16769   SaveEngineSnapshotValues_RND();
16770   SaveEngineSnapshotValues_EM();
16771   SaveEngineSnapshotValues_SP();
16772
16773   /* save values stored in special snapshot structure */
16774
16775   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16776   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16777   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16778 #endif
16779
16780   /* save further RND engine values */
16781
16782   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16783   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16784   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16785
16786   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16787   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16788   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16789   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16790
16791   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16792   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16793   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16794   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16795   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16796
16797   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16799   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16800
16801   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16802
16803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16804
16805   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16807
16808   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16810   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16813   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16814   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16816   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16818   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16819   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16820   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16822   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16824   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16826
16827   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16829
16830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16833
16834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16836
16837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16842
16843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16845
16846   /* save level identification information */
16847
16848   setString(&snapshot_level_identifier, leveldir_current->identifier);
16849   snapshot_level_nr = level_nr;
16850
16851 #if 0
16852   ListNode *node = engine_snapshot_list_rnd;
16853   int num_bytes = 0;
16854
16855   while (node != NULL)
16856   {
16857     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16858
16859     node = node->next;
16860   }
16861
16862   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16863 #endif
16864 }
16865
16866 void LoadEngineSnapshot()
16867 {
16868   /* restore generically stored snapshot buffers */
16869
16870   LoadEngineSnapshotBuffers();
16871
16872   /* restore special values from snapshot structure */
16873
16874 #if 1
16875   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16876     LoadEngineSnapshotValues_RND();
16877   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16878     LoadEngineSnapshotValues_EM();
16879   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16880     LoadEngineSnapshotValues_SP();
16881 #else
16882   LoadEngineSnapshotValues_RND();
16883   LoadEngineSnapshotValues_EM();
16884   LoadEngineSnapshotValues_SP();
16885 #endif
16886
16887 #if 0
16888   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16889 #endif
16890
16891 #if 0
16892   // needed if tile size was different when saving and loading engine snapshot
16893   if (local_player->present)
16894   {
16895     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
16896                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16897                 local_player->jx - MIDPOSX);
16898
16899     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16900                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16901                 local_player->jy - MIDPOSY);
16902   }
16903 #endif
16904
16905 #if 0
16906   printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16907 #endif
16908 }
16909
16910 boolean CheckEngineSnapshot()
16911 {
16912   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16913           snapshot_level_nr == level_nr);
16914 }
16915
16916
16917 /* ---------- new game button stuff ---------------------------------------- */
16918
16919 static struct
16920 {
16921   int graphic;
16922   struct Rect *pos;
16923   int gadget_id;
16924   char *infotext;
16925 } gamebutton_info[NUM_GAME_BUTTONS] =
16926 {
16927   {
16928     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16929     GAME_CTRL_ID_STOP,                  "stop game"
16930   },
16931   {
16932     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16933     GAME_CTRL_ID_PAUSE,                 "pause game"
16934   },
16935   {
16936     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16937     GAME_CTRL_ID_PLAY,                  "play game"
16938   },
16939   {
16940     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16941     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16942   },
16943   {
16944     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16945     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16946   },
16947   {
16948     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16949     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16950   },
16951   {
16952     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16953     GAME_CTRL_ID_SAVE,                  "save game"
16954   },
16955   {
16956     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16957     GAME_CTRL_ID_LOAD,                  "load game"
16958   }
16959 };
16960
16961 void CreateGameButtons()
16962 {
16963   int i;
16964
16965   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16966   {
16967     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16968     struct Rect *pos = gamebutton_info[i].pos;
16969     struct GadgetInfo *gi;
16970     int button_type;
16971     boolean checked;
16972     unsigned int event_mask;
16973     int base_x = (tape.show_game_buttons ? VX : DX);
16974     int base_y = (tape.show_game_buttons ? VY : DY);
16975     int gd_x   = gfx->src_x;
16976     int gd_y   = gfx->src_y;
16977     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16978     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16979     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16980     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16981     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16982     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16983     int id = i;
16984
16985     if (gfx->bitmap == NULL)
16986     {
16987       game_gadget[id] = NULL;
16988
16989       continue;
16990     }
16991
16992     if (id == GAME_CTRL_ID_STOP ||
16993         id == GAME_CTRL_ID_PAUSE ||
16994         id == GAME_CTRL_ID_PLAY ||
16995         id == GAME_CTRL_ID_SAVE ||
16996         id == GAME_CTRL_ID_LOAD)
16997     {
16998       button_type = GD_TYPE_NORMAL_BUTTON;
16999       checked = FALSE;
17000       event_mask = GD_EVENT_RELEASED;
17001     }
17002     else
17003     {
17004       button_type = GD_TYPE_CHECK_BUTTON;
17005       checked =
17006         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17007          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17008          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17009       event_mask = GD_EVENT_PRESSED;
17010     }
17011
17012     gi = CreateGadget(GDI_CUSTOM_ID, id,
17013                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
17014                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17015                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17016                       GDI_WIDTH, gfx->width,
17017                       GDI_HEIGHT, gfx->height,
17018                       GDI_TYPE, button_type,
17019                       GDI_STATE, GD_BUTTON_UNPRESSED,
17020                       GDI_CHECKED, checked,
17021                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17022                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17023                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17024                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17025                       GDI_DIRECT_DRAW, FALSE,
17026                       GDI_EVENT_MASK, event_mask,
17027                       GDI_CALLBACK_ACTION, HandleGameButtons,
17028                       GDI_END);
17029
17030     if (gi == NULL)
17031       Error(ERR_EXIT, "cannot create gadget");
17032
17033     game_gadget[id] = gi;
17034   }
17035 }
17036
17037 void FreeGameButtons()
17038 {
17039   int i;
17040
17041   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17042     FreeGadget(game_gadget[i]);
17043 }
17044
17045 void MapGameButtons()
17046 {
17047   int i;
17048
17049   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17050     MapGadget(game_gadget[i]);
17051 }
17052
17053 void UnmapGameButtons()
17054 {
17055   int i;
17056
17057   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17058     UnmapGadget(game_gadget[i]);
17059 }
17060
17061 void RedrawGameButtons()
17062 {
17063   int i;
17064
17065   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17066     RedrawGadget(game_gadget[i]);
17067 }
17068
17069 static void HandleGameButtonsExt(int id)
17070 {
17071   boolean handle_game_buttons =
17072     (game_status == GAME_MODE_PLAYING ||
17073      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17074
17075   if (!handle_game_buttons)
17076     return;
17077
17078   switch (id)
17079   {
17080     case GAME_CTRL_ID_STOP:
17081       if (game_status == GAME_MODE_MAIN)
17082         break;
17083
17084       if (tape.playing)
17085         TapeStop();
17086       else
17087         RequestQuitGame(TRUE);
17088
17089       break;
17090
17091     case GAME_CTRL_ID_PAUSE:
17092       if (options.network && game_status == GAME_MODE_PLAYING)
17093       {
17094 #if defined(NETWORK_AVALIABLE)
17095         if (tape.pausing)
17096           SendToServer_ContinuePlaying();
17097         else
17098           SendToServer_PausePlaying();
17099 #endif
17100       }
17101       else
17102         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17103       break;
17104
17105     case GAME_CTRL_ID_PLAY:
17106       if (game_status == GAME_MODE_MAIN)
17107       {
17108         StartGameActions(options.network, setup.autorecord, level.random_seed);
17109       }
17110       else if (tape.pausing)
17111       {
17112 #if defined(NETWORK_AVALIABLE)
17113         if (options.network)
17114           SendToServer_ContinuePlaying();
17115         else
17116 #endif
17117         {
17118           tape.pausing = FALSE;
17119           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17120         }
17121       }
17122       break;
17123
17124     case SOUND_CTRL_ID_MUSIC:
17125       if (setup.sound_music)
17126       { 
17127         setup.sound_music = FALSE;
17128
17129         FadeMusic();
17130       }
17131       else if (audio.music_available)
17132       { 
17133         setup.sound = setup.sound_music = TRUE;
17134
17135         SetAudioMode(setup.sound);
17136
17137         PlayLevelMusic();
17138       }
17139       break;
17140
17141     case SOUND_CTRL_ID_LOOPS:
17142       if (setup.sound_loops)
17143         setup.sound_loops = FALSE;
17144       else if (audio.loops_available)
17145       {
17146         setup.sound = setup.sound_loops = TRUE;
17147
17148         SetAudioMode(setup.sound);
17149       }
17150       break;
17151
17152     case SOUND_CTRL_ID_SIMPLE:
17153       if (setup.sound_simple)
17154         setup.sound_simple = FALSE;
17155       else if (audio.sound_available)
17156       {
17157         setup.sound = setup.sound_simple = TRUE;
17158
17159         SetAudioMode(setup.sound);
17160       }
17161       break;
17162
17163     case GAME_CTRL_ID_SAVE:
17164       TapeQuickSave();
17165       break;
17166
17167     case GAME_CTRL_ID_LOAD:
17168       TapeQuickLoad();
17169       break;
17170
17171     default:
17172       break;
17173   }
17174 }
17175
17176 static void HandleGameButtons(struct GadgetInfo *gi)
17177 {
17178   HandleGameButtonsExt(gi->custom_id);
17179 }
17180
17181 void HandleSoundButtonKeys(Key key)
17182 {
17183 #if 1
17184   if (key == setup.shortcut.sound_simple)
17185     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17186   else if (key == setup.shortcut.sound_loops)
17187     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17188   else if (key == setup.shortcut.sound_music)
17189     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17190 #else
17191   if (key == setup.shortcut.sound_simple)
17192     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17193   else if (key == setup.shortcut.sound_loops)
17194     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17195   else if (key == setup.shortcut.sound_music)
17196     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17197 #endif
17198 }