2a88b9a805f5de15d3e2ac015f8c89ad661048ee
[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
1018 #define NUM_GAME_BUTTONS                6
1019
1020
1021 /* forward declaration for internal use */
1022
1023 static void CreateField(int, int, int);
1024
1025 static void ResetGfxAnimation(int, int);
1026
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1029
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1034
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1039
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1046
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1050 #if 0
1051 static void TestIfElementSmashesCustomElement(int, int, int);
1052 #endif
1053
1054 static void HandleElementChange(int, int, int);
1055 static void ExecuteCustomElementAction(int, int, int, int);
1056 static boolean ChangeElement(int, int, int, int);
1057
1058 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1059 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1060         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1061 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1063 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1065 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1066         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1067
1068 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1069 #define CheckElementChange(x, y, e, te, ev)                             \
1070         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1071 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1072         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1073 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1074         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1075
1076 static void PlayLevelSound(int, int, int);
1077 static void PlayLevelSoundNearest(int, int, int);
1078 static void PlayLevelSoundAction(int, int, int);
1079 static void PlayLevelSoundElementAction(int, int, int, int);
1080 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1081 static void PlayLevelSoundActionIfLoop(int, int, int);
1082 static void StopLevelSoundActionIfLoop(int, int, int);
1083 static void PlayLevelMusic();
1084
1085 static void MapGameButtons();
1086 static void HandleGameButtons(struct GadgetInfo *);
1087
1088 int AmoebeNachbarNr(int, int);
1089 void AmoebeUmwandeln(int, int);
1090 void ContinueMoving(int, int);
1091 void Bang(int, int);
1092 void InitMovDir(int, int);
1093 void InitAmoebaNr(int, int);
1094 int NewHiScore(void);
1095
1096 void TestIfGoodThingHitsBadThing(int, int, int);
1097 void TestIfBadThingHitsGoodThing(int, int, int);
1098 void TestIfPlayerTouchesBadThing(int, int);
1099 void TestIfPlayerRunsIntoBadThing(int, int, int);
1100 void TestIfBadThingTouchesPlayer(int, int);
1101 void TestIfBadThingRunsIntoPlayer(int, int, int);
1102 void TestIfFriendTouchesBadThing(int, int);
1103 void TestIfBadThingTouchesFriend(int, int);
1104 void TestIfBadThingTouchesOtherBadThing(int, int);
1105 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1106
1107 void KillPlayer(struct PlayerInfo *);
1108 void BuryPlayer(struct PlayerInfo *);
1109 void RemovePlayer(struct PlayerInfo *);
1110
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1113
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1119
1120 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1121 {                                                                       \
1122   if (recursion_loop_detected)                                          \
1123     return (rc);                                                        \
1124                                                                         \
1125   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1126   {                                                                     \
1127     recursion_loop_detected = TRUE;                                     \
1128     recursion_loop_element = (e);                                       \
1129   }                                                                     \
1130                                                                         \
1131   recursion_loop_depth++;                                               \
1132 }
1133
1134 #define RECURSION_LOOP_DETECTION_END()                                  \
1135 {                                                                       \
1136   recursion_loop_depth--;                                               \
1137 }
1138
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1142
1143 static int map_player_action[MAX_PLAYERS];
1144
1145 static boolean TEST_game_team_mode;
1146
1147
1148 /* ------------------------------------------------------------------------- */
1149 /* definition of elements that automatically change to other elements after  */
1150 /* a specified time, eventually calling a function when changing             */
1151 /* ------------------------------------------------------------------------- */
1152
1153 /* forward declaration for changer functions */
1154 static void InitBuggyBase(int, int);
1155 static void WarnBuggyBase(int, int);
1156
1157 static void InitTrap(int, int);
1158 static void ActivateTrap(int, int);
1159 static void ChangeActiveTrap(int, int);
1160
1161 static void InitRobotWheel(int, int);
1162 static void RunRobotWheel(int, int);
1163 static void StopRobotWheel(int, int);
1164
1165 static void InitTimegateWheel(int, int);
1166 static void RunTimegateWheel(int, int);
1167
1168 static void InitMagicBallDelay(int, int);
1169 static void ActivateMagicBall(int, int);
1170
1171 struct ChangingElementInfo
1172 {
1173   int element;
1174   int target_element;
1175   int change_delay;
1176   void (*pre_change_function)(int x, int y);
1177   void (*change_function)(int x, int y);
1178   void (*post_change_function)(int x, int y);
1179 };
1180
1181 static struct ChangingElementInfo change_delay_list[] =
1182 {
1183   {
1184     EL_NUT_BREAKING,
1185     EL_EMERALD,
1186     6,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_PEARL_BREAKING,
1193     EL_EMPTY,
1194     8,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EXIT_OPENING,
1201     EL_EXIT_OPEN,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_CLOSING,
1209     EL_EXIT_CLOSED,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_STEEL_EXIT_OPENING,
1217     EL_STEEL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_CLOSING,
1225     EL_STEEL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_EM_EXIT_OPENING,
1233     EL_EM_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_CLOSING,
1241 #if 1
1242     EL_EMPTY,
1243 #else
1244     EL_EM_EXIT_CLOSED,
1245 #endif
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261 #if 1
1262     EL_STEELWALL,
1263 #else
1264     EL_EM_STEEL_EXIT_CLOSED,
1265 #endif
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 /* static variables for playfield scan mode (scanning forward or backward) */
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite()
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars()
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   /* make sure that stepsize value is always a power of 2 */
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   /* do no immediately change move delay -- the player might just be moving */
1627   player->move_delay_value_next = move_delay;
1628
1629   /* information if player can move must be set separately */
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig()
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662   InitJoysticks();
1663 }
1664
1665 int GetElementFromGroupElement(int element)
1666 {
1667   if (IS_GROUP_ELEMENT(element))
1668   {
1669     struct ElementGroupInfo *group = element_info[element].group;
1670     int last_anim_random_frame = gfx.anim_random_frame;
1671     int element_pos;
1672
1673     if (group->choice_mode == ANIM_RANDOM)
1674       gfx.anim_random_frame = RND(group->num_elements_resolved);
1675
1676     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1677                                     group->choice_mode, 0,
1678                                     group->choice_pos);
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = last_anim_random_frame;
1682
1683     group->choice_pos++;
1684
1685     element = group->element_resolved[element_pos];
1686   }
1687
1688   return element;
1689 }
1690
1691 static void InitPlayerField(int x, int y, int element, boolean init_game)
1692 {
1693   if (element == EL_SP_MURPHY)
1694   {
1695     if (init_game)
1696     {
1697       if (stored_player[0].present)
1698       {
1699         Feld[x][y] = EL_SP_MURPHY_CLONE;
1700
1701         return;
1702       }
1703       else
1704       {
1705         stored_player[0].initial_element = element;
1706         stored_player[0].use_murphy = TRUE;
1707
1708         if (!level.use_artwork_element[0])
1709           stored_player[0].artwork_element = EL_SP_MURPHY;
1710       }
1711
1712       Feld[x][y] = EL_PLAYER_1;
1713     }
1714   }
1715
1716   if (init_game)
1717   {
1718     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1719     int jx = player->jx, jy = player->jy;
1720
1721     player->present = TRUE;
1722
1723     player->block_last_field = (element == EL_SP_MURPHY ?
1724                                 level.sp_block_last_field :
1725                                 level.block_last_field);
1726
1727     /* ---------- initialize player's last field block delay --------------- */
1728
1729     /* always start with reliable default value (no adjustment needed) */
1730     player->block_delay_adjustment = 0;
1731
1732     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1733     if (player->block_last_field && element == EL_SP_MURPHY)
1734       player->block_delay_adjustment = 1;
1735
1736     /* special case 2: in game engines before 3.1.1, blocking was different */
1737     if (game.use_block_last_field_bug)
1738       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1739
1740     if (!options.network || player->connected)
1741     {
1742       player->active = TRUE;
1743
1744       /* remove potentially duplicate players */
1745       if (StorePlayer[jx][jy] == Feld[x][y])
1746         StorePlayer[jx][jy] = 0;
1747
1748       StorePlayer[x][y] = Feld[x][y];
1749
1750 #if DEBUG_INIT_PLAYER
1751       if (options.debug)
1752       {
1753         printf("- player element %d activated", player->element_nr);
1754         printf(" (local player is %d and currently %s)\n",
1755                local_player->element_nr,
1756                local_player->active ? "active" : "not active");
1757       }
1758     }
1759 #endif
1760
1761     Feld[x][y] = EL_EMPTY;
1762
1763     player->jx = player->last_jx = x;
1764     player->jy = player->last_jy = y;
1765   }
1766
1767 #if USE_PLAYER_REANIMATION
1768   if (!init_game)
1769   {
1770     int player_nr = GET_PLAYER_NR(element);
1771     struct PlayerInfo *player = &stored_player[player_nr];
1772
1773     if (player->active && player->killed)
1774       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1775   }
1776 #endif
1777 }
1778
1779 static void InitField(int x, int y, boolean init_game)
1780 {
1781   int element = Feld[x][y];
1782
1783   switch (element)
1784   {
1785     case EL_SP_MURPHY:
1786     case EL_PLAYER_1:
1787     case EL_PLAYER_2:
1788     case EL_PLAYER_3:
1789     case EL_PLAYER_4:
1790       InitPlayerField(x, y, element, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_PLAYER:
1794       element = Feld[x][y] = EL_PLAYER_1;
1795       InitField(x, y, init_game);
1796
1797       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1798       InitField(x, y, init_game);
1799       break;
1800
1801     case EL_SOKOBAN_FIELD_EMPTY:
1802       local_player->sokobanfields_still_needed++;
1803       break;
1804
1805     case EL_STONEBLOCK:
1806       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1807         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1808       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1809         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1810       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1811         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1812       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1813         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1814       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1815         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816       break;
1817
1818     case EL_BUG:
1819     case EL_BUG_RIGHT:
1820     case EL_BUG_UP:
1821     case EL_BUG_LEFT:
1822     case EL_BUG_DOWN:
1823     case EL_SPACESHIP:
1824     case EL_SPACESHIP_RIGHT:
1825     case EL_SPACESHIP_UP:
1826     case EL_SPACESHIP_LEFT:
1827     case EL_SPACESHIP_DOWN:
1828     case EL_BD_BUTTERFLY:
1829     case EL_BD_BUTTERFLY_RIGHT:
1830     case EL_BD_BUTTERFLY_UP:
1831     case EL_BD_BUTTERFLY_LEFT:
1832     case EL_BD_BUTTERFLY_DOWN:
1833     case EL_BD_FIREFLY:
1834     case EL_BD_FIREFLY_RIGHT:
1835     case EL_BD_FIREFLY_UP:
1836     case EL_BD_FIREFLY_LEFT:
1837     case EL_BD_FIREFLY_DOWN:
1838     case EL_PACMAN_RIGHT:
1839     case EL_PACMAN_UP:
1840     case EL_PACMAN_LEFT:
1841     case EL_PACMAN_DOWN:
1842     case EL_YAMYAM:
1843     case EL_YAMYAM_LEFT:
1844     case EL_YAMYAM_RIGHT:
1845     case EL_YAMYAM_UP:
1846     case EL_YAMYAM_DOWN:
1847     case EL_DARK_YAMYAM:
1848     case EL_ROBOT:
1849     case EL_PACMAN:
1850     case EL_SP_SNIKSNAK:
1851     case EL_SP_ELECTRON:
1852     case EL_MOLE:
1853     case EL_MOLE_LEFT:
1854     case EL_MOLE_RIGHT:
1855     case EL_MOLE_UP:
1856     case EL_MOLE_DOWN:
1857       InitMovDir(x, y);
1858       break;
1859
1860     case EL_AMOEBA_FULL:
1861     case EL_BD_AMOEBA:
1862       InitAmoebaNr(x, y);
1863       break;
1864
1865     case EL_AMOEBA_DROP:
1866       if (y == lev_fieldy - 1)
1867       {
1868         Feld[x][y] = EL_AMOEBA_GROWING;
1869         Store[x][y] = EL_AMOEBA_WET;
1870       }
1871       break;
1872
1873     case EL_DYNAMITE_ACTIVE:
1874     case EL_SP_DISK_RED_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1876     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1877     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1878     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1879       MovDelay[x][y] = 96;
1880       break;
1881
1882     case EL_EM_DYNAMITE_ACTIVE:
1883       MovDelay[x][y] = 32;
1884       break;
1885
1886     case EL_LAMP:
1887       local_player->lights_still_needed++;
1888       break;
1889
1890     case EL_PENGUIN:
1891       local_player->friends_still_needed++;
1892       break;
1893
1894     case EL_PIG:
1895     case EL_DRAGON:
1896       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1897       break;
1898
1899     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1900     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1901     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1902     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1903     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1904     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1905     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1906     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1907     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1908     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1909     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1910     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1911       if (init_game)
1912       {
1913         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1914         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1915         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1916
1917         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1918         {
1919           game.belt_dir[belt_nr] = belt_dir;
1920           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1921         }
1922         else    /* more than one switch -- set it like the first switch */
1923         {
1924           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1925         }
1926       }
1927       break;
1928
1929 #if !USE_BOTH_SWITCHGATE_SWITCHES
1930     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1931       if (init_game)
1932         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1933       break;
1934
1935     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1936       if (init_game)
1937         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1938       break;
1939 #endif
1940
1941     case EL_LIGHT_SWITCH_ACTIVE:
1942       if (init_game)
1943         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1944       break;
1945
1946     case EL_INVISIBLE_STEELWALL:
1947     case EL_INVISIBLE_WALL:
1948     case EL_INVISIBLE_SAND:
1949       if (game.light_time_left > 0 ||
1950           game.lenses_time_left > 0)
1951         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1952       break;
1953
1954     case EL_EMC_MAGIC_BALL:
1955       if (game.ball_state)
1956         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1957       break;
1958
1959     case EL_EMC_MAGIC_BALL_SWITCH:
1960       if (game.ball_state)
1961         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1962       break;
1963
1964     case EL_TRIGGER_PLAYER:
1965     case EL_TRIGGER_ELEMENT:
1966     case EL_TRIGGER_CE_VALUE:
1967     case EL_TRIGGER_CE_SCORE:
1968     case EL_SELF:
1969     case EL_ANY_ELEMENT:
1970     case EL_CURRENT_CE_VALUE:
1971     case EL_CURRENT_CE_SCORE:
1972     case EL_PREV_CE_1:
1973     case EL_PREV_CE_2:
1974     case EL_PREV_CE_3:
1975     case EL_PREV_CE_4:
1976     case EL_PREV_CE_5:
1977     case EL_PREV_CE_6:
1978     case EL_PREV_CE_7:
1979     case EL_PREV_CE_8:
1980     case EL_NEXT_CE_1:
1981     case EL_NEXT_CE_2:
1982     case EL_NEXT_CE_3:
1983     case EL_NEXT_CE_4:
1984     case EL_NEXT_CE_5:
1985     case EL_NEXT_CE_6:
1986     case EL_NEXT_CE_7:
1987     case EL_NEXT_CE_8:
1988       /* reference elements should not be used on the playfield */
1989       Feld[x][y] = EL_EMPTY;
1990       break;
1991
1992     default:
1993       if (IS_CUSTOM_ELEMENT(element))
1994       {
1995         if (CAN_MOVE(element))
1996           InitMovDir(x, y);
1997
1998 #if USE_NEW_CUSTOM_VALUE
1999         if (!element_info[element].use_last_ce_value || init_game)
2000           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2001 #endif
2002       }
2003       else if (IS_GROUP_ELEMENT(element))
2004       {
2005         Feld[x][y] = GetElementFromGroupElement(element);
2006
2007         InitField(x, y, init_game);
2008       }
2009
2010       break;
2011   }
2012
2013   if (!init_game)
2014     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2015 }
2016
2017 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2018 {
2019   InitField(x, y, init_game);
2020
2021   /* not needed to call InitMovDir() -- already done by InitField()! */
2022   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2023       CAN_MOVE(Feld[x][y]))
2024     InitMovDir(x, y);
2025 }
2026
2027 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2028 {
2029   int old_element = Feld[x][y];
2030
2031   InitField(x, y, init_game);
2032
2033   /* not needed to call InitMovDir() -- already done by InitField()! */
2034   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2035       CAN_MOVE(old_element) &&
2036       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2037     InitMovDir(x, y);
2038
2039   /* this case is in fact a combination of not less than three bugs:
2040      first, it calls InitMovDir() for elements that can move, although this is
2041      already done by InitField(); then, it checks the element that was at this
2042      field _before_ the call to InitField() (which can change it); lastly, it
2043      was not called for "mole with direction" elements, which were treated as
2044      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2045   */
2046 }
2047
2048 static int get_key_element_from_nr(int key_nr)
2049 {
2050   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2051                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2052                           EL_EM_KEY_1 : EL_KEY_1);
2053
2054   return key_base_element + key_nr;
2055 }
2056
2057 static int get_next_dropped_element(struct PlayerInfo *player)
2058 {
2059   return (player->inventory_size > 0 ?
2060           player->inventory_element[player->inventory_size - 1] :
2061           player->inventory_infinite_element != EL_UNDEFINED ?
2062           player->inventory_infinite_element :
2063           player->dynabombs_left > 0 ?
2064           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2065           EL_UNDEFINED);
2066 }
2067
2068 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2069 {
2070   /* pos >= 0: get element from bottom of the stack;
2071      pos <  0: get element from top of the stack */
2072
2073   if (pos < 0)
2074   {
2075     int min_inventory_size = -pos;
2076     int inventory_pos = player->inventory_size - min_inventory_size;
2077     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2078
2079     return (player->inventory_size >= min_inventory_size ?
2080             player->inventory_element[inventory_pos] :
2081             player->inventory_infinite_element != EL_UNDEFINED ?
2082             player->inventory_infinite_element :
2083             player->dynabombs_left >= min_dynabombs_left ?
2084             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2085             EL_UNDEFINED);
2086   }
2087   else
2088   {
2089     int min_dynabombs_left = pos + 1;
2090     int min_inventory_size = pos + 1 - player->dynabombs_left;
2091     int inventory_pos = pos - player->dynabombs_left;
2092
2093     return (player->inventory_infinite_element != EL_UNDEFINED ?
2094             player->inventory_infinite_element :
2095             player->dynabombs_left >= min_dynabombs_left ?
2096             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2097             player->inventory_size >= min_inventory_size ?
2098             player->inventory_element[inventory_pos] :
2099             EL_UNDEFINED);
2100   }
2101 }
2102
2103 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2104 {
2105   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2106   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2107   int compare_result;
2108
2109   if (gpo1->sort_priority != gpo2->sort_priority)
2110     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2111   else
2112     compare_result = gpo1->nr - gpo2->nr;
2113
2114   return compare_result;
2115 }
2116
2117 void InitGameControlValues()
2118 {
2119   int i;
2120
2121   for (i = 0; game_panel_controls[i].nr != -1; i++)
2122   {
2123     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2124     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2125     struct TextPosInfo *pos = gpc->pos;
2126     int nr = gpc->nr;
2127     int type = gpc->type;
2128
2129     if (nr != i)
2130     {
2131       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2132       Error(ERR_EXIT, "this should not happen -- please debug");
2133     }
2134
2135     /* force update of game controls after initialization */
2136     gpc->value = gpc->last_value = -1;
2137     gpc->frame = gpc->last_frame = -1;
2138     gpc->gfx_frame = -1;
2139
2140     /* determine panel value width for later calculation of alignment */
2141     if (type == TYPE_INTEGER || type == TYPE_STRING)
2142     {
2143       pos->width = pos->size * getFontWidth(pos->font);
2144       pos->height = getFontHeight(pos->font);
2145     }
2146     else if (type == TYPE_ELEMENT)
2147     {
2148       pos->width = pos->size;
2149       pos->height = pos->size;
2150     }
2151
2152     /* fill structure for game panel draw order */
2153     gpo->nr = gpc->nr;
2154     gpo->sort_priority = pos->sort_priority;
2155   }
2156
2157   /* sort game panel controls according to sort_priority and control number */
2158   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2159         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2160 }
2161
2162 void UpdatePlayfieldElementCount()
2163 {
2164   boolean use_element_count = FALSE;
2165   int i, j, x, y;
2166
2167   /* first check if it is needed at all to calculate playfield element count */
2168   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2169     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2170       use_element_count = TRUE;
2171
2172   if (!use_element_count)
2173     return;
2174
2175   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2176     element_info[i].element_count = 0;
2177
2178   SCAN_PLAYFIELD(x, y)
2179   {
2180     element_info[Feld[x][y]].element_count++;
2181   }
2182
2183   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2184     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2185       if (IS_IN_GROUP(j, i))
2186         element_info[EL_GROUP_START + i].element_count +=
2187           element_info[j].element_count;
2188 }
2189
2190 void UpdateGameControlValues()
2191 {
2192   int i, k;
2193   int time = (local_player->LevelSolved ?
2194               local_player->LevelSolved_CountingTime :
2195               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2196               level.native_em_level->lev->time :
2197               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2198               level.native_sp_level->game_sp->time_played :
2199               game.no_time_limit ? TimePlayed : TimeLeft);
2200   int score = (local_player->LevelSolved ?
2201                local_player->LevelSolved_CountingScore :
2202                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2203                level.native_em_level->lev->score :
2204                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2205                level.native_sp_level->game_sp->score :
2206                local_player->score);
2207   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208               level.native_em_level->lev->required :
2209               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210               level.native_sp_level->game_sp->infotrons_still_needed :
2211               local_player->gems_still_needed);
2212   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213                      level.native_em_level->lev->required > 0 :
2214                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2216                      local_player->gems_still_needed > 0 ||
2217                      local_player->sokobanfields_still_needed > 0 ||
2218                      local_player->lights_still_needed > 0);
2219
2220   UpdatePlayfieldElementCount();
2221
2222   /* update game panel control values */
2223
2224   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2225   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2226
2227   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2228   for (i = 0; i < MAX_NUM_KEYS; i++)
2229     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2230   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2231   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2232
2233   if (game.centered_player_nr == -1)
2234   {
2235     for (i = 0; i < MAX_PLAYERS; i++)
2236     {
2237       /* only one player in Supaplex game engine */
2238       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2239         break;
2240
2241       for (k = 0; k < MAX_NUM_KEYS; k++)
2242       {
2243         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2244         {
2245           if (level.native_em_level->ply[i]->keys & (1 << k))
2246             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247               get_key_element_from_nr(k);
2248         }
2249         else if (stored_player[i].key[k])
2250           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251             get_key_element_from_nr(k);
2252       }
2253
2254       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2255         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256           level.native_em_level->ply[i]->dynamite;
2257       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2258         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259           level.native_sp_level->game_sp->red_disk_count;
2260       else
2261         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2262           stored_player[i].inventory_size;
2263
2264       if (stored_player[i].num_white_keys > 0)
2265         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2266           EL_DC_KEY_WHITE;
2267
2268       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2269         stored_player[i].num_white_keys;
2270     }
2271   }
2272   else
2273   {
2274     int player_nr = game.centered_player_nr;
2275
2276     for (k = 0; k < MAX_NUM_KEYS; k++)
2277     {
2278       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279       {
2280         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2281           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282             get_key_element_from_nr(k);
2283       }
2284       else if (stored_player[player_nr].key[k])
2285         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286           get_key_element_from_nr(k);
2287     }
2288
2289     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         level.native_em_level->ply[player_nr]->dynamite;
2292     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         level.native_sp_level->game_sp->red_disk_count;
2295     else
2296       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2297         stored_player[player_nr].inventory_size;
2298
2299     if (stored_player[player_nr].num_white_keys > 0)
2300       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2301
2302     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2303       stored_player[player_nr].num_white_keys;
2304   }
2305
2306   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2307   {
2308     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2309       get_inventory_element_from_pos(local_player, i);
2310     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2311       get_inventory_element_from_pos(local_player, -i - 1);
2312   }
2313
2314   game_panel_controls[GAME_PANEL_SCORE].value = score;
2315   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2316
2317   game_panel_controls[GAME_PANEL_TIME].value = time;
2318
2319   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2320   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2321   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2322
2323   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2324
2325   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2326     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2327      EL_EMPTY);
2328   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2329     local_player->shield_normal_time_left;
2330   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2331     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2332      EL_EMPTY);
2333   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2334     local_player->shield_deadly_time_left;
2335
2336   game_panel_controls[GAME_PANEL_EXIT].value =
2337     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2338
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2341   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2342     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2343      EL_EMC_MAGIC_BALL_SWITCH);
2344
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2346     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2347   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2348     game.light_time_left;
2349
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2351     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2352   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2353     game.timegate_time_left;
2354
2355   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2356     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2357
2358   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2359     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2361     game.lenses_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2364     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2365   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2366     game.magnify_time_left;
2367
2368   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2369     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2370      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2371      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2372      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2373      EL_BALLOON_SWITCH_NONE);
2374
2375   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2376     local_player->dynabomb_count;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2378     local_player->dynabomb_size;
2379   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2380     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2381
2382   game_panel_controls[GAME_PANEL_PENGUINS].value =
2383     local_player->friends_still_needed;
2384
2385   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2386     local_player->sokobanfields_still_needed;
2387   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2388     local_player->sokobanfields_still_needed;
2389
2390   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2391     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2392
2393   for (i = 0; i < NUM_BELTS; i++)
2394   {
2395     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2396       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2397        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2398     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2399       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2400   }
2401
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2403     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2404   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2405     game.magic_wall_time_left;
2406
2407 #if USE_PLAYER_GRAVITY
2408   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2409     local_player->gravity;
2410 #else
2411   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2412 #endif
2413
2414   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2415     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2416
2417   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2418     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2419       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2420        game.panel.element[i].id : EL_UNDEFINED);
2421
2422   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2423     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2424       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2425        element_info[game.panel.element_count[i].id].element_count : 0);
2426
2427   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2428     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2429       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2430        element_info[game.panel.ce_score[i].id].collect_score : 0);
2431
2432   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2433     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2434       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2435        element_info[game.panel.ce_score_element[i].id].collect_score :
2436        EL_UNDEFINED);
2437
2438   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2439   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2440   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2441
2442   /* update game panel control frames */
2443
2444   for (i = 0; game_panel_controls[i].nr != -1; i++)
2445   {
2446     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2447
2448     if (gpc->type == TYPE_ELEMENT)
2449     {
2450       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2451       {
2452         int last_anim_random_frame = gfx.anim_random_frame;
2453         int element = gpc->value;
2454         int graphic = el2panelimg(element);
2455
2456         if (gpc->value != gpc->last_value)
2457         {
2458           gpc->gfx_frame = 0;
2459           gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461         else
2462         {
2463           gpc->gfx_frame++;
2464
2465           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2466               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2467             gpc->gfx_random = INIT_GFX_RANDOM();
2468         }
2469
2470         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471           gfx.anim_random_frame = gpc->gfx_random;
2472
2473         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2474           gpc->gfx_frame = element_info[element].collect_score;
2475
2476         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2477                                               gpc->gfx_frame);
2478
2479         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2480           gfx.anim_random_frame = last_anim_random_frame;
2481       }
2482     }
2483   }
2484 }
2485
2486 void DisplayGameControlValues()
2487 {
2488   boolean redraw_panel = FALSE;
2489   int i;
2490
2491   for (i = 0; game_panel_controls[i].nr != -1; i++)
2492   {
2493     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2494
2495     if (PANEL_DEACTIVATED(gpc->pos))
2496       continue;
2497
2498     if (gpc->value == gpc->last_value &&
2499         gpc->frame == gpc->last_frame)
2500       continue;
2501
2502     redraw_panel = TRUE;
2503   }
2504
2505   if (!redraw_panel)
2506     return;
2507
2508   /* copy default game door content to main double buffer */
2509 #if 1
2510   /* !!! CHECK AGAIN !!! */
2511   SetPanelBackground();
2512   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2513   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2514 #else
2515   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2516              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2517 #endif
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758   int font_nr = pos->font;
2759   int font_width = getFontWidth(font_nr);
2760   int chars = pos->size;
2761
2762 #if 1
2763   return;       /* !!! USE NEW STUFF !!! */
2764 #endif
2765
2766   if (PANEL_DEACTIVATED(pos))
2767     return;
2768
2769   pos->width = chars * font_width;
2770
2771   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2772 }
2773
2774 void DrawGameValue_Dynamite(int value)
2775 {
2776   struct TextPosInfo *pos = &game.panel.inventory_count;
2777   int font_nr = pos->font;
2778   int font_width = getFontWidth(font_nr);
2779   int chars = pos->size;
2780
2781 #if 1
2782   return;       /* !!! USE NEW STUFF !!! */
2783 #endif
2784
2785   if (PANEL_DEACTIVATED(pos))
2786     return;
2787
2788   pos->width = chars * font_width;
2789
2790   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2791 }
2792
2793 void DrawGameValue_Score(int value)
2794 {
2795   struct TextPosInfo *pos = &game.panel.score;
2796   int font_nr = pos->font;
2797   int font_width = getFontWidth(font_nr);
2798   int chars = pos->size;
2799
2800 #if 1
2801   return;       /* !!! USE NEW STUFF !!! */
2802 #endif
2803
2804   if (PANEL_DEACTIVATED(pos))
2805     return;
2806
2807   pos->width = chars * font_width;
2808
2809   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2810 }
2811
2812 void DrawGameValue_Time(int value)
2813 {
2814   struct TextPosInfo *pos = &game.panel.time;
2815   static int last_value = -1;
2816   int chars1 = 3;
2817   int chars2 = 4;
2818   int chars = pos->size;
2819   int font1_nr = pos->font;
2820   int font2_nr = pos->font_alt;
2821   int font_nr = font1_nr;
2822   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2823
2824 #if 1
2825   return;       /* !!! USE NEW STUFF !!! */
2826 #endif
2827
2828   if (PANEL_DEACTIVATED(pos))
2829     return;
2830
2831   if (use_dynamic_chars)                /* use dynamic number of chars */
2832   {
2833     chars   = (value < 1000 ? chars1   : chars2);
2834     font_nr = (value < 1000 ? font1_nr : font2_nr);
2835   }
2836
2837   /* clear background if value just changed its size (dynamic chars only) */
2838   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2839   {
2840     int width1 = chars1 * getFontWidth(font1_nr);
2841     int width2 = chars2 * getFontWidth(font2_nr);
2842     int max_width = MAX(width1, width2);
2843     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2844
2845     pos->width = max_width;
2846
2847     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2848                                max_width, max_height);
2849   }
2850
2851   pos->width = chars * getFontWidth(font_nr);
2852
2853   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2854
2855   last_value = value;
2856 }
2857
2858 void DrawGameValue_Level(int value)
2859 {
2860   struct TextPosInfo *pos = &game.panel.level_number;
2861   int chars1 = 2;
2862   int chars2 = 3;
2863   int chars = pos->size;
2864   int font1_nr = pos->font;
2865   int font2_nr = pos->font_alt;
2866   int font_nr = font1_nr;
2867   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2868
2869 #if 1
2870   return;       /* !!! USE NEW STUFF !!! */
2871 #endif
2872
2873   if (PANEL_DEACTIVATED(pos))
2874     return;
2875
2876   if (use_dynamic_chars)                /* use dynamic number of chars */
2877   {
2878     chars   = (level_nr < 100 ? chars1   : chars2);
2879     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2880   }
2881
2882   pos->width = chars * getFontWidth(font_nr);
2883
2884   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2885 }
2886
2887 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2888 {
2889   int i;
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   for (i = 0; i < MAX_NUM_KEYS; i++)
2896   {
2897     struct TextPosInfo *pos = &game.panel.key[i];
2898     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2899     int src_y = DOOR_GFX_PAGEY1 + 123;
2900     int dst_x = PANEL_XPOS(pos);
2901     int dst_y = PANEL_YPOS(pos);
2902
2903     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2904                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2905                    EL_KEY_1) + i;
2906     int graphic = el2edimg(element);
2907
2908     if (PANEL_DEACTIVATED(pos))
2909       continue;
2910
2911 #if 0
2912     /* masked blit with tiles from half-size scaled bitmap does not work yet
2913        (no mask bitmap created for these sizes after loading and scaling) --
2914        solution: load without creating mask, scale, then create final mask */
2915
2916     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2917                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2918
2919     if (key[i])
2920     {
2921       Bitmap *src_bitmap;
2922       int src_x, src_y;
2923
2924       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2925
2926       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2927                     dst_x - src_x, dst_y - src_y);
2928       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2929                        dst_x, dst_y);
2930     }
2931 #else
2932     if (key[i])
2933       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2934     else
2935       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2936                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2937 #endif
2938   }
2939 }
2940
2941 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2942                        int key_bits)
2943 {
2944   int key[MAX_NUM_KEYS];
2945   int i;
2946
2947   /* prevent EM engine from updating time/score values parallel to GameWon() */
2948   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2949       local_player->LevelSolved)
2950     return;
2951
2952   for (i = 0; i < MAX_NUM_KEYS; i++)
2953     key[i] = key_bits & (1 << i);
2954
2955   DrawGameValue_Level(level_nr);
2956
2957   DrawGameValue_Emeralds(emeralds);
2958   DrawGameValue_Dynamite(dynamite);
2959   DrawGameValue_Score(score);
2960   DrawGameValue_Time(time);
2961
2962   DrawGameValue_Keys(key);
2963 }
2964
2965 void UpdateGameDoorValues()
2966 {
2967   UpdateGameControlValues();
2968 }
2969
2970 void DrawGameDoorValues()
2971 {
2972   DisplayGameControlValues();
2973 }
2974
2975 void DrawGameDoorValues_OLD()
2976 {
2977   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2978   int dynamite_value = 0;
2979   int score_value = (local_player->LevelSolved ? local_player->score_final :
2980                      local_player->score);
2981   int gems_value = local_player->gems_still_needed;
2982   int key_bits = 0;
2983   int i, j;
2984
2985   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2986   {
2987     DrawGameDoorValues_EM();
2988
2989     return;
2990   }
2991
2992   if (game.centered_player_nr == -1)
2993   {
2994     for (i = 0; i < MAX_PLAYERS; i++)
2995     {
2996       for (j = 0; j < MAX_NUM_KEYS; j++)
2997         if (stored_player[i].key[j])
2998           key_bits |= (1 << j);
2999
3000       dynamite_value += stored_player[i].inventory_size;
3001     }
3002   }
3003   else
3004   {
3005     int player_nr = game.centered_player_nr;
3006
3007     for (i = 0; i < MAX_NUM_KEYS; i++)
3008       if (stored_player[player_nr].key[i])
3009         key_bits |= (1 << i);
3010
3011     dynamite_value = stored_player[player_nr].inventory_size;
3012   }
3013
3014   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3015                     key_bits);
3016 }
3017
3018
3019 /*
3020   =============================================================================
3021   InitGameEngine()
3022   -----------------------------------------------------------------------------
3023   initialize game engine due to level / tape version number
3024   =============================================================================
3025 */
3026
3027 static void InitGameEngine()
3028 {
3029   int i, j, k, l, x, y;
3030
3031   /* set game engine from tape file when re-playing, else from level file */
3032   game.engine_version = (tape.playing ? tape.engine_version :
3033                          level.game_version);
3034
3035   /* ---------------------------------------------------------------------- */
3036   /* set flags for bugs and changes according to active game engine version */
3037   /* ---------------------------------------------------------------------- */
3038
3039   /*
3040     Summary of bugfix/change:
3041     Fixed handling for custom elements that change when pushed by the player.
3042
3043     Fixed/changed in version:
3044     3.1.0
3045
3046     Description:
3047     Before 3.1.0, custom elements that "change when pushing" changed directly
3048     after the player started pushing them (until then handled in "DigField()").
3049     Since 3.1.0, these custom elements are not changed until the "pushing"
3050     move of the element is finished (now handled in "ContinueMoving()").
3051
3052     Affected levels/tapes:
3053     The first condition is generally needed for all levels/tapes before version
3054     3.1.0, which might use the old behaviour before it was changed; known tapes
3055     that are affected are some tapes from the level set "Walpurgis Gardens" by
3056     Jamie Cullen.
3057     The second condition is an exception from the above case and is needed for
3058     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3059     above (including some development versions of 3.1.0), but before it was
3060     known that this change would break tapes like the above and was fixed in
3061     3.1.1, so that the changed behaviour was active although the engine version
3062     while recording maybe was before 3.1.0. There is at least one tape that is
3063     affected by this exception, which is the tape for the one-level set "Bug
3064     Machine" by Juergen Bonhagen.
3065   */
3066
3067   game.use_change_when_pushing_bug =
3068     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3069      !(tape.playing &&
3070        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3071        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3072
3073   /*
3074     Summary of bugfix/change:
3075     Fixed handling for blocking the field the player leaves when moving.
3076
3077     Fixed/changed in version:
3078     3.1.1
3079
3080     Description:
3081     Before 3.1.1, when "block last field when moving" was enabled, the field
3082     the player is leaving when moving was blocked for the time of the move,
3083     and was directly unblocked afterwards. This resulted in the last field
3084     being blocked for exactly one less than the number of frames of one player
3085     move. Additionally, even when blocking was disabled, the last field was
3086     blocked for exactly one frame.
3087     Since 3.1.1, due to changes in player movement handling, the last field
3088     is not blocked at all when blocking is disabled. When blocking is enabled,
3089     the last field is blocked for exactly the number of frames of one player
3090     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3091     last field is blocked for exactly one more than the number of frames of
3092     one player move.
3093
3094     Affected levels/tapes:
3095     (!!! yet to be determined -- probably many !!!)
3096   */
3097
3098   game.use_block_last_field_bug =
3099     (game.engine_version < VERSION_IDENT(3,1,1,0));
3100
3101   /*
3102     Summary of bugfix/change:
3103     Changed behaviour of CE changes with multiple changes per single frame.
3104
3105     Fixed/changed in version:
3106     3.2.0-6
3107
3108     Description:
3109     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3110     This resulted in race conditions where CEs seem to behave strange in some
3111     situations (where triggered CE changes were just skipped because there was
3112     already a CE change on that tile in the playfield in that engine frame).
3113     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3114     (The number of changes per frame must be limited in any case, because else
3115     it is easily possible to define CE changes that would result in an infinite
3116     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3117     should be set large enough so that it would only be reached in cases where
3118     the corresponding CE change conditions run into a loop. Therefore, it seems
3119     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3120     maximal number of change pages for custom elements.)
3121
3122     Affected levels/tapes:
3123     Probably many.
3124   */
3125
3126 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3127   game.max_num_changes_per_frame = 1;
3128 #else
3129   game.max_num_changes_per_frame =
3130     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3131 #endif
3132
3133   /* ---------------------------------------------------------------------- */
3134
3135   /* default scan direction: scan playfield from top/left to bottom/right */
3136   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3137
3138   /* dynamically adjust element properties according to game engine version */
3139   InitElementPropertiesEngine(game.engine_version);
3140
3141 #if 0
3142   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3143   printf("          tape version == %06d [%s] [file: %06d]\n",
3144          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3145          tape.file_version);
3146   printf("       => game.engine_version == %06d\n", game.engine_version);
3147 #endif
3148
3149   /* ---------- initialize player's initial move delay --------------------- */
3150
3151   /* dynamically adjust player properties according to level information */
3152   for (i = 0; i < MAX_PLAYERS; i++)
3153     game.initial_move_delay_value[i] =
3154       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3155
3156   /* dynamically adjust player properties according to game engine version */
3157   for (i = 0; i < MAX_PLAYERS; i++)
3158     game.initial_move_delay[i] =
3159       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3160        game.initial_move_delay_value[i] : 0);
3161
3162   /* ---------- initialize player's initial push delay --------------------- */
3163
3164   /* dynamically adjust player properties according to game engine version */
3165   game.initial_push_delay_value =
3166     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3167
3168   /* ---------- initialize changing elements ------------------------------- */
3169
3170   /* initialize changing elements information */
3171   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3172   {
3173     struct ElementInfo *ei = &element_info[i];
3174
3175     /* this pointer might have been changed in the level editor */
3176     ei->change = &ei->change_page[0];
3177
3178     if (!IS_CUSTOM_ELEMENT(i))
3179     {
3180       ei->change->target_element = EL_EMPTY_SPACE;
3181       ei->change->delay_fixed = 0;
3182       ei->change->delay_random = 0;
3183       ei->change->delay_frames = 1;
3184     }
3185
3186     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3187     {
3188       ei->has_change_event[j] = FALSE;
3189
3190       ei->event_page_nr[j] = 0;
3191       ei->event_page[j] = &ei->change_page[0];
3192     }
3193   }
3194
3195   /* add changing elements from pre-defined list */
3196   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3197   {
3198     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3199     struct ElementInfo *ei = &element_info[ch_delay->element];
3200
3201     ei->change->target_element       = ch_delay->target_element;
3202     ei->change->delay_fixed          = ch_delay->change_delay;
3203
3204     ei->change->pre_change_function  = ch_delay->pre_change_function;
3205     ei->change->change_function      = ch_delay->change_function;
3206     ei->change->post_change_function = ch_delay->post_change_function;
3207
3208     ei->change->can_change = TRUE;
3209     ei->change->can_change_or_has_action = TRUE;
3210
3211     ei->has_change_event[CE_DELAY] = TRUE;
3212
3213     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3214     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3215   }
3216
3217   /* ---------- initialize internal run-time variables --------------------- */
3218
3219   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3220   {
3221     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3222
3223     for (j = 0; j < ei->num_change_pages; j++)
3224     {
3225       ei->change_page[j].can_change_or_has_action =
3226         (ei->change_page[j].can_change |
3227          ei->change_page[j].has_action);
3228     }
3229   }
3230
3231   /* add change events from custom element configuration */
3232   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3233   {
3234     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3235
3236     for (j = 0; j < ei->num_change_pages; j++)
3237     {
3238       if (!ei->change_page[j].can_change_or_has_action)
3239         continue;
3240
3241       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3242       {
3243         /* only add event page for the first page found with this event */
3244         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3245         {
3246           ei->has_change_event[k] = TRUE;
3247
3248           ei->event_page_nr[k] = j;
3249           ei->event_page[k] = &ei->change_page[j];
3250         }
3251       }
3252     }
3253   }
3254
3255 #if 1
3256   /* ---------- initialize reference elements in change conditions --------- */
3257
3258   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3259   {
3260     int element = EL_CUSTOM_START + i;
3261     struct ElementInfo *ei = &element_info[element];
3262
3263     for (j = 0; j < ei->num_change_pages; j++)
3264     {
3265       int trigger_element = ei->change_page[j].initial_trigger_element;
3266
3267       if (trigger_element >= EL_PREV_CE_8 &&
3268           trigger_element <= EL_NEXT_CE_8)
3269         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3270
3271       ei->change_page[j].trigger_element = trigger_element;
3272     }
3273   }
3274 #endif
3275
3276   /* ---------- initialize run-time trigger player and element ------------- */
3277
3278   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3279   {
3280     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3281
3282     for (j = 0; j < ei->num_change_pages; j++)
3283     {
3284       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3285       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3286       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3287       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3288       ei->change_page[j].actual_trigger_ce_value = 0;
3289       ei->change_page[j].actual_trigger_ce_score = 0;
3290     }
3291   }
3292
3293   /* ---------- initialize trigger events ---------------------------------- */
3294
3295   /* initialize trigger events information */
3296   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3298       trigger_events[i][j] = FALSE;
3299
3300   /* add trigger events from element change event properties */
3301   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3302   {
3303     struct ElementInfo *ei = &element_info[i];
3304
3305     for (j = 0; j < ei->num_change_pages; j++)
3306     {
3307       if (!ei->change_page[j].can_change_or_has_action)
3308         continue;
3309
3310       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3311       {
3312         int trigger_element = ei->change_page[j].trigger_element;
3313
3314         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3315         {
3316           if (ei->change_page[j].has_event[k])
3317           {
3318             if (IS_GROUP_ELEMENT(trigger_element))
3319             {
3320               struct ElementGroupInfo *group =
3321                 element_info[trigger_element].group;
3322
3323               for (l = 0; l < group->num_elements_resolved; l++)
3324                 trigger_events[group->element_resolved[l]][k] = TRUE;
3325             }
3326             else if (trigger_element == EL_ANY_ELEMENT)
3327               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3328                 trigger_events[l][k] = TRUE;
3329             else
3330               trigger_events[trigger_element][k] = TRUE;
3331           }
3332         }
3333       }
3334     }
3335   }
3336
3337   /* ---------- initialize push delay -------------------------------------- */
3338
3339   /* initialize push delay values to default */
3340   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341   {
3342     if (!IS_CUSTOM_ELEMENT(i))
3343     {
3344       /* set default push delay values (corrected since version 3.0.7-1) */
3345       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3346       {
3347         element_info[i].push_delay_fixed = 2;
3348         element_info[i].push_delay_random = 8;
3349       }
3350       else
3351       {
3352         element_info[i].push_delay_fixed = 8;
3353         element_info[i].push_delay_random = 8;
3354       }
3355     }
3356   }
3357
3358   /* set push delay value for certain elements from pre-defined list */
3359   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3360   {
3361     int e = push_delay_list[i].element;
3362
3363     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3364     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3365   }
3366
3367   /* set push delay value for Supaplex elements for newer engine versions */
3368   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3369   {
3370     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371     {
3372       if (IS_SP_ELEMENT(i))
3373       {
3374         /* set SP push delay to just enough to push under a falling zonk */
3375         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3376
3377         element_info[i].push_delay_fixed  = delay;
3378         element_info[i].push_delay_random = 0;
3379       }
3380     }
3381   }
3382
3383   /* ---------- initialize move stepsize ----------------------------------- */
3384
3385   /* initialize move stepsize values to default */
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387     if (!IS_CUSTOM_ELEMENT(i))
3388       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3389
3390   /* set move stepsize value for certain elements from pre-defined list */
3391   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3392   {
3393     int e = move_stepsize_list[i].element;
3394
3395     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3396   }
3397
3398   /* ---------- initialize collect score ----------------------------------- */
3399
3400   /* initialize collect score values for custom elements from initial value */
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402     if (IS_CUSTOM_ELEMENT(i))
3403       element_info[i].collect_score = element_info[i].collect_score_initial;
3404
3405   /* ---------- initialize collect count ----------------------------------- */
3406
3407   /* initialize collect count values for non-custom elements */
3408   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3409     if (!IS_CUSTOM_ELEMENT(i))
3410       element_info[i].collect_count_initial = 0;
3411
3412   /* add collect count values for all elements from pre-defined list */
3413   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3414     element_info[collect_count_list[i].element].collect_count_initial =
3415       collect_count_list[i].count;
3416
3417   /* ---------- initialize access direction -------------------------------- */
3418
3419   /* initialize access direction values to default (access from every side) */
3420   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     if (!IS_CUSTOM_ELEMENT(i))
3422       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3423
3424   /* set access direction value for certain elements from pre-defined list */
3425   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3426     element_info[access_direction_list[i].element].access_direction =
3427       access_direction_list[i].direction;
3428
3429   /* ---------- initialize explosion content ------------------------------- */
3430   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3431   {
3432     if (IS_CUSTOM_ELEMENT(i))
3433       continue;
3434
3435     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3436     {
3437       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3438
3439       element_info[i].content.e[x][y] =
3440         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3441          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3442          i == EL_PLAYER_3 ? EL_EMERALD :
3443          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3444          i == EL_MOLE ? EL_EMERALD_RED :
3445          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3446          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3447          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3448          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3449          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3450          i == EL_WALL_EMERALD ? EL_EMERALD :
3451          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3452          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3453          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3454          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3455          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3456          i == EL_WALL_PEARL ? EL_PEARL :
3457          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3458          EL_EMPTY);
3459     }
3460   }
3461
3462   /* ---------- initialize recursion detection ------------------------------ */
3463   recursion_loop_depth = 0;
3464   recursion_loop_detected = FALSE;
3465   recursion_loop_element = EL_UNDEFINED;
3466
3467   /* ---------- initialize graphics engine ---------------------------------- */
3468   game.scroll_delay_value =
3469     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3470      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3471   game.scroll_delay_value =
3472     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3473 }
3474
3475 int get_num_special_action(int element, int action_first, int action_last)
3476 {
3477   int num_special_action = 0;
3478   int i, j;
3479
3480   for (i = action_first; i <= action_last; i++)
3481   {
3482     boolean found = FALSE;
3483
3484     for (j = 0; j < NUM_DIRECTIONS; j++)
3485       if (el_act_dir2img(element, i, j) !=
3486           el_act_dir2img(element, ACTION_DEFAULT, j))
3487         found = TRUE;
3488
3489     if (found)
3490       num_special_action++;
3491     else
3492       break;
3493   }
3494
3495   return num_special_action;
3496 }
3497
3498
3499 /*
3500   =============================================================================
3501   InitGame()
3502   -----------------------------------------------------------------------------
3503   initialize and start new game
3504   =============================================================================
3505 */
3506
3507 void InitGame()
3508 {
3509   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3510   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3511   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3512 #if 0
3513   boolean do_fading = (game_status == GAME_MODE_MAIN);
3514 #endif
3515 #if 1
3516   int initial_move_dir = MV_DOWN;
3517 #else
3518   int initial_move_dir = MV_NONE;
3519 #endif
3520   int i, j, x, y;
3521
3522   game_status = GAME_MODE_PLAYING;
3523
3524 #if 1
3525   /* needed if different viewport properties defined for playing */
3526   ChangeViewportPropertiesIfNeeded();
3527 #endif
3528
3529 #if 1
3530   DrawCompleteVideoDisplay();
3531 #endif
3532
3533   InitGameEngine();
3534   InitGameControlValues();
3535
3536   /* don't play tapes over network */
3537   network_playing = (options.network && !tape.playing);
3538
3539   for (i = 0; i < MAX_PLAYERS; i++)
3540   {
3541     struct PlayerInfo *player = &stored_player[i];
3542
3543     player->index_nr = i;
3544     player->index_bit = (1 << i);
3545     player->element_nr = EL_PLAYER_1 + i;
3546
3547     player->present = FALSE;
3548     player->active = FALSE;
3549     player->mapped = FALSE;
3550
3551     player->killed = FALSE;
3552     player->reanimated = FALSE;
3553
3554     player->action = 0;
3555     player->effective_action = 0;
3556     player->programmed_action = 0;
3557
3558     player->score = 0;
3559     player->score_final = 0;
3560
3561     player->gems_still_needed = level.gems_needed;
3562     player->sokobanfields_still_needed = 0;
3563     player->lights_still_needed = 0;
3564     player->friends_still_needed = 0;
3565
3566     for (j = 0; j < MAX_NUM_KEYS; j++)
3567       player->key[j] = FALSE;
3568
3569     player->num_white_keys = 0;
3570
3571     player->dynabomb_count = 0;
3572     player->dynabomb_size = 1;
3573     player->dynabombs_left = 0;
3574     player->dynabomb_xl = FALSE;
3575
3576     player->MovDir = initial_move_dir;
3577     player->MovPos = 0;
3578     player->GfxPos = 0;
3579     player->GfxDir = initial_move_dir;
3580     player->GfxAction = ACTION_DEFAULT;
3581     player->Frame = 0;
3582     player->StepFrame = 0;
3583
3584     player->initial_element = player->element_nr;
3585     player->artwork_element =
3586       (level.use_artwork_element[i] ? level.artwork_element[i] :
3587        player->element_nr);
3588     player->use_murphy = FALSE;
3589
3590     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3591     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3592
3593     player->gravity = level.initial_player_gravity[i];
3594
3595     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3596
3597     player->actual_frame_counter = 0;
3598
3599     player->step_counter = 0;
3600
3601     player->last_move_dir = initial_move_dir;
3602
3603     player->is_active = FALSE;
3604
3605     player->is_waiting = FALSE;
3606     player->is_moving = FALSE;
3607     player->is_auto_moving = FALSE;
3608     player->is_digging = FALSE;
3609     player->is_snapping = FALSE;
3610     player->is_collecting = FALSE;
3611     player->is_pushing = FALSE;
3612     player->is_switching = FALSE;
3613     player->is_dropping = FALSE;
3614     player->is_dropping_pressed = FALSE;
3615
3616     player->is_bored = FALSE;
3617     player->is_sleeping = FALSE;
3618
3619     player->frame_counter_bored = -1;
3620     player->frame_counter_sleeping = -1;
3621
3622     player->anim_delay_counter = 0;
3623     player->post_delay_counter = 0;
3624
3625     player->dir_waiting = initial_move_dir;
3626     player->action_waiting = ACTION_DEFAULT;
3627     player->last_action_waiting = ACTION_DEFAULT;
3628     player->special_action_bored = ACTION_DEFAULT;
3629     player->special_action_sleeping = ACTION_DEFAULT;
3630
3631     player->switch_x = -1;
3632     player->switch_y = -1;
3633
3634     player->drop_x = -1;
3635     player->drop_y = -1;
3636
3637     player->show_envelope = 0;
3638
3639     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3640
3641     player->push_delay       = -1;      /* initialized when pushing starts */
3642     player->push_delay_value = game.initial_push_delay_value;
3643
3644     player->drop_delay = 0;
3645     player->drop_pressed_delay = 0;
3646
3647     player->last_jx = -1;
3648     player->last_jy = -1;
3649     player->jx = -1;
3650     player->jy = -1;
3651
3652     player->shield_normal_time_left = 0;
3653     player->shield_deadly_time_left = 0;
3654
3655     player->inventory_infinite_element = EL_UNDEFINED;
3656     player->inventory_size = 0;
3657
3658     if (level.use_initial_inventory[i])
3659     {
3660       for (j = 0; j < level.initial_inventory_size[i]; j++)
3661       {
3662         int element = level.initial_inventory_content[i][j];
3663         int collect_count = element_info[element].collect_count_initial;
3664         int k;
3665
3666         if (!IS_CUSTOM_ELEMENT(element))
3667           collect_count = 1;
3668
3669         if (collect_count == 0)
3670           player->inventory_infinite_element = element;
3671         else
3672           for (k = 0; k < collect_count; k++)
3673             if (player->inventory_size < MAX_INVENTORY_SIZE)
3674               player->inventory_element[player->inventory_size++] = element;
3675       }
3676     }
3677
3678     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3679     SnapField(player, 0, 0);
3680
3681     player->LevelSolved = FALSE;
3682     player->GameOver = FALSE;
3683
3684     player->LevelSolved_GameWon = FALSE;
3685     player->LevelSolved_GameEnd = FALSE;
3686     player->LevelSolved_PanelOff = FALSE;
3687     player->LevelSolved_SaveTape = FALSE;
3688     player->LevelSolved_SaveScore = FALSE;
3689     player->LevelSolved_CountingTime = 0;
3690     player->LevelSolved_CountingScore = 0;
3691
3692     map_player_action[i] = i;
3693   }
3694
3695   network_player_action_received = FALSE;
3696
3697 #if defined(NETWORK_AVALIABLE)
3698   /* initial null action */
3699   if (network_playing)
3700     SendToServer_MovePlayer(MV_NONE);
3701 #endif
3702
3703   ZX = ZY = -1;
3704   ExitX = ExitY = -1;
3705
3706   FrameCounter = 0;
3707   TimeFrames = 0;
3708   TimePlayed = 0;
3709   TimeLeft = level.time;
3710   TapeTime = 0;
3711
3712   ScreenMovDir = MV_NONE;
3713   ScreenMovPos = 0;
3714   ScreenGfxPos = 0;
3715
3716   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3717
3718   AllPlayersGone = FALSE;
3719
3720   game.no_time_limit = (level.time == 0);
3721
3722   game.yamyam_content_nr = 0;
3723   game.robot_wheel_active = FALSE;
3724   game.magic_wall_active = FALSE;
3725   game.magic_wall_time_left = 0;
3726   game.light_time_left = 0;
3727   game.timegate_time_left = 0;
3728   game.switchgate_pos = 0;
3729   game.wind_direction = level.wind_direction_initial;
3730
3731 #if !USE_PLAYER_GRAVITY
3732   game.gravity = FALSE;
3733   game.explosions_delayed = TRUE;
3734 #endif
3735
3736   game.lenses_time_left = 0;
3737   game.magnify_time_left = 0;
3738
3739   game.ball_state = level.ball_state_initial;
3740   game.ball_content_nr = 0;
3741
3742   game.envelope_active = FALSE;
3743
3744   /* set focus to local player for network games, else to all players */
3745   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3746   game.centered_player_nr_next = game.centered_player_nr;
3747   game.set_centered_player = FALSE;
3748
3749   if (network_playing && tape.recording)
3750   {
3751     /* store client dependent player focus when recording network games */
3752     tape.centered_player_nr_next = game.centered_player_nr_next;
3753     tape.set_centered_player = TRUE;
3754   }
3755
3756   for (i = 0; i < NUM_BELTS; i++)
3757   {
3758     game.belt_dir[i] = MV_NONE;
3759     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3760   }
3761
3762   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3763     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3764
3765 #if DEBUG_INIT_PLAYER
3766   if (options.debug)
3767   {
3768     printf("Player status at level initialization:\n");
3769   }
3770 #endif
3771
3772   SCAN_PLAYFIELD(x, y)
3773   {
3774     Feld[x][y] = level.field[x][y];
3775     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3776     ChangeDelay[x][y] = 0;
3777     ChangePage[x][y] = -1;
3778 #if USE_NEW_CUSTOM_VALUE
3779     CustomValue[x][y] = 0;              /* initialized in InitField() */
3780 #endif
3781     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3782     AmoebaNr[x][y] = 0;
3783     WasJustMoving[x][y] = 0;
3784     WasJustFalling[x][y] = 0;
3785     CheckCollision[x][y] = 0;
3786     CheckImpact[x][y] = 0;
3787     Stop[x][y] = FALSE;
3788     Pushed[x][y] = FALSE;
3789
3790     ChangeCount[x][y] = 0;
3791     ChangeEvent[x][y] = -1;
3792
3793     ExplodePhase[x][y] = 0;
3794     ExplodeDelay[x][y] = 0;
3795     ExplodeField[x][y] = EX_TYPE_NONE;
3796
3797     RunnerVisit[x][y] = 0;
3798     PlayerVisit[x][y] = 0;
3799
3800     GfxFrame[x][y] = 0;
3801     GfxRandom[x][y] = INIT_GFX_RANDOM();
3802     GfxElement[x][y] = EL_UNDEFINED;
3803     GfxAction[x][y] = ACTION_DEFAULT;
3804     GfxDir[x][y] = MV_NONE;
3805     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3806   }
3807
3808   SCAN_PLAYFIELD(x, y)
3809   {
3810     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3811       emulate_bd = FALSE;
3812     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3813       emulate_sb = FALSE;
3814     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3815       emulate_sp = FALSE;
3816
3817     InitField(x, y, TRUE);
3818
3819     ResetGfxAnimation(x, y);
3820   }
3821
3822   InitBeltMovement();
3823
3824   for (i = 0; i < MAX_PLAYERS; i++)
3825   {
3826     struct PlayerInfo *player = &stored_player[i];
3827
3828     /* set number of special actions for bored and sleeping animation */
3829     player->num_special_action_bored =
3830       get_num_special_action(player->artwork_element,
3831                              ACTION_BORING_1, ACTION_BORING_LAST);
3832     player->num_special_action_sleeping =
3833       get_num_special_action(player->artwork_element,
3834                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3835   }
3836
3837   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3838                     emulate_sb ? EMU_SOKOBAN :
3839                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3840
3841 #if USE_NEW_ALL_SLIPPERY
3842   /* initialize type of slippery elements */
3843   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3844   {
3845     if (!IS_CUSTOM_ELEMENT(i))
3846     {
3847       /* default: elements slip down either to the left or right randomly */
3848       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3849
3850       /* SP style elements prefer to slip down on the left side */
3851       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3852         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3853
3854       /* BD style elements prefer to slip down on the left side */
3855       if (game.emulation == EMU_BOULDERDASH)
3856         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3857     }
3858   }
3859 #endif
3860
3861   /* initialize explosion and ignition delay */
3862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3863   {
3864     if (!IS_CUSTOM_ELEMENT(i))
3865     {
3866       int num_phase = 8;
3867       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3868                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3869                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3870       int last_phase = (num_phase + 1) * delay;
3871       int half_phase = (num_phase / 2) * delay;
3872
3873       element_info[i].explosion_delay = last_phase - 1;
3874       element_info[i].ignition_delay = half_phase;
3875
3876       if (i == EL_BLACK_ORB)
3877         element_info[i].ignition_delay = 1;
3878     }
3879
3880 #if 0
3881     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3882       element_info[i].explosion_delay = 1;
3883
3884     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3885       element_info[i].ignition_delay = 1;
3886 #endif
3887   }
3888
3889   /* correct non-moving belts to start moving left */
3890   for (i = 0; i < NUM_BELTS; i++)
3891     if (game.belt_dir[i] == MV_NONE)
3892       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3893
3894 #if USE_NEW_PLAYER_ASSIGNMENTS
3895   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3896   /* choose default local player */
3897   local_player = &stored_player[0];
3898
3899   for (i = 0; i < MAX_PLAYERS; i++)
3900     stored_player[i].connected = FALSE;
3901
3902   local_player->connected = TRUE;
3903   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3904
3905   TEST_game_team_mode = setup.team_mode;
3906
3907   if (tape.playing)
3908   {
3909 #if 1
3910     int num_players = 0;
3911
3912     for (i = 0; i < MAX_PLAYERS; i++)
3913       if (tape.player_participates[i])
3914         num_players++;
3915
3916     TEST_game_team_mode = (num_players > 1);
3917
3918     printf("::: TAPE TEAM MODE: %s (%d)\n",
3919            (TEST_game_team_mode ? "true" : "false"), num_players);
3920 #endif
3921
3922 #if 1
3923     for (i = 0; i < MAX_PLAYERS; i++)
3924       stored_player[i].connected = tape.player_participates[i];
3925 #else
3926     /* try to guess locally connected team mode players (needed for correct
3927        assignment of player figures from level to locally playing players) */
3928
3929     for (i = 0; i < MAX_PLAYERS; i++)
3930       if (tape.player_participates[i])
3931         stored_player[i].connected = TRUE;
3932 #endif
3933   }
3934   else if (setup.team_mode && !options.network)
3935   {
3936     /* try to guess locally connected team mode players (needed for correct
3937        assignment of player figures from level to locally playing players) */
3938
3939     for (i = 0; i < MAX_PLAYERS; i++)
3940       if (setup.input[i].use_joystick ||
3941           setup.input[i].key.left != KSYM_UNDEFINED)
3942         stored_player[i].connected = TRUE;
3943   }
3944
3945 #if DEBUG_INIT_PLAYER
3946   if (options.debug)
3947   {
3948     printf("Player status after level initialization:\n");
3949
3950     for (i = 0; i < MAX_PLAYERS; i++)
3951     {
3952       struct PlayerInfo *player = &stored_player[i];
3953
3954       printf("- player %d: present == %d, connected == %d, active == %d",
3955              i + 1,
3956              player->present,
3957              player->connected,
3958              player->active);
3959
3960       if (local_player == player)
3961         printf(" (local player)");
3962
3963       printf("\n");
3964     }
3965   }
3966 #endif
3967
3968 #if DEBUG_INIT_PLAYER
3969   if (options.debug)
3970     printf("Reassigning players ...\n");
3971 #endif
3972
3973   /* check if any connected player was not found in playfield */
3974   for (i = 0; i < MAX_PLAYERS; i++)
3975   {
3976     struct PlayerInfo *player = &stored_player[i];
3977
3978     if (player->connected && !player->present)
3979     {
3980       struct PlayerInfo *field_player = NULL;
3981
3982 #if DEBUG_INIT_PLAYER
3983       if (options.debug)
3984         printf("- looking for field player for player %d ...\n", i + 1);
3985 #endif
3986
3987       /* assign first free player found that is present in the playfield */
3988
3989 #if 1
3990       /* first try: look for unmapped playfield player that is not connected */
3991       for (j = 0; j < MAX_PLAYERS; j++)
3992         if (field_player == NULL &&
3993             stored_player[j].present &&
3994             !stored_player[j].mapped &&
3995             !stored_player[j].connected)
3996           field_player = &stored_player[j];
3997
3998       /* second try: look for *any* unmapped playfield player */
3999       for (j = 0; j < MAX_PLAYERS; j++)
4000         if (field_player == NULL &&
4001             stored_player[j].present &&
4002             !stored_player[j].mapped)
4003           field_player = &stored_player[j];
4004 #else
4005       /* first try: look for unmapped playfield player that is not connected */
4006       if (field_player == NULL)
4007         for (j = 0; j < MAX_PLAYERS; j++)
4008           if (stored_player[j].present &&
4009               !stored_player[j].mapped &&
4010               !stored_player[j].connected)
4011             field_player = &stored_player[j];
4012
4013       /* second try: look for *any* unmapped playfield player */
4014       if (field_player == NULL)
4015         for (j = 0; j < MAX_PLAYERS; j++)
4016           if (stored_player[j].present &&
4017               !stored_player[j].mapped)
4018             field_player = &stored_player[j];
4019 #endif
4020
4021       if (field_player != NULL)
4022       {
4023         int jx = field_player->jx, jy = field_player->jy;
4024
4025 #if DEBUG_INIT_PLAYER
4026         if (options.debug)
4027           printf("- found player %d\n", field_player->index_nr + 1);
4028 #endif
4029
4030         player->present = FALSE;
4031         player->active = FALSE;
4032
4033         field_player->present = TRUE;
4034         field_player->active = TRUE;
4035
4036         /*
4037         player->initial_element = field_player->initial_element;
4038         player->artwork_element = field_player->artwork_element;
4039
4040         player->block_last_field       = field_player->block_last_field;
4041         player->block_delay_adjustment = field_player->block_delay_adjustment;
4042         */
4043
4044         StorePlayer[jx][jy] = field_player->element_nr;
4045
4046         field_player->jx = field_player->last_jx = jx;
4047         field_player->jy = field_player->last_jy = jy;
4048
4049         if (local_player == player)
4050           local_player = field_player;
4051
4052         map_player_action[field_player->index_nr] = i;
4053
4054         field_player->mapped = TRUE;
4055
4056 #if DEBUG_INIT_PLAYER
4057         if (options.debug)
4058           printf("- map_player_action[%d] == %d\n",
4059                  field_player->index_nr + 1, i + 1);
4060 #endif
4061       }
4062     }
4063
4064     if (player->connected && player->present)
4065       player->mapped = TRUE;
4066   }
4067
4068 #if DEBUG_INIT_PLAYER
4069   if (options.debug)
4070   {
4071     printf("Player status after player assignment (first stage):\n");
4072
4073     for (i = 0; i < MAX_PLAYERS; i++)
4074     {
4075       struct PlayerInfo *player = &stored_player[i];
4076
4077       printf("- player %d: present == %d, connected == %d, active == %d",
4078              i + 1,
4079              player->present,
4080              player->connected,
4081              player->active);
4082
4083       if (local_player == player)
4084         printf(" (local player)");
4085
4086       printf("\n");
4087     }
4088   }
4089 #endif
4090
4091 #else
4092
4093   /* check if any connected player was not found in playfield */
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       for (j = 0; j < MAX_PLAYERS; j++)
4101       {
4102         struct PlayerInfo *field_player = &stored_player[j];
4103         int jx = field_player->jx, jy = field_player->jy;
4104
4105         /* assign first free player found that is present in the playfield */
4106         if (field_player->present && !field_player->connected)
4107         {
4108           player->present = TRUE;
4109           player->active = TRUE;
4110
4111           field_player->present = FALSE;
4112           field_player->active = FALSE;
4113
4114           player->initial_element = field_player->initial_element;
4115           player->artwork_element = field_player->artwork_element;
4116
4117           player->block_last_field       = field_player->block_last_field;
4118           player->block_delay_adjustment = field_player->block_delay_adjustment;
4119
4120           StorePlayer[jx][jy] = player->element_nr;
4121
4122           player->jx = player->last_jx = jx;
4123           player->jy = player->last_jy = jy;
4124
4125           break;
4126         }
4127       }
4128     }
4129   }
4130 #endif
4131
4132 #if 0
4133   printf("::: local_player->present == %d\n", local_player->present);
4134 #endif
4135
4136   if (tape.playing)
4137   {
4138     /* when playing a tape, eliminate all players who do not participate */
4139
4140 #if USE_NEW_PLAYER_ASSIGNMENTS
4141
4142 #if 1
4143     // if (!setup.team_mode)
4144     if (!TEST_game_team_mode)
4145 #endif
4146
4147     for (i = 0; i < MAX_PLAYERS; i++)
4148     {
4149       if (stored_player[i].active &&
4150           !tape.player_participates[map_player_action[i]])
4151       {
4152         struct PlayerInfo *player = &stored_player[i];
4153         int jx = player->jx, jy = player->jy;
4154
4155 #if DEBUG_INIT_PLAYER
4156         if (options.debug)
4157           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4158 #endif
4159
4160         player->active = FALSE;
4161         StorePlayer[jx][jy] = 0;
4162         Feld[jx][jy] = EL_EMPTY;
4163       }
4164     }
4165 #else
4166     for (i = 0; i < MAX_PLAYERS; i++)
4167     {
4168       if (stored_player[i].active &&
4169           !tape.player_participates[i])
4170       {
4171         struct PlayerInfo *player = &stored_player[i];
4172         int jx = player->jx, jy = player->jy;
4173
4174         player->active = FALSE;
4175         StorePlayer[jx][jy] = 0;
4176         Feld[jx][jy] = EL_EMPTY;
4177       }
4178     }
4179 #endif
4180   }
4181   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4182   {
4183     /* when in single player mode, eliminate all but the first active player */
4184
4185     for (i = 0; i < MAX_PLAYERS; i++)
4186     {
4187       if (stored_player[i].active)
4188       {
4189         for (j = i + 1; j < MAX_PLAYERS; j++)
4190         {
4191           if (stored_player[j].active)
4192           {
4193             struct PlayerInfo *player = &stored_player[j];
4194             int jx = player->jx, jy = player->jy;
4195
4196             player->active = FALSE;
4197             player->present = FALSE;
4198
4199             StorePlayer[jx][jy] = 0;
4200             Feld[jx][jy] = EL_EMPTY;
4201           }
4202         }
4203       }
4204     }
4205   }
4206
4207   /* when recording the game, store which players take part in the game */
4208   if (tape.recording)
4209   {
4210 #if USE_NEW_PLAYER_ASSIGNMENTS
4211     for (i = 0; i < MAX_PLAYERS; i++)
4212       if (stored_player[i].connected)
4213         tape.player_participates[i] = TRUE;
4214 #else
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216       if (stored_player[i].active)
4217         tape.player_participates[i] = TRUE;
4218 #endif
4219   }
4220
4221 #if DEBUG_INIT_PLAYER
4222   if (options.debug)
4223   {
4224     printf("Player status after player assignment (final stage):\n");
4225
4226     for (i = 0; i < MAX_PLAYERS; i++)
4227     {
4228       struct PlayerInfo *player = &stored_player[i];
4229
4230       printf("- player %d: present == %d, connected == %d, active == %d",
4231              i + 1,
4232              player->present,
4233              player->connected,
4234              player->active);
4235
4236       if (local_player == player)
4237         printf(" (local player)");
4238
4239       printf("\n");
4240     }
4241   }
4242 #endif
4243
4244   if (BorderElement == EL_EMPTY)
4245   {
4246     SBX_Left = 0;
4247     SBX_Right = lev_fieldx - SCR_FIELDX;
4248     SBY_Upper = 0;
4249     SBY_Lower = lev_fieldy - SCR_FIELDY;
4250   }
4251   else
4252   {
4253     SBX_Left = -1;
4254     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4255     SBY_Upper = -1;
4256     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4257   }
4258
4259 #if NEW_TILESIZE
4260
4261   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4262     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4263
4264   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4265     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4266
4267   if (EVEN(SCR_FIELDX))
4268     SBX_Left--;
4269   if (EVEN(SCR_FIELDY))
4270     SBY_Upper--;
4271
4272 #else
4273
4274   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4275     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4276
4277   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4278     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4279 #endif
4280
4281   /* if local player not found, look for custom element that might create
4282      the player (make some assumptions about the right custom element) */
4283   if (!local_player->present)
4284   {
4285     int start_x = 0, start_y = 0;
4286     int found_rating = 0;
4287     int found_element = EL_UNDEFINED;
4288     int player_nr = local_player->index_nr;
4289
4290     SCAN_PLAYFIELD(x, y)
4291     {
4292       int element = Feld[x][y];
4293       int content;
4294       int xx, yy;
4295       boolean is_player;
4296
4297       if (level.use_start_element[player_nr] &&
4298           level.start_element[player_nr] == element &&
4299           found_rating < 4)
4300       {
4301         start_x = x;
4302         start_y = y;
4303
4304         found_rating = 4;
4305         found_element = element;
4306       }
4307
4308       if (!IS_CUSTOM_ELEMENT(element))
4309         continue;
4310
4311       if (CAN_CHANGE(element))
4312       {
4313         for (i = 0; i < element_info[element].num_change_pages; i++)
4314         {
4315           /* check for player created from custom element as single target */
4316           content = element_info[element].change_page[i].target_element;
4317           is_player = ELEM_IS_PLAYER(content);
4318
4319           if (is_player && (found_rating < 3 ||
4320                             (found_rating == 3 && element < found_element)))
4321           {
4322             start_x = x;
4323             start_y = y;
4324
4325             found_rating = 3;
4326             found_element = element;
4327           }
4328         }
4329       }
4330
4331       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4332       {
4333         /* check for player created from custom element as explosion content */
4334         content = element_info[element].content.e[xx][yy];
4335         is_player = ELEM_IS_PLAYER(content);
4336
4337         if (is_player && (found_rating < 2 ||
4338                           (found_rating == 2 && element < found_element)))
4339         {
4340           start_x = x + xx - 1;
4341           start_y = y + yy - 1;
4342
4343           found_rating = 2;
4344           found_element = element;
4345         }
4346
4347         if (!CAN_CHANGE(element))
4348           continue;
4349
4350         for (i = 0; i < element_info[element].num_change_pages; i++)
4351         {
4352           /* check for player created from custom element as extended target */
4353           content =
4354             element_info[element].change_page[i].target_content.e[xx][yy];
4355
4356           is_player = ELEM_IS_PLAYER(content);
4357
4358           if (is_player && (found_rating < 1 ||
4359                             (found_rating == 1 && element < found_element)))
4360           {
4361             start_x = x + xx - 1;
4362             start_y = y + yy - 1;
4363
4364             found_rating = 1;
4365             found_element = element;
4366           }
4367         }
4368       }
4369     }
4370
4371     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4372                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4373                 start_x - MIDPOSX);
4374
4375     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4376                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4377                 start_y - MIDPOSY);
4378   }
4379   else
4380   {
4381     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4382                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4383                 local_player->jx - MIDPOSX);
4384
4385     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4386                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4387                 local_player->jy - MIDPOSY);
4388   }
4389
4390 #if 0
4391   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4392 #endif
4393
4394 #if 0
4395   /* do not use PLAYING mask for fading out from main screen */
4396   game_status = GAME_MODE_MAIN;
4397 #endif
4398
4399   StopAnimation();
4400
4401   if (!game.restart_level)
4402     CloseDoor(DOOR_CLOSE_1);
4403
4404 #if 1
4405   if (level_editor_test_game)
4406     FadeSkipNextFadeIn();
4407   else
4408     FadeSetEnterScreen();
4409 #else
4410   if (level_editor_test_game)
4411     fading = fading_none;
4412   else
4413     fading = menu.destination;
4414 #endif
4415
4416 #if 1
4417   FadeOut(REDRAW_FIELD);
4418 #else
4419   if (do_fading)
4420     FadeOut(REDRAW_FIELD);
4421 #endif
4422
4423 #if 0
4424   game_status = GAME_MODE_PLAYING;
4425 #endif
4426
4427   /* !!! FIX THIS (START) !!! */
4428   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4429   {
4430     InitGameEngine_EM();
4431
4432     /* blit playfield from scroll buffer to normal back buffer for fading in */
4433     BlitScreenToBitmap_EM(backbuffer);
4434   }
4435   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4436   {
4437     InitGameEngine_SP();
4438
4439     /* blit playfield from scroll buffer to normal back buffer for fading in */
4440     BlitScreenToBitmap_SP(backbuffer);
4441   }
4442   else
4443   {
4444     DrawLevel();
4445     DrawAllPlayers();
4446
4447     /* after drawing the level, correct some elements */
4448     if (game.timegate_time_left == 0)
4449       CloseAllOpenTimegates();
4450
4451 #if NEW_TILESIZE
4452     BlitScreenToBitmap(backbuffer);
4453 #else
4454     /* blit playfield from scroll buffer to normal back buffer for fading in */
4455     if (setup.soft_scrolling)
4456       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4457 #endif
4458
4459     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4460   }
4461   /* !!! FIX THIS (END) !!! */
4462
4463 #if 1
4464   FadeIn(REDRAW_FIELD);
4465 #else
4466   if (do_fading)
4467     FadeIn(REDRAW_FIELD);
4468
4469   BackToFront();
4470 #endif
4471
4472   if (!game.restart_level)
4473   {
4474     /* copy default game door content to main double buffer */
4475 #if 1
4476 #if 1
4477     /* !!! CHECK AGAIN !!! */
4478     SetPanelBackground();
4479     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4480     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4481 #else
4482     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4483
4484     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4485     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4486     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4487                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4488 #endif
4489 #else
4490     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4491                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4492 #endif
4493   }
4494
4495   SetPanelBackground();
4496   SetDrawBackgroundMask(REDRAW_DOOR_1);
4497
4498 #if 1
4499   UpdateAndDisplayGameControlValues();
4500 #else
4501   UpdateGameDoorValues();
4502   DrawGameDoorValues();
4503 #endif
4504
4505   if (!game.restart_level)
4506   {
4507     UnmapGameButtons();
4508     UnmapTapeButtons();
4509     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4510     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4511     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4512     MapGameButtons();
4513     MapTapeButtons();
4514
4515     /* copy actual game door content to door double buffer for OpenDoor() */
4516     BlitBitmap(drawto, bitmap_db_door,
4517                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4518
4519     OpenDoor(DOOR_OPEN_ALL);
4520
4521     PlaySound(SND_GAME_STARTING);
4522
4523     if (setup.sound_music)
4524       PlayLevelMusic();
4525
4526     KeyboardAutoRepeatOffUnlessAutoplay();
4527
4528 #if DEBUG_INIT_PLAYER
4529     if (options.debug)
4530     {
4531       printf("Player status (final):\n");
4532
4533       for (i = 0; i < MAX_PLAYERS; i++)
4534       {
4535         struct PlayerInfo *player = &stored_player[i];
4536
4537         printf("- player %d: present == %d, connected == %d, active == %d",
4538                i + 1,
4539                player->present,
4540                player->connected,
4541                player->active);
4542
4543         if (local_player == player)
4544           printf(" (local player)");
4545
4546         printf("\n");
4547       }
4548     }
4549 #endif
4550   }
4551
4552 #if 1
4553   UnmapAllGadgets();
4554
4555   MapGameButtons();
4556   MapTapeButtons();
4557 #endif
4558
4559   if (!game.restart_level && !tape.playing)
4560   {
4561     LevelStats_incPlayed(level_nr);
4562
4563     SaveLevelSetup_SeriesInfo();
4564
4565 #if 0
4566     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4567 #endif
4568   }
4569
4570   game.restart_level = FALSE;
4571 }
4572
4573 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4574 {
4575   /* this is used for non-R'n'D game engines to update certain engine values */
4576
4577   /* needed to determine if sounds are played within the visible screen area */
4578   scroll_x = actual_scroll_x;
4579   scroll_y = actual_scroll_y;
4580 }
4581
4582 void InitMovDir(int x, int y)
4583 {
4584   int i, element = Feld[x][y];
4585   static int xy[4][2] =
4586   {
4587     {  0, +1 },
4588     { +1,  0 },
4589     {  0, -1 },
4590     { -1,  0 }
4591   };
4592   static int direction[3][4] =
4593   {
4594     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4595     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4596     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4597   };
4598
4599   switch (element)
4600   {
4601     case EL_BUG_RIGHT:
4602     case EL_BUG_UP:
4603     case EL_BUG_LEFT:
4604     case EL_BUG_DOWN:
4605       Feld[x][y] = EL_BUG;
4606       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4607       break;
4608
4609     case EL_SPACESHIP_RIGHT:
4610     case EL_SPACESHIP_UP:
4611     case EL_SPACESHIP_LEFT:
4612     case EL_SPACESHIP_DOWN:
4613       Feld[x][y] = EL_SPACESHIP;
4614       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4615       break;
4616
4617     case EL_BD_BUTTERFLY_RIGHT:
4618     case EL_BD_BUTTERFLY_UP:
4619     case EL_BD_BUTTERFLY_LEFT:
4620     case EL_BD_BUTTERFLY_DOWN:
4621       Feld[x][y] = EL_BD_BUTTERFLY;
4622       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4623       break;
4624
4625     case EL_BD_FIREFLY_RIGHT:
4626     case EL_BD_FIREFLY_UP:
4627     case EL_BD_FIREFLY_LEFT:
4628     case EL_BD_FIREFLY_DOWN:
4629       Feld[x][y] = EL_BD_FIREFLY;
4630       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4631       break;
4632
4633     case EL_PACMAN_RIGHT:
4634     case EL_PACMAN_UP:
4635     case EL_PACMAN_LEFT:
4636     case EL_PACMAN_DOWN:
4637       Feld[x][y] = EL_PACMAN;
4638       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4639       break;
4640
4641     case EL_YAMYAM_LEFT:
4642     case EL_YAMYAM_RIGHT:
4643     case EL_YAMYAM_UP:
4644     case EL_YAMYAM_DOWN:
4645       Feld[x][y] = EL_YAMYAM;
4646       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4647       break;
4648
4649     case EL_SP_SNIKSNAK:
4650       MovDir[x][y] = MV_UP;
4651       break;
4652
4653     case EL_SP_ELECTRON:
4654       MovDir[x][y] = MV_LEFT;
4655       break;
4656
4657     case EL_MOLE_LEFT:
4658     case EL_MOLE_RIGHT:
4659     case EL_MOLE_UP:
4660     case EL_MOLE_DOWN:
4661       Feld[x][y] = EL_MOLE;
4662       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4663       break;
4664
4665     default:
4666       if (IS_CUSTOM_ELEMENT(element))
4667       {
4668         struct ElementInfo *ei = &element_info[element];
4669         int move_direction_initial = ei->move_direction_initial;
4670         int move_pattern = ei->move_pattern;
4671
4672         if (move_direction_initial == MV_START_PREVIOUS)
4673         {
4674           if (MovDir[x][y] != MV_NONE)
4675             return;
4676
4677           move_direction_initial = MV_START_AUTOMATIC;
4678         }
4679
4680         if (move_direction_initial == MV_START_RANDOM)
4681           MovDir[x][y] = 1 << RND(4);
4682         else if (move_direction_initial & MV_ANY_DIRECTION)
4683           MovDir[x][y] = move_direction_initial;
4684         else if (move_pattern == MV_ALL_DIRECTIONS ||
4685                  move_pattern == MV_TURNING_LEFT ||
4686                  move_pattern == MV_TURNING_RIGHT ||
4687                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4688                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4689                  move_pattern == MV_TURNING_RANDOM)
4690           MovDir[x][y] = 1 << RND(4);
4691         else if (move_pattern == MV_HORIZONTAL)
4692           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4693         else if (move_pattern == MV_VERTICAL)
4694           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4695         else if (move_pattern & MV_ANY_DIRECTION)
4696           MovDir[x][y] = element_info[element].move_pattern;
4697         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4698                  move_pattern == MV_ALONG_RIGHT_SIDE)
4699         {
4700           /* use random direction as default start direction */
4701           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4702             MovDir[x][y] = 1 << RND(4);
4703
4704           for (i = 0; i < NUM_DIRECTIONS; i++)
4705           {
4706             int x1 = x + xy[i][0];
4707             int y1 = y + xy[i][1];
4708
4709             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4710             {
4711               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4712                 MovDir[x][y] = direction[0][i];
4713               else
4714                 MovDir[x][y] = direction[1][i];
4715
4716               break;
4717             }
4718           }
4719         }                
4720       }
4721       else
4722       {
4723         MovDir[x][y] = 1 << RND(4);
4724
4725         if (element != EL_BUG &&
4726             element != EL_SPACESHIP &&
4727             element != EL_BD_BUTTERFLY &&
4728             element != EL_BD_FIREFLY)
4729           break;
4730
4731         for (i = 0; i < NUM_DIRECTIONS; i++)
4732         {
4733           int x1 = x + xy[i][0];
4734           int y1 = y + xy[i][1];
4735
4736           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4737           {
4738             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4739             {
4740               MovDir[x][y] = direction[0][i];
4741               break;
4742             }
4743             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4744                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4745             {
4746               MovDir[x][y] = direction[1][i];
4747               break;
4748             }
4749           }
4750         }
4751       }
4752       break;
4753   }
4754
4755   GfxDir[x][y] = MovDir[x][y];
4756 }
4757
4758 void InitAmoebaNr(int x, int y)
4759 {
4760   int i;
4761   int group_nr = AmoebeNachbarNr(x, y);
4762
4763   if (group_nr == 0)
4764   {
4765     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4766     {
4767       if (AmoebaCnt[i] == 0)
4768       {
4769         group_nr = i;
4770         break;
4771       }
4772     }
4773   }
4774
4775   AmoebaNr[x][y] = group_nr;
4776   AmoebaCnt[group_nr]++;
4777   AmoebaCnt2[group_nr]++;
4778 }
4779
4780 static void PlayerWins(struct PlayerInfo *player)
4781 {
4782   player->LevelSolved = TRUE;
4783   player->GameOver = TRUE;
4784
4785   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4786                          level.native_em_level->lev->score : player->score);
4787
4788   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4789                                       TimeLeft);
4790   player->LevelSolved_CountingScore = player->score_final;
4791 }
4792
4793 void GameWon()
4794 {
4795   static int time, time_final;
4796   static int score, score_final;
4797   static int game_over_delay_1 = 0;
4798   static int game_over_delay_2 = 0;
4799   int game_over_delay_value_1 = 50;
4800   int game_over_delay_value_2 = 50;
4801
4802   if (!local_player->LevelSolved_GameWon)
4803   {
4804     int i;
4805
4806     /* do not start end game actions before the player stops moving (to exit) */
4807     if (local_player->MovPos)
4808       return;
4809
4810     local_player->LevelSolved_GameWon = TRUE;
4811     local_player->LevelSolved_SaveTape = tape.recording;
4812     local_player->LevelSolved_SaveScore = !tape.playing;
4813
4814     if (!tape.playing)
4815     {
4816       LevelStats_incSolved(level_nr);
4817
4818       SaveLevelSetup_SeriesInfo();
4819
4820 #if 0
4821       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4822 #endif
4823     }
4824
4825     if (tape.auto_play)         /* tape might already be stopped here */
4826       tape.auto_play_level_solved = TRUE;
4827
4828 #if 1
4829     TapeStop();
4830 #endif
4831
4832     game_over_delay_1 = game_over_delay_value_1;
4833     game_over_delay_2 = game_over_delay_value_2;
4834
4835     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4836     score = score_final = local_player->score_final;
4837
4838     if (TimeLeft > 0)
4839     {
4840       time_final = 0;
4841       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4842     }
4843     else if (game.no_time_limit && TimePlayed < 999)
4844     {
4845       time_final = 999;
4846       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4847     }
4848
4849     local_player->score_final = score_final;
4850
4851     if (level_editor_test_game)
4852     {
4853       time = time_final;
4854       score = score_final;
4855
4856 #if 1
4857       local_player->LevelSolved_CountingTime = time;
4858       local_player->LevelSolved_CountingScore = score;
4859
4860       game_panel_controls[GAME_PANEL_TIME].value = time;
4861       game_panel_controls[GAME_PANEL_SCORE].value = score;
4862
4863       DisplayGameControlValues();
4864 #else
4865       DrawGameValue_Time(time);
4866       DrawGameValue_Score(score);
4867 #endif
4868     }
4869
4870     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4871     {
4872       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4873       {
4874         /* close exit door after last player */
4875         if ((AllPlayersGone &&
4876              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4877               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4878               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4879             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4880             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4881         {
4882           int element = Feld[ExitX][ExitY];
4883
4884 #if 0
4885           if (element == EL_EM_EXIT_OPEN ||
4886               element == EL_EM_STEEL_EXIT_OPEN)
4887           {
4888             Bang(ExitX, ExitY);
4889           }
4890           else
4891 #endif
4892           {
4893             Feld[ExitX][ExitY] =
4894               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4895                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4896                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4897                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4898                EL_EM_STEEL_EXIT_CLOSING);
4899
4900             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4901           }
4902         }
4903
4904         /* player disappears */
4905         DrawLevelField(ExitX, ExitY);
4906       }
4907
4908       for (i = 0; i < MAX_PLAYERS; i++)
4909       {
4910         struct PlayerInfo *player = &stored_player[i];
4911
4912         if (player->present)
4913         {
4914           RemovePlayer(player);
4915
4916           /* player disappears */
4917           DrawLevelField(player->jx, player->jy);
4918         }
4919       }
4920     }
4921
4922     PlaySound(SND_GAME_WINNING);
4923   }
4924
4925   if (game_over_delay_1 > 0)
4926   {
4927     game_over_delay_1--;
4928
4929     return;
4930   }
4931
4932   if (time != time_final)
4933   {
4934     int time_to_go = ABS(time_final - time);
4935     int time_count_dir = (time < time_final ? +1 : -1);
4936     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4937
4938     time  += time_count_steps * time_count_dir;
4939     score += time_count_steps * level.score[SC_TIME_BONUS];
4940
4941 #if 1
4942     local_player->LevelSolved_CountingTime = time;
4943     local_player->LevelSolved_CountingScore = score;
4944
4945     game_panel_controls[GAME_PANEL_TIME].value = time;
4946     game_panel_controls[GAME_PANEL_SCORE].value = score;
4947
4948     DisplayGameControlValues();
4949 #else
4950     DrawGameValue_Time(time);
4951     DrawGameValue_Score(score);
4952 #endif
4953
4954     if (time == time_final)
4955       StopSound(SND_GAME_LEVELTIME_BONUS);
4956     else if (setup.sound_loops)
4957       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4958     else
4959       PlaySound(SND_GAME_LEVELTIME_BONUS);
4960
4961     return;
4962   }
4963
4964   local_player->LevelSolved_PanelOff = TRUE;
4965
4966   if (game_over_delay_2 > 0)
4967   {
4968     game_over_delay_2--;
4969
4970     return;
4971   }
4972
4973 #if 1
4974   GameEnd();
4975 #endif
4976 }
4977
4978 void GameEnd()
4979 {
4980   int hi_pos;
4981   boolean raise_level = FALSE;
4982
4983   local_player->LevelSolved_GameEnd = TRUE;
4984
4985   CloseDoor(DOOR_CLOSE_1);
4986
4987   if (local_player->LevelSolved_SaveTape)
4988   {
4989 #if 0
4990     TapeStop();
4991 #endif
4992
4993 #if 1
4994     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4995 #else
4996     SaveTape(tape.level_nr);            /* ask to save tape */
4997 #endif
4998   }
4999
5000   if (level_editor_test_game)
5001   {
5002     game_status = GAME_MODE_MAIN;
5003
5004 #if 1
5005     DrawAndFadeInMainMenu(REDRAW_FIELD);
5006 #else
5007     DrawMainMenu();
5008 #endif
5009
5010     return;
5011   }
5012
5013   if (!local_player->LevelSolved_SaveScore)
5014   {
5015 #if 1
5016     FadeOut(REDRAW_FIELD);
5017 #endif
5018
5019     game_status = GAME_MODE_MAIN;
5020
5021     DrawAndFadeInMainMenu(REDRAW_FIELD);
5022
5023     return;
5024   }
5025
5026   if (level_nr == leveldir_current->handicap_level)
5027   {
5028     leveldir_current->handicap_level++;
5029
5030     SaveLevelSetup_SeriesInfo();
5031   }
5032
5033   if (level_nr < leveldir_current->last_level)
5034     raise_level = TRUE;                 /* advance to next level */
5035
5036   if ((hi_pos = NewHiScore()) >= 0) 
5037   {
5038     game_status = GAME_MODE_SCORES;
5039
5040     DrawHallOfFame(hi_pos);
5041
5042     if (raise_level)
5043     {
5044       level_nr++;
5045       TapeErase();
5046     }
5047   }
5048   else
5049   {
5050 #if 1
5051     FadeOut(REDRAW_FIELD);
5052 #endif
5053
5054     game_status = GAME_MODE_MAIN;
5055
5056     if (raise_level)
5057     {
5058       level_nr++;
5059       TapeErase();
5060     }
5061
5062     DrawAndFadeInMainMenu(REDRAW_FIELD);
5063   }
5064 }
5065
5066 int NewHiScore()
5067 {
5068   int k, l;
5069   int position = -1;
5070
5071   LoadScore(level_nr);
5072
5073   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5074       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5075     return -1;
5076
5077   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5078   {
5079     if (local_player->score_final > highscore[k].Score)
5080     {
5081       /* player has made it to the hall of fame */
5082
5083       if (k < MAX_SCORE_ENTRIES - 1)
5084       {
5085         int m = MAX_SCORE_ENTRIES - 1;
5086
5087 #ifdef ONE_PER_NAME
5088         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5089           if (strEqual(setup.player_name, highscore[l].Name))
5090             m = l;
5091         if (m == k)     /* player's new highscore overwrites his old one */
5092           goto put_into_list;
5093 #endif
5094
5095         for (l = m; l > k; l--)
5096         {
5097           strcpy(highscore[l].Name, highscore[l - 1].Name);
5098           highscore[l].Score = highscore[l - 1].Score;
5099         }
5100       }
5101
5102 #ifdef ONE_PER_NAME
5103       put_into_list:
5104 #endif
5105       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5106       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5107       highscore[k].Score = local_player->score_final; 
5108       position = k;
5109       break;
5110     }
5111
5112 #ifdef ONE_PER_NAME
5113     else if (!strncmp(setup.player_name, highscore[k].Name,
5114                       MAX_PLAYER_NAME_LEN))
5115       break;    /* player already there with a higher score */
5116 #endif
5117
5118   }
5119
5120   if (position >= 0) 
5121     SaveScore(level_nr);
5122
5123   return position;
5124 }
5125
5126 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5127 {
5128   int element = Feld[x][y];
5129   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5130   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5131   int horiz_move = (dx != 0);
5132   int sign = (horiz_move ? dx : dy);
5133   int step = sign * element_info[element].move_stepsize;
5134
5135   /* special values for move stepsize for spring and things on conveyor belt */
5136   if (horiz_move)
5137   {
5138     if (CAN_FALL(element) &&
5139         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5140       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5141     else if (element == EL_SPRING)
5142       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5143   }
5144
5145   return step;
5146 }
5147
5148 inline static int getElementMoveStepsize(int x, int y)
5149 {
5150   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5151 }
5152
5153 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5154 {
5155   if (player->GfxAction != action || player->GfxDir != dir)
5156   {
5157 #if 0
5158     printf("Player frame reset! (%d => %d, %d => %d)\n",
5159            player->GfxAction, action, player->GfxDir, dir);
5160 #endif
5161
5162     player->GfxAction = action;
5163     player->GfxDir = dir;
5164     player->Frame = 0;
5165     player->StepFrame = 0;
5166   }
5167 }
5168
5169 #if USE_GFX_RESET_GFX_ANIMATION
5170 static void ResetGfxFrame(int x, int y, boolean redraw)
5171 {
5172   int element = Feld[x][y];
5173   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5174   int last_gfx_frame = GfxFrame[x][y];
5175
5176   if (graphic_info[graphic].anim_global_sync)
5177     GfxFrame[x][y] = FrameCounter;
5178   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5179     GfxFrame[x][y] = CustomValue[x][y];
5180   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5181     GfxFrame[x][y] = element_info[element].collect_score;
5182   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5183     GfxFrame[x][y] = ChangeDelay[x][y];
5184
5185   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5186     DrawLevelGraphicAnimation(x, y, graphic);
5187 }
5188 #endif
5189
5190 static void ResetGfxAnimation(int x, int y)
5191 {
5192   GfxAction[x][y] = ACTION_DEFAULT;
5193   GfxDir[x][y] = MovDir[x][y];
5194   GfxFrame[x][y] = 0;
5195
5196 #if USE_GFX_RESET_GFX_ANIMATION
5197   ResetGfxFrame(x, y, FALSE);
5198 #endif
5199 }
5200
5201 static void ResetRandomAnimationValue(int x, int y)
5202 {
5203   GfxRandom[x][y] = INIT_GFX_RANDOM();
5204 }
5205
5206 void InitMovingField(int x, int y, int direction)
5207 {
5208   int element = Feld[x][y];
5209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5211   int newx = x + dx;
5212   int newy = y + dy;
5213   boolean is_moving_before, is_moving_after;
5214 #if 0
5215   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5216 #endif
5217
5218   /* check if element was/is moving or being moved before/after mode change */
5219 #if 1
5220 #if 1
5221   is_moving_before = (WasJustMoving[x][y] != 0);
5222 #else
5223   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5224   is_moving_before = WasJustMoving[x][y];
5225 #endif
5226 #else
5227   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5228 #endif
5229   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5230
5231   /* reset animation only for moving elements which change direction of moving
5232      or which just started or stopped moving
5233      (else CEs with property "can move" / "not moving" are reset each frame) */
5234 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5235 #if 1
5236   if (is_moving_before != is_moving_after ||
5237       direction != MovDir[x][y])
5238     ResetGfxAnimation(x, y);
5239 #else
5240   if ((is_moving_before || is_moving_after) && !continues_moving)
5241     ResetGfxAnimation(x, y);
5242 #endif
5243 #else
5244   if (!continues_moving)
5245     ResetGfxAnimation(x, y);
5246 #endif
5247
5248   MovDir[x][y] = direction;
5249   GfxDir[x][y] = direction;
5250
5251 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5252   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5253                      direction == MV_DOWN && CAN_FALL(element) ?
5254                      ACTION_FALLING : ACTION_MOVING);
5255 #else
5256   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5257                      ACTION_FALLING : ACTION_MOVING);
5258 #endif
5259
5260   /* this is needed for CEs with property "can move" / "not moving" */
5261
5262   if (is_moving_after)
5263   {
5264     if (Feld[newx][newy] == EL_EMPTY)
5265       Feld[newx][newy] = EL_BLOCKED;
5266
5267     MovDir[newx][newy] = MovDir[x][y];
5268
5269 #if USE_NEW_CUSTOM_VALUE
5270     CustomValue[newx][newy] = CustomValue[x][y];
5271 #endif
5272
5273     GfxFrame[newx][newy] = GfxFrame[x][y];
5274     GfxRandom[newx][newy] = GfxRandom[x][y];
5275     GfxAction[newx][newy] = GfxAction[x][y];
5276     GfxDir[newx][newy] = GfxDir[x][y];
5277   }
5278 }
5279
5280 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5281 {
5282   int direction = MovDir[x][y];
5283   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5284   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5285
5286   *goes_to_x = newx;
5287   *goes_to_y = newy;
5288 }
5289
5290 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5291 {
5292   int oldx = x, oldy = y;
5293   int direction = MovDir[x][y];
5294
5295   if (direction == MV_LEFT)
5296     oldx++;
5297   else if (direction == MV_RIGHT)
5298     oldx--;
5299   else if (direction == MV_UP)
5300     oldy++;
5301   else if (direction == MV_DOWN)
5302     oldy--;
5303
5304   *comes_from_x = oldx;
5305   *comes_from_y = oldy;
5306 }
5307
5308 int MovingOrBlocked2Element(int x, int y)
5309 {
5310   int element = Feld[x][y];
5311
5312   if (element == EL_BLOCKED)
5313   {
5314     int oldx, oldy;
5315
5316     Blocked2Moving(x, y, &oldx, &oldy);
5317     return Feld[oldx][oldy];
5318   }
5319   else
5320     return element;
5321 }
5322
5323 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5324 {
5325   /* like MovingOrBlocked2Element(), but if element is moving
5326      and (x,y) is the field the moving element is just leaving,
5327      return EL_BLOCKED instead of the element value */
5328   int element = Feld[x][y];
5329
5330   if (IS_MOVING(x, y))
5331   {
5332     if (element == EL_BLOCKED)
5333     {
5334       int oldx, oldy;
5335
5336       Blocked2Moving(x, y, &oldx, &oldy);
5337       return Feld[oldx][oldy];
5338     }
5339     else
5340       return EL_BLOCKED;
5341   }
5342   else
5343     return element;
5344 }
5345
5346 static void RemoveField(int x, int y)
5347 {
5348   Feld[x][y] = EL_EMPTY;
5349
5350   MovPos[x][y] = 0;
5351   MovDir[x][y] = 0;
5352   MovDelay[x][y] = 0;
5353
5354 #if USE_NEW_CUSTOM_VALUE
5355   CustomValue[x][y] = 0;
5356 #endif
5357
5358   AmoebaNr[x][y] = 0;
5359   ChangeDelay[x][y] = 0;
5360   ChangePage[x][y] = -1;
5361   Pushed[x][y] = FALSE;
5362
5363 #if 0
5364   ExplodeField[x][y] = EX_TYPE_NONE;
5365 #endif
5366
5367   GfxElement[x][y] = EL_UNDEFINED;
5368   GfxAction[x][y] = ACTION_DEFAULT;
5369   GfxDir[x][y] = MV_NONE;
5370 #if 0
5371   /* !!! this would prevent the removed tile from being redrawn !!! */
5372   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5373 #endif
5374 }
5375
5376 void RemoveMovingField(int x, int y)
5377 {
5378   int oldx = x, oldy = y, newx = x, newy = y;
5379   int element = Feld[x][y];
5380   int next_element = EL_UNDEFINED;
5381
5382   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5383     return;
5384
5385   if (IS_MOVING(x, y))
5386   {
5387     Moving2Blocked(x, y, &newx, &newy);
5388
5389     if (Feld[newx][newy] != EL_BLOCKED)
5390     {
5391       /* element is moving, but target field is not free (blocked), but
5392          already occupied by something different (example: acid pool);
5393          in this case, only remove the moving field, but not the target */
5394
5395       RemoveField(oldx, oldy);
5396
5397       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5398
5399       TEST_DrawLevelField(oldx, oldy);
5400
5401       return;
5402     }
5403   }
5404   else if (element == EL_BLOCKED)
5405   {
5406     Blocked2Moving(x, y, &oldx, &oldy);
5407     if (!IS_MOVING(oldx, oldy))
5408       return;
5409   }
5410
5411   if (element == EL_BLOCKED &&
5412       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5413        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5414        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5415        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5416        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5417        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5418     next_element = get_next_element(Feld[oldx][oldy]);
5419
5420   RemoveField(oldx, oldy);
5421   RemoveField(newx, newy);
5422
5423   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5424
5425   if (next_element != EL_UNDEFINED)
5426     Feld[oldx][oldy] = next_element;
5427
5428   TEST_DrawLevelField(oldx, oldy);
5429   TEST_DrawLevelField(newx, newy);
5430 }
5431
5432 void DrawDynamite(int x, int y)
5433 {
5434   int sx = SCREENX(x), sy = SCREENY(y);
5435   int graphic = el2img(Feld[x][y]);
5436   int frame;
5437
5438   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5439     return;
5440
5441   if (IS_WALKABLE_INSIDE(Back[x][y]))
5442     return;
5443
5444   if (Back[x][y])
5445     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5446   else if (Store[x][y])
5447     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5448
5449   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5450
5451   if (Back[x][y] || Store[x][y])
5452     DrawGraphicThruMask(sx, sy, graphic, frame);
5453   else
5454     DrawGraphic(sx, sy, graphic, frame);
5455 }
5456
5457 void CheckDynamite(int x, int y)
5458 {
5459   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5460   {
5461     MovDelay[x][y]--;
5462
5463     if (MovDelay[x][y] != 0)
5464     {
5465       DrawDynamite(x, y);
5466       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5467
5468       return;
5469     }
5470   }
5471
5472   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5473
5474   Bang(x, y);
5475 }
5476
5477 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5478 {
5479   boolean num_checked_players = 0;
5480   int i;
5481
5482   for (i = 0; i < MAX_PLAYERS; i++)
5483   {
5484     if (stored_player[i].active)
5485     {
5486       int sx = stored_player[i].jx;
5487       int sy = stored_player[i].jy;
5488
5489       if (num_checked_players == 0)
5490       {
5491         *sx1 = *sx2 = sx;
5492         *sy1 = *sy2 = sy;
5493       }
5494       else
5495       {
5496         *sx1 = MIN(*sx1, sx);
5497         *sy1 = MIN(*sy1, sy);
5498         *sx2 = MAX(*sx2, sx);
5499         *sy2 = MAX(*sy2, sy);
5500       }
5501
5502       num_checked_players++;
5503     }
5504   }
5505 }
5506
5507 static boolean checkIfAllPlayersFitToScreen_RND()
5508 {
5509   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5510
5511   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5512
5513   return (sx2 - sx1 < SCR_FIELDX &&
5514           sy2 - sy1 < SCR_FIELDY);
5515 }
5516
5517 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5518 {
5519   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5520
5521   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5522
5523   *sx = (sx1 + sx2) / 2;
5524   *sy = (sy1 + sy2) / 2;
5525 }
5526
5527 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5528                         boolean center_screen, boolean quick_relocation)
5529 {
5530   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5531   boolean no_delay = (tape.warp_forward);
5532   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5533   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5534
5535   if (quick_relocation)
5536   {
5537     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5538     {
5539       if (!level.shifted_relocation || center_screen)
5540       {
5541         /* quick relocation (without scrolling), with centering of screen */
5542
5543         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5544                     x > SBX_Right + MIDPOSX ? SBX_Right :
5545                     x - MIDPOSX);
5546
5547         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5548                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5549                     y - MIDPOSY);
5550       }
5551       else
5552       {
5553         /* quick relocation (without scrolling), but do not center screen */
5554
5555         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5556                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5557                                old_x - MIDPOSX);
5558
5559         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5560                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5561                                old_y - MIDPOSY);
5562
5563         int offset_x = x + (scroll_x - center_scroll_x);
5564         int offset_y = y + (scroll_y - center_scroll_y);
5565
5566         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5567                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5568                     offset_x - MIDPOSX);
5569
5570         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5572                     offset_y - MIDPOSY);
5573       }
5574     }
5575     else
5576     {
5577 #if 1
5578       if (!level.shifted_relocation || center_screen)
5579       {
5580         /* quick relocation (without scrolling), with centering of screen */
5581
5582         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5583                     x > SBX_Right + MIDPOSX ? SBX_Right :
5584                     x - MIDPOSX);
5585
5586         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5587                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5588                     y - MIDPOSY);
5589       }
5590       else
5591       {
5592         /* quick relocation (without scrolling), but do not center screen */
5593
5594         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5595                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5596                                old_x - MIDPOSX);
5597
5598         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5599                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5600                                old_y - MIDPOSY);
5601
5602         int offset_x = x + (scroll_x - center_scroll_x);
5603         int offset_y = y + (scroll_y - center_scroll_y);
5604
5605         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5606                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5607                     offset_x - MIDPOSX);
5608
5609         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5611                     offset_y - MIDPOSY);
5612       }
5613 #else
5614       /* quick relocation (without scrolling), inside visible screen area */
5615
5616       int offset = game.scroll_delay_value;
5617
5618       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5619           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5620         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5621
5622       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5623           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5624         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5625
5626       /* don't scroll over playfield boundaries */
5627       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5628         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5629
5630       /* don't scroll over playfield boundaries */
5631       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5632         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5633 #endif
5634     }
5635
5636     RedrawPlayfield(TRUE, 0,0,0,0);
5637   }
5638   else
5639   {
5640 #if 1
5641     int scroll_xx, scroll_yy;
5642
5643     if (!level.shifted_relocation || center_screen)
5644     {
5645       /* visible relocation (with scrolling), with centering of screen */
5646
5647       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5648                    x > SBX_Right + MIDPOSX ? SBX_Right :
5649                    x - MIDPOSX);
5650
5651       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5652                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5653                    y - MIDPOSY);
5654     }
5655     else
5656     {
5657       /* visible relocation (with scrolling), but do not center screen */
5658
5659       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5660                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5661                              old_x - MIDPOSX);
5662
5663       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665                              old_y - MIDPOSY);
5666
5667       int offset_x = x + (scroll_x - center_scroll_x);
5668       int offset_y = y + (scroll_y - center_scroll_y);
5669
5670       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5671                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5672                    offset_x - MIDPOSX);
5673
5674       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5676                    offset_y - MIDPOSY);
5677     }
5678
5679 #else
5680
5681     /* visible relocation (with scrolling), with centering of screen */
5682
5683     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5684                      x > SBX_Right + MIDPOSX ? SBX_Right :
5685                      x - MIDPOSX);
5686
5687     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5688                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5689                      y - MIDPOSY);
5690 #endif
5691
5692     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5693
5694     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5695     {
5696       int dx = 0, dy = 0;
5697       int fx = FX, fy = FY;
5698
5699       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5700       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5701
5702       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5703         break;
5704
5705       scroll_x -= dx;
5706       scroll_y -= dy;
5707
5708       fx += dx * TILEX / 2;
5709       fy += dy * TILEY / 2;
5710
5711       ScrollLevel(dx, dy);
5712       DrawAllPlayers();
5713
5714       /* scroll in two steps of half tile size to make things smoother */
5715       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5716       FlushDisplay();
5717       Delay(wait_delay_value);
5718
5719       /* scroll second step to align at full tile size */
5720       BackToFront();
5721       Delay(wait_delay_value);
5722     }
5723
5724     DrawAllPlayers();
5725     BackToFront();
5726     Delay(wait_delay_value);
5727   }
5728 }
5729
5730 void RelocatePlayer(int jx, int jy, int el_player_raw)
5731 {
5732   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5733   int player_nr = GET_PLAYER_NR(el_player);
5734   struct PlayerInfo *player = &stored_player[player_nr];
5735   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5736   boolean no_delay = (tape.warp_forward);
5737   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5738   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5739   int old_jx = player->jx;
5740   int old_jy = player->jy;
5741   int old_element = Feld[old_jx][old_jy];
5742   int element = Feld[jx][jy];
5743   boolean player_relocated = (old_jx != jx || old_jy != jy);
5744
5745   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5746   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5747   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5748   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5749   int leave_side_horiz = move_dir_horiz;
5750   int leave_side_vert  = move_dir_vert;
5751   int enter_side = enter_side_horiz | enter_side_vert;
5752   int leave_side = leave_side_horiz | leave_side_vert;
5753
5754   if (player->GameOver)         /* do not reanimate dead player */
5755     return;
5756
5757   if (!player_relocated)        /* no need to relocate the player */
5758     return;
5759
5760   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5761   {
5762     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5763     DrawLevelField(jx, jy);
5764   }
5765
5766   if (player->present)
5767   {
5768     while (player->MovPos)
5769     {
5770       ScrollPlayer(player, SCROLL_GO_ON);
5771       ScrollScreen(NULL, SCROLL_GO_ON);
5772
5773       AdvanceFrameAndPlayerCounters(player->index_nr);
5774
5775       DrawPlayer(player);
5776
5777       BackToFront();
5778       Delay(wait_delay_value);
5779     }
5780
5781     DrawPlayer(player);         /* needed here only to cleanup last field */
5782     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5783
5784     player->is_moving = FALSE;
5785   }
5786
5787   if (IS_CUSTOM_ELEMENT(old_element))
5788     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5789                                CE_LEFT_BY_PLAYER,
5790                                player->index_bit, leave_side);
5791
5792   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5793                                       CE_PLAYER_LEAVES_X,
5794                                       player->index_bit, leave_side);
5795
5796   Feld[jx][jy] = el_player;
5797   InitPlayerField(jx, jy, el_player, TRUE);
5798
5799   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5800      possible that the relocation target field did not contain a player element,
5801      but a walkable element, to which the new player was relocated -- in this
5802      case, restore that (already initialized!) element on the player field */
5803   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5804   {
5805     Feld[jx][jy] = element;     /* restore previously existing element */
5806 #if 0
5807     /* !!! do not initialize already initialized element a second time !!! */
5808     /* (this causes at least problems with "element creation" CE trigger for
5809        already existing elements, and existing Sokoban fields counted twice) */
5810     InitField(jx, jy, FALSE);
5811 #endif
5812   }
5813
5814   /* only visually relocate centered player */
5815   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5816                      FALSE, level.instant_relocation);
5817
5818   TestIfPlayerTouchesBadThing(jx, jy);
5819   TestIfPlayerTouchesCustomElement(jx, jy);
5820
5821   if (IS_CUSTOM_ELEMENT(element))
5822     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5823                                player->index_bit, enter_side);
5824
5825   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5826                                       player->index_bit, enter_side);
5827
5828 #if 1
5829   if (player->is_switching)
5830   {
5831     /* ensure that relocation while still switching an element does not cause
5832        a new element to be treated as also switched directly after relocation
5833        (this is important for teleporter switches that teleport the player to
5834        a place where another teleporter switch is in the same direction, which
5835        would then incorrectly be treated as immediately switched before the
5836        direction key that caused the switch was released) */
5837
5838     player->switch_x += jx - old_jx;
5839     player->switch_y += jy - old_jy;
5840   }
5841 #endif
5842 }
5843
5844 void Explode(int ex, int ey, int phase, int mode)
5845 {
5846   int x, y;
5847   int last_phase;
5848   int border_element;
5849
5850   /* !!! eliminate this variable !!! */
5851   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5852
5853   if (game.explosions_delayed)
5854   {
5855     ExplodeField[ex][ey] = mode;
5856     return;
5857   }
5858
5859   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5860   {
5861     int center_element = Feld[ex][ey];
5862     int artwork_element, explosion_element;     /* set these values later */
5863
5864 #if 0
5865     /* --- This is only really needed (and now handled) in "Impact()". --- */
5866     /* do not explode moving elements that left the explode field in time */
5867     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5868         center_element == EL_EMPTY &&
5869         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5870       return;
5871 #endif
5872
5873 #if 0
5874     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5875     if (mode == EX_TYPE_NORMAL ||
5876         mode == EX_TYPE_CENTER ||
5877         mode == EX_TYPE_CROSS)
5878       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5879 #endif
5880
5881     /* remove things displayed in background while burning dynamite */
5882     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5883       Back[ex][ey] = 0;
5884
5885     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5886     {
5887       /* put moving element to center field (and let it explode there) */
5888       center_element = MovingOrBlocked2Element(ex, ey);
5889       RemoveMovingField(ex, ey);
5890       Feld[ex][ey] = center_element;
5891     }
5892
5893     /* now "center_element" is finally determined -- set related values now */
5894     artwork_element = center_element;           /* for custom player artwork */
5895     explosion_element = center_element;         /* for custom player artwork */
5896
5897     if (IS_PLAYER(ex, ey))
5898     {
5899       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5900
5901       artwork_element = stored_player[player_nr].artwork_element;
5902
5903       if (level.use_explosion_element[player_nr])
5904       {
5905         explosion_element = level.explosion_element[player_nr];
5906         artwork_element = explosion_element;
5907       }
5908     }
5909
5910 #if 1
5911     if (mode == EX_TYPE_NORMAL ||
5912         mode == EX_TYPE_CENTER ||
5913         mode == EX_TYPE_CROSS)
5914       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5915 #endif
5916
5917     last_phase = element_info[explosion_element].explosion_delay + 1;
5918
5919     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5920     {
5921       int xx = x - ex + 1;
5922       int yy = y - ey + 1;
5923       int element;
5924
5925       if (!IN_LEV_FIELD(x, y) ||
5926           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5927           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5928         continue;
5929
5930       element = Feld[x][y];
5931
5932       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5933       {
5934         element = MovingOrBlocked2Element(x, y);
5935
5936         if (!IS_EXPLOSION_PROOF(element))
5937           RemoveMovingField(x, y);
5938       }
5939
5940       /* indestructible elements can only explode in center (but not flames) */
5941       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5942                                            mode == EX_TYPE_BORDER)) ||
5943           element == EL_FLAMES)
5944         continue;
5945
5946       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5947          behaviour, for example when touching a yamyam that explodes to rocks
5948          with active deadly shield, a rock is created under the player !!! */
5949       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5950 #if 0
5951       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5952           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5953            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5954 #else
5955       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5956 #endif
5957       {
5958         if (IS_ACTIVE_BOMB(element))
5959         {
5960           /* re-activate things under the bomb like gate or penguin */
5961           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5962           Back[x][y] = 0;
5963         }
5964
5965         continue;
5966       }
5967
5968       /* save walkable background elements while explosion on same tile */
5969       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5970           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5971         Back[x][y] = element;
5972
5973       /* ignite explodable elements reached by other explosion */
5974       if (element == EL_EXPLOSION)
5975         element = Store2[x][y];
5976
5977       if (AmoebaNr[x][y] &&
5978           (element == EL_AMOEBA_FULL ||
5979            element == EL_BD_AMOEBA ||
5980            element == EL_AMOEBA_GROWING))
5981       {
5982         AmoebaCnt[AmoebaNr[x][y]]--;
5983         AmoebaCnt2[AmoebaNr[x][y]]--;
5984       }
5985
5986       RemoveField(x, y);
5987
5988       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5989       {
5990         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5991
5992         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5993
5994         if (PLAYERINFO(ex, ey)->use_murphy)
5995           Store[x][y] = EL_EMPTY;
5996       }
5997
5998       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5999          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6000       else if (ELEM_IS_PLAYER(center_element))
6001         Store[x][y] = EL_EMPTY;
6002       else if (center_element == EL_YAMYAM)
6003         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6004       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6005         Store[x][y] = element_info[center_element].content.e[xx][yy];
6006 #if 1
6007       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6008          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6009          otherwise) -- FIX THIS !!! */
6010       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6011         Store[x][y] = element_info[element].content.e[1][1];
6012 #else
6013       else if (!CAN_EXPLODE(element))
6014         Store[x][y] = element_info[element].content.e[1][1];
6015 #endif
6016       else
6017         Store[x][y] = EL_EMPTY;
6018
6019       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6020           center_element == EL_AMOEBA_TO_DIAMOND)
6021         Store2[x][y] = element;
6022
6023       Feld[x][y] = EL_EXPLOSION;
6024       GfxElement[x][y] = artwork_element;
6025
6026       ExplodePhase[x][y] = 1;
6027       ExplodeDelay[x][y] = last_phase;
6028
6029       Stop[x][y] = TRUE;
6030     }
6031
6032     if (center_element == EL_YAMYAM)
6033       game.yamyam_content_nr =
6034         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6035
6036     return;
6037   }
6038
6039   if (Stop[ex][ey])
6040     return;
6041
6042   x = ex;
6043   y = ey;
6044
6045   if (phase == 1)
6046     GfxFrame[x][y] = 0;         /* restart explosion animation */
6047
6048   last_phase = ExplodeDelay[x][y];
6049
6050   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6051
6052 #ifdef DEBUG
6053
6054   /* activate this even in non-DEBUG version until cause for crash in
6055      getGraphicAnimationFrame() (see below) is found and eliminated */
6056
6057 #endif
6058 #if 1
6059
6060 #if 1
6061   /* this can happen if the player leaves an explosion just in time */
6062   if (GfxElement[x][y] == EL_UNDEFINED)
6063     GfxElement[x][y] = EL_EMPTY;
6064 #else
6065   if (GfxElement[x][y] == EL_UNDEFINED)
6066   {
6067     printf("\n\n");
6068     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6069     printf("Explode(): This should never happen!\n");
6070     printf("\n\n");
6071
6072     GfxElement[x][y] = EL_EMPTY;
6073   }
6074 #endif
6075
6076 #endif
6077
6078   border_element = Store2[x][y];
6079   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6080     border_element = StorePlayer[x][y];
6081
6082   if (phase == element_info[border_element].ignition_delay ||
6083       phase == last_phase)
6084   {
6085     boolean border_explosion = FALSE;
6086
6087     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6088         !PLAYER_EXPLOSION_PROTECTED(x, y))
6089     {
6090       KillPlayerUnlessExplosionProtected(x, y);
6091       border_explosion = TRUE;
6092     }
6093     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6094     {
6095       Feld[x][y] = Store2[x][y];
6096       Store2[x][y] = 0;
6097       Bang(x, y);
6098       border_explosion = TRUE;
6099     }
6100     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6101     {
6102       AmoebeUmwandeln(x, y);
6103       Store2[x][y] = 0;
6104       border_explosion = TRUE;
6105     }
6106
6107     /* if an element just explodes due to another explosion (chain-reaction),
6108        do not immediately end the new explosion when it was the last frame of
6109        the explosion (as it would be done in the following "if"-statement!) */
6110     if (border_explosion && phase == last_phase)
6111       return;
6112   }
6113
6114   if (phase == last_phase)
6115   {
6116     int element;
6117
6118     element = Feld[x][y] = Store[x][y];
6119     Store[x][y] = Store2[x][y] = 0;
6120     GfxElement[x][y] = EL_UNDEFINED;
6121
6122     /* player can escape from explosions and might therefore be still alive */
6123     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6124         element <= EL_PLAYER_IS_EXPLODING_4)
6125     {
6126       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6127       int explosion_element = EL_PLAYER_1 + player_nr;
6128       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6129       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6130
6131       if (level.use_explosion_element[player_nr])
6132         explosion_element = level.explosion_element[player_nr];
6133
6134       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6135                     element_info[explosion_element].content.e[xx][yy]);
6136     }
6137
6138     /* restore probably existing indestructible background element */
6139     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6140       element = Feld[x][y] = Back[x][y];
6141     Back[x][y] = 0;
6142
6143     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6144     GfxDir[x][y] = MV_NONE;
6145     ChangeDelay[x][y] = 0;
6146     ChangePage[x][y] = -1;
6147
6148 #if USE_NEW_CUSTOM_VALUE
6149     CustomValue[x][y] = 0;
6150 #endif
6151
6152     InitField_WithBug2(x, y, FALSE);
6153
6154     TEST_DrawLevelField(x, y);
6155
6156     TestIfElementTouchesCustomElement(x, y);
6157
6158     if (GFX_CRUMBLED(element))
6159       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6160
6161     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6162       StorePlayer[x][y] = 0;
6163
6164     if (ELEM_IS_PLAYER(element))
6165       RelocatePlayer(x, y, element);
6166   }
6167   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6168   {
6169     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6170     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6171
6172     if (phase == delay)
6173       TEST_DrawLevelFieldCrumbled(x, y);
6174
6175     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6176     {
6177       DrawLevelElement(x, y, Back[x][y]);
6178       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6179     }
6180     else if (IS_WALKABLE_UNDER(Back[x][y]))
6181     {
6182       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6183       DrawLevelElementThruMask(x, y, Back[x][y]);
6184     }
6185     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6186       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6187   }
6188 }
6189
6190 void DynaExplode(int ex, int ey)
6191 {
6192   int i, j;
6193   int dynabomb_element = Feld[ex][ey];
6194   int dynabomb_size = 1;
6195   boolean dynabomb_xl = FALSE;
6196   struct PlayerInfo *player;
6197   static int xy[4][2] =
6198   {
6199     { 0, -1 },
6200     { -1, 0 },
6201     { +1, 0 },
6202     { 0, +1 }
6203   };
6204
6205   if (IS_ACTIVE_BOMB(dynabomb_element))
6206   {
6207     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6208     dynabomb_size = player->dynabomb_size;
6209     dynabomb_xl = player->dynabomb_xl;
6210     player->dynabombs_left++;
6211   }
6212
6213   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6214
6215   for (i = 0; i < NUM_DIRECTIONS; i++)
6216   {
6217     for (j = 1; j <= dynabomb_size; j++)
6218     {
6219       int x = ex + j * xy[i][0];
6220       int y = ey + j * xy[i][1];
6221       int element;
6222
6223       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6224         break;
6225
6226       element = Feld[x][y];
6227
6228       /* do not restart explosions of fields with active bombs */
6229       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6230         continue;
6231
6232       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6233
6234       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6235           !IS_DIGGABLE(element) && !dynabomb_xl)
6236         break;
6237     }
6238   }
6239 }
6240
6241 void Bang(int x, int y)
6242 {
6243   int element = MovingOrBlocked2Element(x, y);
6244   int explosion_type = EX_TYPE_NORMAL;
6245
6246   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6247   {
6248     struct PlayerInfo *player = PLAYERINFO(x, y);
6249
6250 #if USE_FIX_CE_ACTION_WITH_PLAYER
6251     element = Feld[x][y] = player->initial_element;
6252 #else
6253     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6254                             player->element_nr);
6255 #endif
6256
6257     if (level.use_explosion_element[player->index_nr])
6258     {
6259       int explosion_element = level.explosion_element[player->index_nr];
6260
6261       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6262         explosion_type = EX_TYPE_CROSS;
6263       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6264         explosion_type = EX_TYPE_CENTER;
6265     }
6266   }
6267
6268   switch (element)
6269   {
6270     case EL_BUG:
6271     case EL_SPACESHIP:
6272     case EL_BD_BUTTERFLY:
6273     case EL_BD_FIREFLY:
6274     case EL_YAMYAM:
6275     case EL_DARK_YAMYAM:
6276     case EL_ROBOT:
6277     case EL_PACMAN:
6278     case EL_MOLE:
6279       RaiseScoreElement(element);
6280       break;
6281
6282     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6283     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6284     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6285     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6286     case EL_DYNABOMB_INCREASE_NUMBER:
6287     case EL_DYNABOMB_INCREASE_SIZE:
6288     case EL_DYNABOMB_INCREASE_POWER:
6289       explosion_type = EX_TYPE_DYNA;
6290       break;
6291
6292     case EL_DC_LANDMINE:
6293 #if 0
6294     case EL_EM_EXIT_OPEN:
6295     case EL_EM_STEEL_EXIT_OPEN:
6296 #endif
6297       explosion_type = EX_TYPE_CENTER;
6298       break;
6299
6300     case EL_PENGUIN:
6301     case EL_LAMP:
6302     case EL_LAMP_ACTIVE:
6303     case EL_AMOEBA_TO_DIAMOND:
6304       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6305         explosion_type = EX_TYPE_CENTER;
6306       break;
6307
6308     default:
6309       if (element_info[element].explosion_type == EXPLODES_CROSS)
6310         explosion_type = EX_TYPE_CROSS;
6311       else if (element_info[element].explosion_type == EXPLODES_1X1)
6312         explosion_type = EX_TYPE_CENTER;
6313       break;
6314   }
6315
6316   if (explosion_type == EX_TYPE_DYNA)
6317     DynaExplode(x, y);
6318   else
6319     Explode(x, y, EX_PHASE_START, explosion_type);
6320
6321   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6322 }
6323
6324 void SplashAcid(int x, int y)
6325 {
6326   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6327       (!IN_LEV_FIELD(x - 1, y - 2) ||
6328        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6329     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6330
6331   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6332       (!IN_LEV_FIELD(x + 1, y - 2) ||
6333        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6334     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6335
6336   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6337 }
6338
6339 static void InitBeltMovement()
6340 {
6341   static int belt_base_element[4] =
6342   {
6343     EL_CONVEYOR_BELT_1_LEFT,
6344     EL_CONVEYOR_BELT_2_LEFT,
6345     EL_CONVEYOR_BELT_3_LEFT,
6346     EL_CONVEYOR_BELT_4_LEFT
6347   };
6348   static int belt_base_active_element[4] =
6349   {
6350     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6351     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6352     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6354   };
6355
6356   int x, y, i, j;
6357
6358   /* set frame order for belt animation graphic according to belt direction */
6359   for (i = 0; i < NUM_BELTS; i++)
6360   {
6361     int belt_nr = i;
6362
6363     for (j = 0; j < NUM_BELT_PARTS; j++)
6364     {
6365       int element = belt_base_active_element[belt_nr] + j;
6366       int graphic_1 = el2img(element);
6367       int graphic_2 = el2panelimg(element);
6368
6369       if (game.belt_dir[i] == MV_LEFT)
6370       {
6371         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6372         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6373       }
6374       else
6375       {
6376         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6377         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6378       }
6379     }
6380   }
6381
6382   SCAN_PLAYFIELD(x, y)
6383   {
6384     int element = Feld[x][y];
6385
6386     for (i = 0; i < NUM_BELTS; i++)
6387     {
6388       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6389       {
6390         int e_belt_nr = getBeltNrFromBeltElement(element);
6391         int belt_nr = i;
6392
6393         if (e_belt_nr == belt_nr)
6394         {
6395           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6396
6397           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6398         }
6399       }
6400     }
6401   }
6402 }
6403
6404 static void ToggleBeltSwitch(int x, int y)
6405 {
6406   static int belt_base_element[4] =
6407   {
6408     EL_CONVEYOR_BELT_1_LEFT,
6409     EL_CONVEYOR_BELT_2_LEFT,
6410     EL_CONVEYOR_BELT_3_LEFT,
6411     EL_CONVEYOR_BELT_4_LEFT
6412   };
6413   static int belt_base_active_element[4] =
6414   {
6415     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6416     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6417     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6418     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6419   };
6420   static int belt_base_switch_element[4] =
6421   {
6422     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6423     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6424     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6425     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6426   };
6427   static int belt_move_dir[4] =
6428   {
6429     MV_LEFT,
6430     MV_NONE,
6431     MV_RIGHT,
6432     MV_NONE,
6433   };
6434
6435   int element = Feld[x][y];
6436   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6437   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6438   int belt_dir = belt_move_dir[belt_dir_nr];
6439   int xx, yy, i;
6440
6441   if (!IS_BELT_SWITCH(element))
6442     return;
6443
6444   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6445   game.belt_dir[belt_nr] = belt_dir;
6446
6447   if (belt_dir_nr == 3)
6448     belt_dir_nr = 1;
6449
6450   /* set frame order for belt animation graphic according to belt direction */
6451   for (i = 0; i < NUM_BELT_PARTS; i++)
6452   {
6453     int element = belt_base_active_element[belt_nr] + i;
6454     int graphic_1 = el2img(element);
6455     int graphic_2 = el2panelimg(element);
6456
6457     if (belt_dir == MV_LEFT)
6458     {
6459       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6460       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6461     }
6462     else
6463     {
6464       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6465       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6466     }
6467   }
6468
6469   SCAN_PLAYFIELD(xx, yy)
6470   {
6471     int element = Feld[xx][yy];
6472
6473     if (IS_BELT_SWITCH(element))
6474     {
6475       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6476
6477       if (e_belt_nr == belt_nr)
6478       {
6479         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6480         TEST_DrawLevelField(xx, yy);
6481       }
6482     }
6483     else if (IS_BELT(element) && belt_dir != MV_NONE)
6484     {
6485       int e_belt_nr = getBeltNrFromBeltElement(element);
6486
6487       if (e_belt_nr == belt_nr)
6488       {
6489         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6490
6491         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6492         TEST_DrawLevelField(xx, yy);
6493       }
6494     }
6495     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6496     {
6497       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6498
6499       if (e_belt_nr == belt_nr)
6500       {
6501         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6502
6503         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6504         TEST_DrawLevelField(xx, yy);
6505       }
6506     }
6507   }
6508 }
6509
6510 static void ToggleSwitchgateSwitch(int x, int y)
6511 {
6512   int xx, yy;
6513
6514   game.switchgate_pos = !game.switchgate_pos;
6515
6516   SCAN_PLAYFIELD(xx, yy)
6517   {
6518     int element = Feld[xx][yy];
6519
6520 #if !USE_BOTH_SWITCHGATE_SWITCHES
6521     if (element == EL_SWITCHGATE_SWITCH_UP ||
6522         element == EL_SWITCHGATE_SWITCH_DOWN)
6523     {
6524       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6525       TEST_DrawLevelField(xx, yy);
6526     }
6527     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6528              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6529     {
6530       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6531       TEST_DrawLevelField(xx, yy);
6532     }
6533 #else
6534     if (element == EL_SWITCHGATE_SWITCH_UP)
6535     {
6536       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6537       TEST_DrawLevelField(xx, yy);
6538     }
6539     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6540     {
6541       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6542       TEST_DrawLevelField(xx, yy);
6543     }
6544     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6545     {
6546       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6547       TEST_DrawLevelField(xx, yy);
6548     }
6549     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6550     {
6551       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6552       TEST_DrawLevelField(xx, yy);
6553     }
6554 #endif
6555     else if (element == EL_SWITCHGATE_OPEN ||
6556              element == EL_SWITCHGATE_OPENING)
6557     {
6558       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6559
6560       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6561     }
6562     else if (element == EL_SWITCHGATE_CLOSED ||
6563              element == EL_SWITCHGATE_CLOSING)
6564     {
6565       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6566
6567       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6568     }
6569   }
6570 }
6571
6572 static int getInvisibleActiveFromInvisibleElement(int element)
6573 {
6574   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6575           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6576           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6577           element);
6578 }
6579
6580 static int getInvisibleFromInvisibleActiveElement(int element)
6581 {
6582   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6583           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6584           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6585           element);
6586 }
6587
6588 static void RedrawAllLightSwitchesAndInvisibleElements()
6589 {
6590   int x, y;
6591
6592   SCAN_PLAYFIELD(x, y)
6593   {
6594     int element = Feld[x][y];
6595
6596     if (element == EL_LIGHT_SWITCH &&
6597         game.light_time_left > 0)
6598     {
6599       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6600       TEST_DrawLevelField(x, y);
6601     }
6602     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6603              game.light_time_left == 0)
6604     {
6605       Feld[x][y] = EL_LIGHT_SWITCH;
6606       TEST_DrawLevelField(x, y);
6607     }
6608     else if (element == EL_EMC_DRIPPER &&
6609              game.light_time_left > 0)
6610     {
6611       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6612       TEST_DrawLevelField(x, y);
6613     }
6614     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6615              game.light_time_left == 0)
6616     {
6617       Feld[x][y] = EL_EMC_DRIPPER;
6618       TEST_DrawLevelField(x, y);
6619     }
6620     else if (element == EL_INVISIBLE_STEELWALL ||
6621              element == EL_INVISIBLE_WALL ||
6622              element == EL_INVISIBLE_SAND)
6623     {
6624       if (game.light_time_left > 0)
6625         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6626
6627       TEST_DrawLevelField(x, y);
6628
6629       /* uncrumble neighbour fields, if needed */
6630       if (element == EL_INVISIBLE_SAND)
6631         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6632     }
6633     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6634              element == EL_INVISIBLE_WALL_ACTIVE ||
6635              element == EL_INVISIBLE_SAND_ACTIVE)
6636     {
6637       if (game.light_time_left == 0)
6638         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6639
6640       TEST_DrawLevelField(x, y);
6641
6642       /* re-crumble neighbour fields, if needed */
6643       if (element == EL_INVISIBLE_SAND)
6644         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6645     }
6646   }
6647 }
6648
6649 static void RedrawAllInvisibleElementsForLenses()
6650 {
6651   int x, y;
6652
6653   SCAN_PLAYFIELD(x, y)
6654   {
6655     int element = Feld[x][y];
6656
6657     if (element == EL_EMC_DRIPPER &&
6658         game.lenses_time_left > 0)
6659     {
6660       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6661       TEST_DrawLevelField(x, y);
6662     }
6663     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6664              game.lenses_time_left == 0)
6665     {
6666       Feld[x][y] = EL_EMC_DRIPPER;
6667       TEST_DrawLevelField(x, y);
6668     }
6669     else if (element == EL_INVISIBLE_STEELWALL ||
6670              element == EL_INVISIBLE_WALL ||
6671              element == EL_INVISIBLE_SAND)
6672     {
6673       if (game.lenses_time_left > 0)
6674         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6675
6676       TEST_DrawLevelField(x, y);
6677
6678       /* uncrumble neighbour fields, if needed */
6679       if (element == EL_INVISIBLE_SAND)
6680         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6681     }
6682     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6683              element == EL_INVISIBLE_WALL_ACTIVE ||
6684              element == EL_INVISIBLE_SAND_ACTIVE)
6685     {
6686       if (game.lenses_time_left == 0)
6687         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6688
6689       TEST_DrawLevelField(x, y);
6690
6691       /* re-crumble neighbour fields, if needed */
6692       if (element == EL_INVISIBLE_SAND)
6693         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6694     }
6695   }
6696 }
6697
6698 static void RedrawAllInvisibleElementsForMagnifier()
6699 {
6700   int x, y;
6701
6702   SCAN_PLAYFIELD(x, y)
6703   {
6704     int element = Feld[x][y];
6705
6706     if (element == EL_EMC_FAKE_GRASS &&
6707         game.magnify_time_left > 0)
6708     {
6709       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6710       TEST_DrawLevelField(x, y);
6711     }
6712     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6713              game.magnify_time_left == 0)
6714     {
6715       Feld[x][y] = EL_EMC_FAKE_GRASS;
6716       TEST_DrawLevelField(x, y);
6717     }
6718     else if (IS_GATE_GRAY(element) &&
6719              game.magnify_time_left > 0)
6720     {
6721       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6722                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6723                     IS_EM_GATE_GRAY(element) ?
6724                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6725                     IS_EMC_GATE_GRAY(element) ?
6726                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6727                     IS_DC_GATE_GRAY(element) ?
6728                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6729                     element);
6730       TEST_DrawLevelField(x, y);
6731     }
6732     else if (IS_GATE_GRAY_ACTIVE(element) &&
6733              game.magnify_time_left == 0)
6734     {
6735       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6736                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6737                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6738                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6739                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6740                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6741                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6742                     EL_DC_GATE_WHITE_GRAY :
6743                     element);
6744       TEST_DrawLevelField(x, y);
6745     }
6746   }
6747 }
6748
6749 static void ToggleLightSwitch(int x, int y)
6750 {
6751   int element = Feld[x][y];
6752
6753   game.light_time_left =
6754     (element == EL_LIGHT_SWITCH ?
6755      level.time_light * FRAMES_PER_SECOND : 0);
6756
6757   RedrawAllLightSwitchesAndInvisibleElements();
6758 }
6759
6760 static void ActivateTimegateSwitch(int x, int y)
6761 {
6762   int xx, yy;
6763
6764   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6765
6766   SCAN_PLAYFIELD(xx, yy)
6767   {
6768     int element = Feld[xx][yy];
6769
6770     if (element == EL_TIMEGATE_CLOSED ||
6771         element == EL_TIMEGATE_CLOSING)
6772     {
6773       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6774       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6775     }
6776
6777     /*
6778     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6779     {
6780       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6781       TEST_DrawLevelField(xx, yy);
6782     }
6783     */
6784
6785   }
6786
6787 #if 1
6788   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6789                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6790 #else
6791   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6792 #endif
6793 }
6794
6795 void Impact(int x, int y)
6796 {
6797   boolean last_line = (y == lev_fieldy - 1);
6798   boolean object_hit = FALSE;
6799   boolean impact = (last_line || object_hit);
6800   int element = Feld[x][y];
6801   int smashed = EL_STEELWALL;
6802
6803   if (!last_line)       /* check if element below was hit */
6804   {
6805     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6806       return;
6807
6808     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6809                                          MovDir[x][y + 1] != MV_DOWN ||
6810                                          MovPos[x][y + 1] <= TILEY / 2));
6811
6812     /* do not smash moving elements that left the smashed field in time */
6813     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6814         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6815       object_hit = FALSE;
6816
6817 #if USE_QUICKSAND_IMPACT_BUGFIX
6818     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6819     {
6820       RemoveMovingField(x, y + 1);
6821       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6822       Feld[x][y + 2] = EL_ROCK;
6823       TEST_DrawLevelField(x, y + 2);
6824
6825       object_hit = TRUE;
6826     }
6827
6828     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6829     {
6830       RemoveMovingField(x, y + 1);
6831       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6832       Feld[x][y + 2] = EL_ROCK;
6833       TEST_DrawLevelField(x, y + 2);
6834
6835       object_hit = TRUE;
6836     }
6837 #endif
6838
6839     if (object_hit)
6840       smashed = MovingOrBlocked2Element(x, y + 1);
6841
6842     impact = (last_line || object_hit);
6843   }
6844
6845   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6846   {
6847     SplashAcid(x, y + 1);
6848     return;
6849   }
6850
6851   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6852   /* only reset graphic animation if graphic really changes after impact */
6853   if (impact &&
6854       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6855   {
6856     ResetGfxAnimation(x, y);
6857     TEST_DrawLevelField(x, y);
6858   }
6859
6860   if (impact && CAN_EXPLODE_IMPACT(element))
6861   {
6862     Bang(x, y);
6863     return;
6864   }
6865   else if (impact && element == EL_PEARL &&
6866            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6867   {
6868     ResetGfxAnimation(x, y);
6869
6870     Feld[x][y] = EL_PEARL_BREAKING;
6871     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6872     return;
6873   }
6874   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6875   {
6876     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6877
6878     return;
6879   }
6880
6881   if (impact && element == EL_AMOEBA_DROP)
6882   {
6883     if (object_hit && IS_PLAYER(x, y + 1))
6884       KillPlayerUnlessEnemyProtected(x, y + 1);
6885     else if (object_hit && smashed == EL_PENGUIN)
6886       Bang(x, y + 1);
6887     else
6888     {
6889       Feld[x][y] = EL_AMOEBA_GROWING;
6890       Store[x][y] = EL_AMOEBA_WET;
6891
6892       ResetRandomAnimationValue(x, y);
6893     }
6894     return;
6895   }
6896
6897   if (object_hit)               /* check which object was hit */
6898   {
6899     if ((CAN_PASS_MAGIC_WALL(element) && 
6900          (smashed == EL_MAGIC_WALL ||
6901           smashed == EL_BD_MAGIC_WALL)) ||
6902         (CAN_PASS_DC_MAGIC_WALL(element) &&
6903          smashed == EL_DC_MAGIC_WALL))
6904     {
6905       int xx, yy;
6906       int activated_magic_wall =
6907         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6908          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6909          EL_DC_MAGIC_WALL_ACTIVE);
6910
6911       /* activate magic wall / mill */
6912       SCAN_PLAYFIELD(xx, yy)
6913       {
6914         if (Feld[xx][yy] == smashed)
6915           Feld[xx][yy] = activated_magic_wall;
6916       }
6917
6918       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6919       game.magic_wall_active = TRUE;
6920
6921       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6922                             SND_MAGIC_WALL_ACTIVATING :
6923                             smashed == EL_BD_MAGIC_WALL ?
6924                             SND_BD_MAGIC_WALL_ACTIVATING :
6925                             SND_DC_MAGIC_WALL_ACTIVATING));
6926     }
6927
6928     if (IS_PLAYER(x, y + 1))
6929     {
6930       if (CAN_SMASH_PLAYER(element))
6931       {
6932         KillPlayerUnlessEnemyProtected(x, y + 1);
6933         return;
6934       }
6935     }
6936     else if (smashed == EL_PENGUIN)
6937     {
6938       if (CAN_SMASH_PLAYER(element))
6939       {
6940         Bang(x, y + 1);
6941         return;
6942       }
6943     }
6944     else if (element == EL_BD_DIAMOND)
6945     {
6946       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6947       {
6948         Bang(x, y + 1);
6949         return;
6950       }
6951     }
6952     else if (((element == EL_SP_INFOTRON ||
6953                element == EL_SP_ZONK) &&
6954               (smashed == EL_SP_SNIKSNAK ||
6955                smashed == EL_SP_ELECTRON ||
6956                smashed == EL_SP_DISK_ORANGE)) ||
6957              (element == EL_SP_INFOTRON &&
6958               smashed == EL_SP_DISK_YELLOW))
6959     {
6960       Bang(x, y + 1);
6961       return;
6962     }
6963     else if (CAN_SMASH_EVERYTHING(element))
6964     {
6965       if (IS_CLASSIC_ENEMY(smashed) ||
6966           CAN_EXPLODE_SMASHED(smashed))
6967       {
6968         Bang(x, y + 1);
6969         return;
6970       }
6971       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6972       {
6973         if (smashed == EL_LAMP ||
6974             smashed == EL_LAMP_ACTIVE)
6975         {
6976           Bang(x, y + 1);
6977           return;
6978         }
6979         else if (smashed == EL_NUT)
6980         {
6981           Feld[x][y + 1] = EL_NUT_BREAKING;
6982           PlayLevelSound(x, y, SND_NUT_BREAKING);
6983           RaiseScoreElement(EL_NUT);
6984           return;
6985         }
6986         else if (smashed == EL_PEARL)
6987         {
6988           ResetGfxAnimation(x, y);
6989
6990           Feld[x][y + 1] = EL_PEARL_BREAKING;
6991           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6992           return;
6993         }
6994         else if (smashed == EL_DIAMOND)
6995         {
6996           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6997           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6998           return;
6999         }
7000         else if (IS_BELT_SWITCH(smashed))
7001         {
7002           ToggleBeltSwitch(x, y + 1);
7003         }
7004         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7005                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7006                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7007                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7008         {
7009           ToggleSwitchgateSwitch(x, y + 1);
7010         }
7011         else if (smashed == EL_LIGHT_SWITCH ||
7012                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7013         {
7014           ToggleLightSwitch(x, y + 1);
7015         }
7016         else
7017         {
7018 #if 0
7019           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7020 #endif
7021
7022           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7023
7024           CheckElementChangeBySide(x, y + 1, smashed, element,
7025                                    CE_SWITCHED, CH_SIDE_TOP);
7026           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7027                                             CH_SIDE_TOP);
7028         }
7029       }
7030       else
7031       {
7032         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7033       }
7034     }
7035   }
7036
7037   /* play sound of magic wall / mill */
7038   if (!last_line &&
7039       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7040        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7041        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7042   {
7043     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7044       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7045     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7046       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7047     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7048       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7049
7050     return;
7051   }
7052
7053   /* play sound of object that hits the ground */
7054   if (last_line || object_hit)
7055     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7056 }
7057
7058 inline static void TurnRoundExt(int x, int y)
7059 {
7060   static struct
7061   {
7062     int dx, dy;
7063   } move_xy[] =
7064   {
7065     {  0,  0 },
7066     { -1,  0 },
7067     { +1,  0 },
7068     {  0,  0 },
7069     {  0, -1 },
7070     {  0,  0 }, { 0, 0 }, { 0, 0 },
7071     {  0, +1 }
7072   };
7073   static struct
7074   {
7075     int left, right, back;
7076   } turn[] =
7077   {
7078     { 0,        0,              0        },
7079     { MV_DOWN,  MV_UP,          MV_RIGHT },
7080     { MV_UP,    MV_DOWN,        MV_LEFT  },
7081     { 0,        0,              0        },
7082     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7083     { 0,        0,              0        },
7084     { 0,        0,              0        },
7085     { 0,        0,              0        },
7086     { MV_RIGHT, MV_LEFT,        MV_UP    }
7087   };
7088
7089   int element = Feld[x][y];
7090   int move_pattern = element_info[element].move_pattern;
7091
7092   int old_move_dir = MovDir[x][y];
7093   int left_dir  = turn[old_move_dir].left;
7094   int right_dir = turn[old_move_dir].right;
7095   int back_dir  = turn[old_move_dir].back;
7096
7097   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7098   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7099   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7100   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7101
7102   int left_x  = x + left_dx,  left_y  = y + left_dy;
7103   int right_x = x + right_dx, right_y = y + right_dy;
7104   int move_x  = x + move_dx,  move_y  = y + move_dy;
7105
7106   int xx, yy;
7107
7108   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7109   {
7110     TestIfBadThingTouchesOtherBadThing(x, y);
7111
7112     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7113       MovDir[x][y] = right_dir;
7114     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7115       MovDir[x][y] = left_dir;
7116
7117     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7118       MovDelay[x][y] = 9;
7119     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7120       MovDelay[x][y] = 1;
7121   }
7122   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7123   {
7124     TestIfBadThingTouchesOtherBadThing(x, y);
7125
7126     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7127       MovDir[x][y] = left_dir;
7128     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7129       MovDir[x][y] = right_dir;
7130
7131     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7132       MovDelay[x][y] = 9;
7133     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7134       MovDelay[x][y] = 1;
7135   }
7136   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7137   {
7138     TestIfBadThingTouchesOtherBadThing(x, y);
7139
7140     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7141       MovDir[x][y] = left_dir;
7142     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7143       MovDir[x][y] = right_dir;
7144
7145     if (MovDir[x][y] != old_move_dir)
7146       MovDelay[x][y] = 9;
7147   }
7148   else if (element == EL_YAMYAM)
7149   {
7150     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7151     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7152
7153     if (can_turn_left && can_turn_right)
7154       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7155     else if (can_turn_left)
7156       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7157     else if (can_turn_right)
7158       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7159     else
7160       MovDir[x][y] = back_dir;
7161
7162     MovDelay[x][y] = 16 + 16 * RND(3);
7163   }
7164   else if (element == EL_DARK_YAMYAM)
7165   {
7166     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7167                                                          left_x, left_y);
7168     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7169                                                          right_x, right_y);
7170
7171     if (can_turn_left && can_turn_right)
7172       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7173     else if (can_turn_left)
7174       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7175     else if (can_turn_right)
7176       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7177     else
7178       MovDir[x][y] = back_dir;
7179
7180     MovDelay[x][y] = 16 + 16 * RND(3);
7181   }
7182   else if (element == EL_PACMAN)
7183   {
7184     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7185     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7186
7187     if (can_turn_left && can_turn_right)
7188       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7189     else if (can_turn_left)
7190       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7191     else if (can_turn_right)
7192       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7193     else
7194       MovDir[x][y] = back_dir;
7195
7196     MovDelay[x][y] = 6 + RND(40);
7197   }
7198   else if (element == EL_PIG)
7199   {
7200     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7201     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7202     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7203     boolean should_turn_left, should_turn_right, should_move_on;
7204     int rnd_value = 24;
7205     int rnd = RND(rnd_value);
7206
7207     should_turn_left = (can_turn_left &&
7208                         (!can_move_on ||
7209                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7210                                                    y + back_dy + left_dy)));
7211     should_turn_right = (can_turn_right &&
7212                          (!can_move_on ||
7213                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7214                                                     y + back_dy + right_dy)));
7215     should_move_on = (can_move_on &&
7216                       (!can_turn_left ||
7217                        !can_turn_right ||
7218                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7219                                                  y + move_dy + left_dy) ||
7220                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7221                                                  y + move_dy + right_dy)));
7222
7223     if (should_turn_left || should_turn_right || should_move_on)
7224     {
7225       if (should_turn_left && should_turn_right && should_move_on)
7226         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7227                         rnd < 2 * rnd_value / 3 ? right_dir :
7228                         old_move_dir);
7229       else if (should_turn_left && should_turn_right)
7230         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7231       else if (should_turn_left && should_move_on)
7232         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7233       else if (should_turn_right && should_move_on)
7234         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7235       else if (should_turn_left)
7236         MovDir[x][y] = left_dir;
7237       else if (should_turn_right)
7238         MovDir[x][y] = right_dir;
7239       else if (should_move_on)
7240         MovDir[x][y] = old_move_dir;
7241     }
7242     else if (can_move_on && rnd > rnd_value / 8)
7243       MovDir[x][y] = old_move_dir;
7244     else if (can_turn_left && can_turn_right)
7245       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7246     else if (can_turn_left && rnd > rnd_value / 8)
7247       MovDir[x][y] = left_dir;
7248     else if (can_turn_right && rnd > rnd_value/8)
7249       MovDir[x][y] = right_dir;
7250     else
7251       MovDir[x][y] = back_dir;
7252
7253     xx = x + move_xy[MovDir[x][y]].dx;
7254     yy = y + move_xy[MovDir[x][y]].dy;
7255
7256     if (!IN_LEV_FIELD(xx, yy) ||
7257         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7258       MovDir[x][y] = old_move_dir;
7259
7260     MovDelay[x][y] = 0;
7261   }
7262   else if (element == EL_DRAGON)
7263   {
7264     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7265     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7266     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7267     int rnd_value = 24;
7268     int rnd = RND(rnd_value);
7269
7270     if (can_move_on && rnd > rnd_value / 8)
7271       MovDir[x][y] = old_move_dir;
7272     else if (can_turn_left && can_turn_right)
7273       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7274     else if (can_turn_left && rnd > rnd_value / 8)
7275       MovDir[x][y] = left_dir;
7276     else if (can_turn_right && rnd > rnd_value / 8)
7277       MovDir[x][y] = right_dir;
7278     else
7279       MovDir[x][y] = back_dir;
7280
7281     xx = x + move_xy[MovDir[x][y]].dx;
7282     yy = y + move_xy[MovDir[x][y]].dy;
7283
7284     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7285       MovDir[x][y] = old_move_dir;
7286
7287     MovDelay[x][y] = 0;
7288   }
7289   else if (element == EL_MOLE)
7290   {
7291     boolean can_move_on =
7292       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7293                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7294                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7295     if (!can_move_on)
7296     {
7297       boolean can_turn_left =
7298         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7299                               IS_AMOEBOID(Feld[left_x][left_y])));
7300
7301       boolean can_turn_right =
7302         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7303                               IS_AMOEBOID(Feld[right_x][right_y])));
7304
7305       if (can_turn_left && can_turn_right)
7306         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7307       else if (can_turn_left)
7308         MovDir[x][y] = left_dir;
7309       else
7310         MovDir[x][y] = right_dir;
7311     }
7312
7313     if (MovDir[x][y] != old_move_dir)
7314       MovDelay[x][y] = 9;
7315   }
7316   else if (element == EL_BALLOON)
7317   {
7318     MovDir[x][y] = game.wind_direction;
7319     MovDelay[x][y] = 0;
7320   }
7321   else if (element == EL_SPRING)
7322   {
7323 #if USE_NEW_SPRING_BUMPER
7324     if (MovDir[x][y] & MV_HORIZONTAL)
7325     {
7326       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7327           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7328       {
7329         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7330         ResetGfxAnimation(move_x, move_y);
7331         TEST_DrawLevelField(move_x, move_y);
7332
7333         MovDir[x][y] = back_dir;
7334       }
7335       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7336                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7337         MovDir[x][y] = MV_NONE;
7338     }
7339 #else
7340     if (MovDir[x][y] & MV_HORIZONTAL &&
7341         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7342          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7343       MovDir[x][y] = MV_NONE;
7344 #endif
7345
7346     MovDelay[x][y] = 0;
7347   }
7348   else if (element == EL_ROBOT ||
7349            element == EL_SATELLITE ||
7350            element == EL_PENGUIN ||
7351            element == EL_EMC_ANDROID)
7352   {
7353     int attr_x = -1, attr_y = -1;
7354
7355     if (AllPlayersGone)
7356     {
7357       attr_x = ExitX;
7358       attr_y = ExitY;
7359     }
7360     else
7361     {
7362       int i;
7363
7364       for (i = 0; i < MAX_PLAYERS; i++)
7365       {
7366         struct PlayerInfo *player = &stored_player[i];
7367         int jx = player->jx, jy = player->jy;
7368
7369         if (!player->active)
7370           continue;
7371
7372         if (attr_x == -1 ||
7373             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7374         {
7375           attr_x = jx;
7376           attr_y = jy;
7377         }
7378       }
7379     }
7380
7381     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7382         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7383          game.engine_version < VERSION_IDENT(3,1,0,0)))
7384     {
7385       attr_x = ZX;
7386       attr_y = ZY;
7387     }
7388
7389     if (element == EL_PENGUIN)
7390     {
7391       int i;
7392       static int xy[4][2] =
7393       {
7394         { 0, -1 },
7395         { -1, 0 },
7396         { +1, 0 },
7397         { 0, +1 }
7398       };
7399
7400       for (i = 0; i < NUM_DIRECTIONS; i++)
7401       {
7402         int ex = x + xy[i][0];
7403         int ey = y + xy[i][1];
7404
7405         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7406                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7407                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7408                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7409         {
7410           attr_x = ex;
7411           attr_y = ey;
7412           break;
7413         }
7414       }
7415     }
7416
7417     MovDir[x][y] = MV_NONE;
7418     if (attr_x < x)
7419       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7420     else if (attr_x > x)
7421       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7422     if (attr_y < y)
7423       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7424     else if (attr_y > y)
7425       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7426
7427     if (element == EL_ROBOT)
7428     {
7429       int newx, newy;
7430
7431       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7432         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7433       Moving2Blocked(x, y, &newx, &newy);
7434
7435       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7436         MovDelay[x][y] = 8 + 8 * !RND(3);
7437       else
7438         MovDelay[x][y] = 16;
7439     }
7440     else if (element == EL_PENGUIN)
7441     {
7442       int newx, newy;
7443
7444       MovDelay[x][y] = 1;
7445
7446       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7447       {
7448         boolean first_horiz = RND(2);
7449         int new_move_dir = MovDir[x][y];
7450
7451         MovDir[x][y] =
7452           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453         Moving2Blocked(x, y, &newx, &newy);
7454
7455         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7456           return;
7457
7458         MovDir[x][y] =
7459           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7460         Moving2Blocked(x, y, &newx, &newy);
7461
7462         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7463           return;
7464
7465         MovDir[x][y] = old_move_dir;
7466         return;
7467       }
7468     }
7469     else if (element == EL_SATELLITE)
7470     {
7471       int newx, newy;
7472
7473       MovDelay[x][y] = 1;
7474
7475       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7476       {
7477         boolean first_horiz = RND(2);
7478         int new_move_dir = MovDir[x][y];
7479
7480         MovDir[x][y] =
7481           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7482         Moving2Blocked(x, y, &newx, &newy);
7483
7484         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7485           return;
7486
7487         MovDir[x][y] =
7488           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489         Moving2Blocked(x, y, &newx, &newy);
7490
7491         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7492           return;
7493
7494         MovDir[x][y] = old_move_dir;
7495         return;
7496       }
7497     }
7498     else if (element == EL_EMC_ANDROID)
7499     {
7500       static int check_pos[16] =
7501       {
7502         -1,             /*  0 => (invalid)          */
7503         7,              /*  1 => MV_LEFT            */
7504         3,              /*  2 => MV_RIGHT           */
7505         -1,             /*  3 => (invalid)          */
7506         1,              /*  4 =>            MV_UP   */
7507         0,              /*  5 => MV_LEFT  | MV_UP   */
7508         2,              /*  6 => MV_RIGHT | MV_UP   */
7509         -1,             /*  7 => (invalid)          */
7510         5,              /*  8 =>            MV_DOWN */
7511         6,              /*  9 => MV_LEFT  | MV_DOWN */
7512         4,              /* 10 => MV_RIGHT | MV_DOWN */
7513         -1,             /* 11 => (invalid)          */
7514         -1,             /* 12 => (invalid)          */
7515         -1,             /* 13 => (invalid)          */
7516         -1,             /* 14 => (invalid)          */
7517         -1,             /* 15 => (invalid)          */
7518       };
7519       static struct
7520       {
7521         int dx, dy;
7522         int dir;
7523       } check_xy[8] =
7524       {
7525         { -1, -1,       MV_LEFT  | MV_UP   },
7526         {  0, -1,                  MV_UP   },
7527         { +1, -1,       MV_RIGHT | MV_UP   },
7528         { +1,  0,       MV_RIGHT           },
7529         { +1, +1,       MV_RIGHT | MV_DOWN },
7530         {  0, +1,                  MV_DOWN },
7531         { -1, +1,       MV_LEFT  | MV_DOWN },
7532         { -1,  0,       MV_LEFT            },
7533       };
7534       int start_pos, check_order;
7535       boolean can_clone = FALSE;
7536       int i;
7537
7538       /* check if there is any free field around current position */
7539       for (i = 0; i < 8; i++)
7540       {
7541         int newx = x + check_xy[i].dx;
7542         int newy = y + check_xy[i].dy;
7543
7544         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7545         {
7546           can_clone = TRUE;
7547
7548           break;
7549         }
7550       }
7551
7552       if (can_clone)            /* randomly find an element to clone */
7553       {
7554         can_clone = FALSE;
7555
7556         start_pos = check_pos[RND(8)];
7557         check_order = (RND(2) ? -1 : +1);
7558
7559         for (i = 0; i < 8; i++)
7560         {
7561           int pos_raw = start_pos + i * check_order;
7562           int pos = (pos_raw + 8) % 8;
7563           int newx = x + check_xy[pos].dx;
7564           int newy = y + check_xy[pos].dy;
7565
7566           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7567           {
7568             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7569             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7570
7571             Store[x][y] = Feld[newx][newy];
7572
7573             can_clone = TRUE;
7574
7575             break;
7576           }
7577         }
7578       }
7579
7580       if (can_clone)            /* randomly find a direction to move */
7581       {
7582         can_clone = FALSE;
7583
7584         start_pos = check_pos[RND(8)];
7585         check_order = (RND(2) ? -1 : +1);
7586
7587         for (i = 0; i < 8; i++)
7588         {
7589           int pos_raw = start_pos + i * check_order;
7590           int pos = (pos_raw + 8) % 8;
7591           int newx = x + check_xy[pos].dx;
7592           int newy = y + check_xy[pos].dy;
7593           int new_move_dir = check_xy[pos].dir;
7594
7595           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7596           {
7597             MovDir[x][y] = new_move_dir;
7598             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7599
7600             can_clone = TRUE;
7601
7602             break;
7603           }
7604         }
7605       }
7606
7607       if (can_clone)            /* cloning and moving successful */
7608         return;
7609
7610       /* cannot clone -- try to move towards player */
7611
7612       start_pos = check_pos[MovDir[x][y] & 0x0f];
7613       check_order = (RND(2) ? -1 : +1);
7614
7615       for (i = 0; i < 3; i++)
7616       {
7617         /* first check start_pos, then previous/next or (next/previous) pos */
7618         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7619         int pos = (pos_raw + 8) % 8;
7620         int newx = x + check_xy[pos].dx;
7621         int newy = y + check_xy[pos].dy;
7622         int new_move_dir = check_xy[pos].dir;
7623
7624         if (IS_PLAYER(newx, newy))
7625           break;
7626
7627         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7628         {
7629           MovDir[x][y] = new_move_dir;
7630           MovDelay[x][y] = level.android_move_time * 8 + 1;
7631
7632           break;
7633         }
7634       }
7635     }
7636   }
7637   else if (move_pattern == MV_TURNING_LEFT ||
7638            move_pattern == MV_TURNING_RIGHT ||
7639            move_pattern == MV_TURNING_LEFT_RIGHT ||
7640            move_pattern == MV_TURNING_RIGHT_LEFT ||
7641            move_pattern == MV_TURNING_RANDOM ||
7642            move_pattern == MV_ALL_DIRECTIONS)
7643   {
7644     boolean can_turn_left =
7645       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7646     boolean can_turn_right =
7647       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7648
7649     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7650       return;
7651
7652     if (move_pattern == MV_TURNING_LEFT)
7653       MovDir[x][y] = left_dir;
7654     else if (move_pattern == MV_TURNING_RIGHT)
7655       MovDir[x][y] = right_dir;
7656     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7657       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7658     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7659       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7660     else if (move_pattern == MV_TURNING_RANDOM)
7661       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7662                       can_turn_right && !can_turn_left ? right_dir :
7663                       RND(2) ? left_dir : right_dir);
7664     else if (can_turn_left && can_turn_right)
7665       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7666     else if (can_turn_left)
7667       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7668     else if (can_turn_right)
7669       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7670     else
7671       MovDir[x][y] = back_dir;
7672
7673     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674   }
7675   else if (move_pattern == MV_HORIZONTAL ||
7676            move_pattern == MV_VERTICAL)
7677   {
7678     if (move_pattern & old_move_dir)
7679       MovDir[x][y] = back_dir;
7680     else if (move_pattern == MV_HORIZONTAL)
7681       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7682     else if (move_pattern == MV_VERTICAL)
7683       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7684
7685     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7686   }
7687   else if (move_pattern & MV_ANY_DIRECTION)
7688   {
7689     MovDir[x][y] = move_pattern;
7690     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7691   }
7692   else if (move_pattern & MV_WIND_DIRECTION)
7693   {
7694     MovDir[x][y] = game.wind_direction;
7695     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7696   }
7697   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7698   {
7699     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7700       MovDir[x][y] = left_dir;
7701     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7702       MovDir[x][y] = right_dir;
7703
7704     if (MovDir[x][y] != old_move_dir)
7705       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7706   }
7707   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7708   {
7709     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7710       MovDir[x][y] = right_dir;
7711     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7712       MovDir[x][y] = left_dir;
7713
7714     if (MovDir[x][y] != old_move_dir)
7715       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7716   }
7717   else if (move_pattern == MV_TOWARDS_PLAYER ||
7718            move_pattern == MV_AWAY_FROM_PLAYER)
7719   {
7720     int attr_x = -1, attr_y = -1;
7721     int newx, newy;
7722     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7723
7724     if (AllPlayersGone)
7725     {
7726       attr_x = ExitX;
7727       attr_y = ExitY;
7728     }
7729     else
7730     {
7731       int i;
7732
7733       for (i = 0; i < MAX_PLAYERS; i++)
7734       {
7735         struct PlayerInfo *player = &stored_player[i];
7736         int jx = player->jx, jy = player->jy;
7737
7738         if (!player->active)
7739           continue;
7740
7741         if (attr_x == -1 ||
7742             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7743         {
7744           attr_x = jx;
7745           attr_y = jy;
7746         }
7747       }
7748     }
7749
7750     MovDir[x][y] = MV_NONE;
7751     if (attr_x < x)
7752       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7753     else if (attr_x > x)
7754       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7755     if (attr_y < y)
7756       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7757     else if (attr_y > y)
7758       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7759
7760     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7761
7762     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7763     {
7764       boolean first_horiz = RND(2);
7765       int new_move_dir = MovDir[x][y];
7766
7767       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7768       {
7769         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7770         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7771
7772         return;
7773       }
7774
7775       MovDir[x][y] =
7776         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7777       Moving2Blocked(x, y, &newx, &newy);
7778
7779       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7780         return;
7781
7782       MovDir[x][y] =
7783         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7784       Moving2Blocked(x, y, &newx, &newy);
7785
7786       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7787         return;
7788
7789       MovDir[x][y] = old_move_dir;
7790     }
7791   }
7792   else if (move_pattern == MV_WHEN_PUSHED ||
7793            move_pattern == MV_WHEN_DROPPED)
7794   {
7795     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796       MovDir[x][y] = MV_NONE;
7797
7798     MovDelay[x][y] = 0;
7799   }
7800   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7801   {
7802     static int test_xy[7][2] =
7803     {
7804       { 0, -1 },
7805       { -1, 0 },
7806       { +1, 0 },
7807       { 0, +1 },
7808       { 0, -1 },
7809       { -1, 0 },
7810       { +1, 0 },
7811     };
7812     static int test_dir[7] =
7813     {
7814       MV_UP,
7815       MV_LEFT,
7816       MV_RIGHT,
7817       MV_DOWN,
7818       MV_UP,
7819       MV_LEFT,
7820       MV_RIGHT,
7821     };
7822     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7823     int move_preference = -1000000;     /* start with very low preference */
7824     int new_move_dir = MV_NONE;
7825     int start_test = RND(4);
7826     int i;
7827
7828     for (i = 0; i < NUM_DIRECTIONS; i++)
7829     {
7830       int move_dir = test_dir[start_test + i];
7831       int move_dir_preference;
7832
7833       xx = x + test_xy[start_test + i][0];
7834       yy = y + test_xy[start_test + i][1];
7835
7836       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7837           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7838       {
7839         new_move_dir = move_dir;
7840
7841         break;
7842       }
7843
7844       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7845         continue;
7846
7847       move_dir_preference = -1 * RunnerVisit[xx][yy];
7848       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7849         move_dir_preference = PlayerVisit[xx][yy];
7850
7851       if (move_dir_preference > move_preference)
7852       {
7853         /* prefer field that has not been visited for the longest time */
7854         move_preference = move_dir_preference;
7855         new_move_dir = move_dir;
7856       }
7857       else if (move_dir_preference == move_preference &&
7858                move_dir == old_move_dir)
7859       {
7860         /* prefer last direction when all directions are preferred equally */
7861         move_preference = move_dir_preference;
7862         new_move_dir = move_dir;
7863       }
7864     }
7865
7866     MovDir[x][y] = new_move_dir;
7867     if (old_move_dir != new_move_dir)
7868       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7869   }
7870 }
7871
7872 static void TurnRound(int x, int y)
7873 {
7874   int direction = MovDir[x][y];
7875
7876   TurnRoundExt(x, y);
7877
7878   GfxDir[x][y] = MovDir[x][y];
7879
7880   if (direction != MovDir[x][y])
7881     GfxFrame[x][y] = 0;
7882
7883   if (MovDelay[x][y])
7884     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7885
7886   ResetGfxFrame(x, y, FALSE);
7887 }
7888
7889 static boolean JustBeingPushed(int x, int y)
7890 {
7891   int i;
7892
7893   for (i = 0; i < MAX_PLAYERS; i++)
7894   {
7895     struct PlayerInfo *player = &stored_player[i];
7896
7897     if (player->active && player->is_pushing && player->MovPos)
7898     {
7899       int next_jx = player->jx + (player->jx - player->last_jx);
7900       int next_jy = player->jy + (player->jy - player->last_jy);
7901
7902       if (x == next_jx && y == next_jy)
7903         return TRUE;
7904     }
7905   }
7906
7907   return FALSE;
7908 }
7909
7910 void StartMoving(int x, int y)
7911 {
7912   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7913   int element = Feld[x][y];
7914
7915   if (Stop[x][y])
7916     return;
7917
7918   if (MovDelay[x][y] == 0)
7919     GfxAction[x][y] = ACTION_DEFAULT;
7920
7921   if (CAN_FALL(element) && y < lev_fieldy - 1)
7922   {
7923     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7924         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7925       if (JustBeingPushed(x, y))
7926         return;
7927
7928     if (element == EL_QUICKSAND_FULL)
7929     {
7930       if (IS_FREE(x, y + 1))
7931       {
7932         InitMovingField(x, y, MV_DOWN);
7933         started_moving = TRUE;
7934
7935         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7936 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7937         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7938           Store[x][y] = EL_ROCK;
7939 #else
7940         Store[x][y] = EL_ROCK;
7941 #endif
7942
7943         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7944       }
7945       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7946       {
7947         if (!MovDelay[x][y])
7948         {
7949           MovDelay[x][y] = TILEY + 1;
7950
7951           ResetGfxAnimation(x, y);
7952           ResetGfxAnimation(x, y + 1);
7953         }
7954
7955         if (MovDelay[x][y])
7956         {
7957           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7958           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7959
7960           MovDelay[x][y]--;
7961           if (MovDelay[x][y])
7962             return;
7963         }
7964
7965         Feld[x][y] = EL_QUICKSAND_EMPTY;
7966         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7967         Store[x][y + 1] = Store[x][y];
7968         Store[x][y] = 0;
7969
7970         PlayLevelSoundAction(x, y, ACTION_FILLING);
7971       }
7972       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7973       {
7974         if (!MovDelay[x][y])
7975         {
7976           MovDelay[x][y] = TILEY + 1;
7977
7978           ResetGfxAnimation(x, y);
7979           ResetGfxAnimation(x, y + 1);
7980         }
7981
7982         if (MovDelay[x][y])
7983         {
7984           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7985           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7986
7987           MovDelay[x][y]--;
7988           if (MovDelay[x][y])
7989             return;
7990         }
7991
7992         Feld[x][y] = EL_QUICKSAND_EMPTY;
7993         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7994         Store[x][y + 1] = Store[x][y];
7995         Store[x][y] = 0;
7996
7997         PlayLevelSoundAction(x, y, ACTION_FILLING);
7998       }
7999     }
8000     else if (element == EL_QUICKSAND_FAST_FULL)
8001     {
8002       if (IS_FREE(x, y + 1))
8003       {
8004         InitMovingField(x, y, MV_DOWN);
8005         started_moving = TRUE;
8006
8007         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8008 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8009         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8010           Store[x][y] = EL_ROCK;
8011 #else
8012         Store[x][y] = EL_ROCK;
8013 #endif
8014
8015         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8016       }
8017       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8018       {
8019         if (!MovDelay[x][y])
8020         {
8021           MovDelay[x][y] = TILEY + 1;
8022
8023           ResetGfxAnimation(x, y);
8024           ResetGfxAnimation(x, y + 1);
8025         }
8026
8027         if (MovDelay[x][y])
8028         {
8029           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8030           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8031
8032           MovDelay[x][y]--;
8033           if (MovDelay[x][y])
8034             return;
8035         }
8036
8037         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8038         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8039         Store[x][y + 1] = Store[x][y];
8040         Store[x][y] = 0;
8041
8042         PlayLevelSoundAction(x, y, ACTION_FILLING);
8043       }
8044       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8045       {
8046         if (!MovDelay[x][y])
8047         {
8048           MovDelay[x][y] = TILEY + 1;
8049
8050           ResetGfxAnimation(x, y);
8051           ResetGfxAnimation(x, y + 1);
8052         }
8053
8054         if (MovDelay[x][y])
8055         {
8056           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8057           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8058
8059           MovDelay[x][y]--;
8060           if (MovDelay[x][y])
8061             return;
8062         }
8063
8064         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8065         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8066         Store[x][y + 1] = Store[x][y];
8067         Store[x][y] = 0;
8068
8069         PlayLevelSoundAction(x, y, ACTION_FILLING);
8070       }
8071     }
8072     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8073              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8074     {
8075       InitMovingField(x, y, MV_DOWN);
8076       started_moving = TRUE;
8077
8078       Feld[x][y] = EL_QUICKSAND_FILLING;
8079       Store[x][y] = element;
8080
8081       PlayLevelSoundAction(x, y, ACTION_FILLING);
8082     }
8083     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8085     {
8086       InitMovingField(x, y, MV_DOWN);
8087       started_moving = TRUE;
8088
8089       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8090       Store[x][y] = element;
8091
8092       PlayLevelSoundAction(x, y, ACTION_FILLING);
8093     }
8094     else if (element == EL_MAGIC_WALL_FULL)
8095     {
8096       if (IS_FREE(x, y + 1))
8097       {
8098         InitMovingField(x, y, MV_DOWN);
8099         started_moving = TRUE;
8100
8101         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8102         Store[x][y] = EL_CHANGED(Store[x][y]);
8103       }
8104       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8105       {
8106         if (!MovDelay[x][y])
8107           MovDelay[x][y] = TILEY / 4 + 1;
8108
8109         if (MovDelay[x][y])
8110         {
8111           MovDelay[x][y]--;
8112           if (MovDelay[x][y])
8113             return;
8114         }
8115
8116         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8117         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8118         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8119         Store[x][y] = 0;
8120       }
8121     }
8122     else if (element == EL_BD_MAGIC_WALL_FULL)
8123     {
8124       if (IS_FREE(x, y + 1))
8125       {
8126         InitMovingField(x, y, MV_DOWN);
8127         started_moving = TRUE;
8128
8129         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8130         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8131       }
8132       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8133       {
8134         if (!MovDelay[x][y])
8135           MovDelay[x][y] = TILEY / 4 + 1;
8136
8137         if (MovDelay[x][y])
8138         {
8139           MovDelay[x][y]--;
8140           if (MovDelay[x][y])
8141             return;
8142         }
8143
8144         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8145         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8146         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8147         Store[x][y] = 0;
8148       }
8149     }
8150     else if (element == EL_DC_MAGIC_WALL_FULL)
8151     {
8152       if (IS_FREE(x, y + 1))
8153       {
8154         InitMovingField(x, y, MV_DOWN);
8155         started_moving = TRUE;
8156
8157         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8158         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8159       }
8160       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8161       {
8162         if (!MovDelay[x][y])
8163           MovDelay[x][y] = TILEY / 4 + 1;
8164
8165         if (MovDelay[x][y])
8166         {
8167           MovDelay[x][y]--;
8168           if (MovDelay[x][y])
8169             return;
8170         }
8171
8172         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8173         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8174         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8175         Store[x][y] = 0;
8176       }
8177     }
8178     else if ((CAN_PASS_MAGIC_WALL(element) &&
8179               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8180                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8181              (CAN_PASS_DC_MAGIC_WALL(element) &&
8182               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8183
8184     {
8185       InitMovingField(x, y, MV_DOWN);
8186       started_moving = TRUE;
8187
8188       Feld[x][y] =
8189         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8190          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8191          EL_DC_MAGIC_WALL_FILLING);
8192       Store[x][y] = element;
8193     }
8194     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8195     {
8196       SplashAcid(x, y + 1);
8197
8198       InitMovingField(x, y, MV_DOWN);
8199       started_moving = TRUE;
8200
8201       Store[x][y] = EL_ACID;
8202     }
8203     else if (
8204 #if USE_FIX_IMPACT_COLLISION
8205              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8206               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8207 #else
8208              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8209               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8210 #endif
8211              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8212               CAN_FALL(element) && WasJustFalling[x][y] &&
8213               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8214
8215              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8216               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8217               (Feld[x][y + 1] == EL_BLOCKED)))
8218     {
8219       /* this is needed for a special case not covered by calling "Impact()"
8220          from "ContinueMoving()": if an element moves to a tile directly below
8221          another element which was just falling on that tile (which was empty
8222          in the previous frame), the falling element above would just stop
8223          instead of smashing the element below (in previous version, the above
8224          element was just checked for "moving" instead of "falling", resulting
8225          in incorrect smashes caused by horizontal movement of the above
8226          element; also, the case of the player being the element to smash was
8227          simply not covered here... :-/ ) */
8228
8229       CheckCollision[x][y] = 0;
8230       CheckImpact[x][y] = 0;
8231
8232       Impact(x, y);
8233     }
8234     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8235     {
8236       if (MovDir[x][y] == MV_NONE)
8237       {
8238         InitMovingField(x, y, MV_DOWN);
8239         started_moving = TRUE;
8240       }
8241     }
8242     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8243     {
8244       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8245         MovDir[x][y] = MV_DOWN;
8246
8247       InitMovingField(x, y, MV_DOWN);
8248       started_moving = TRUE;
8249     }
8250     else if (element == EL_AMOEBA_DROP)
8251     {
8252       Feld[x][y] = EL_AMOEBA_GROWING;
8253       Store[x][y] = EL_AMOEBA_WET;
8254     }
8255     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8256               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8257              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8258              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8259     {
8260       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8261                                 (IS_FREE(x - 1, y + 1) ||
8262                                  Feld[x - 1][y + 1] == EL_ACID));
8263       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8264                                 (IS_FREE(x + 1, y + 1) ||
8265                                  Feld[x + 1][y + 1] == EL_ACID));
8266       boolean can_fall_any  = (can_fall_left || can_fall_right);
8267       boolean can_fall_both = (can_fall_left && can_fall_right);
8268       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8269
8270 #if USE_NEW_ALL_SLIPPERY
8271       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8272       {
8273         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8274           can_fall_right = FALSE;
8275         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8276           can_fall_left = FALSE;
8277         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8278           can_fall_right = FALSE;
8279         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8280           can_fall_left = FALSE;
8281
8282         can_fall_any  = (can_fall_left || can_fall_right);
8283         can_fall_both = FALSE;
8284       }
8285 #else
8286       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8287       {
8288         if (slippery_type == SLIPPERY_ONLY_LEFT)
8289           can_fall_right = FALSE;
8290         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291           can_fall_left = FALSE;
8292         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8293           can_fall_right = FALSE;
8294         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8295           can_fall_left = FALSE;
8296
8297         can_fall_any  = (can_fall_left || can_fall_right);
8298         can_fall_both = (can_fall_left && can_fall_right);
8299       }
8300 #endif
8301
8302 #if USE_NEW_ALL_SLIPPERY
8303 #else
8304 #if USE_NEW_SP_SLIPPERY
8305       /* !!! better use the same properties as for custom elements here !!! */
8306       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8307                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8308       {
8309         can_fall_right = FALSE;         /* slip down on left side */
8310         can_fall_both = FALSE;
8311       }
8312 #endif
8313 #endif
8314
8315 #if USE_NEW_ALL_SLIPPERY
8316       if (can_fall_both)
8317       {
8318         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8319           can_fall_right = FALSE;       /* slip down on left side */
8320         else
8321           can_fall_left = !(can_fall_right = RND(2));
8322
8323         can_fall_both = FALSE;
8324       }
8325 #else
8326       if (can_fall_both)
8327       {
8328         if (game.emulation == EMU_BOULDERDASH ||
8329             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330           can_fall_right = FALSE;       /* slip down on left side */
8331         else
8332           can_fall_left = !(can_fall_right = RND(2));
8333
8334         can_fall_both = FALSE;
8335       }
8336 #endif
8337
8338       if (can_fall_any)
8339       {
8340         /* if not determined otherwise, prefer left side for slipping down */
8341         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8342         started_moving = TRUE;
8343       }
8344     }
8345 #if 0
8346     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8347 #else
8348     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8349 #endif
8350     {
8351       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8352       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8353       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8354       int belt_dir = game.belt_dir[belt_nr];
8355
8356       if ((belt_dir == MV_LEFT  && left_is_free) ||
8357           (belt_dir == MV_RIGHT && right_is_free))
8358       {
8359         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8360
8361         InitMovingField(x, y, belt_dir);
8362         started_moving = TRUE;
8363
8364         Pushed[x][y] = TRUE;
8365         Pushed[nextx][y] = TRUE;
8366
8367         GfxAction[x][y] = ACTION_DEFAULT;
8368       }
8369       else
8370       {
8371         MovDir[x][y] = 0;       /* if element was moving, stop it */
8372       }
8373     }
8374   }
8375
8376   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8377 #if 0
8378   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8379 #else
8380   if (CAN_MOVE(element) && !started_moving)
8381 #endif
8382   {
8383     int move_pattern = element_info[element].move_pattern;
8384     int newx, newy;
8385
8386 #if 0
8387 #if DEBUG
8388     if (MovDir[x][y] == MV_NONE)
8389     {
8390       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8391              x, y, element, element_info[element].token_name);
8392       printf("StartMoving(): This should never happen!\n");
8393     }
8394 #endif
8395 #endif
8396
8397     Moving2Blocked(x, y, &newx, &newy);
8398
8399     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8400       return;
8401
8402     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8403         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8404     {
8405       WasJustMoving[x][y] = 0;
8406       CheckCollision[x][y] = 0;
8407
8408       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8409
8410       if (Feld[x][y] != element)        /* element has changed */
8411         return;
8412     }
8413
8414     if (!MovDelay[x][y])        /* start new movement phase */
8415     {
8416       /* all objects that can change their move direction after each step
8417          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8418
8419       if (element != EL_YAMYAM &&
8420           element != EL_DARK_YAMYAM &&
8421           element != EL_PACMAN &&
8422           !(move_pattern & MV_ANY_DIRECTION) &&
8423           move_pattern != MV_TURNING_LEFT &&
8424           move_pattern != MV_TURNING_RIGHT &&
8425           move_pattern != MV_TURNING_LEFT_RIGHT &&
8426           move_pattern != MV_TURNING_RIGHT_LEFT &&
8427           move_pattern != MV_TURNING_RANDOM)
8428       {
8429         TurnRound(x, y);
8430
8431         if (MovDelay[x][y] && (element == EL_BUG ||
8432                                element == EL_SPACESHIP ||
8433                                element == EL_SP_SNIKSNAK ||
8434                                element == EL_SP_ELECTRON ||
8435                                element == EL_MOLE))
8436           TEST_DrawLevelField(x, y);
8437       }
8438     }
8439
8440     if (MovDelay[x][y])         /* wait some time before next movement */
8441     {
8442       MovDelay[x][y]--;
8443
8444       if (element == EL_ROBOT ||
8445           element == EL_YAMYAM ||
8446           element == EL_DARK_YAMYAM)
8447       {
8448         DrawLevelElementAnimationIfNeeded(x, y, element);
8449         PlayLevelSoundAction(x, y, ACTION_WAITING);
8450       }
8451       else if (element == EL_SP_ELECTRON)
8452         DrawLevelElementAnimationIfNeeded(x, y, element);
8453       else if (element == EL_DRAGON)
8454       {
8455         int i;
8456         int dir = MovDir[x][y];
8457         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8458         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8459         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8460                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8461                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8462                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8463         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8464
8465         GfxAction[x][y] = ACTION_ATTACKING;
8466
8467         if (IS_PLAYER(x, y))
8468           DrawPlayerField(x, y);
8469         else
8470           TEST_DrawLevelField(x, y);
8471
8472         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8473
8474         for (i = 1; i <= 3; i++)
8475         {
8476           int xx = x + i * dx;
8477           int yy = y + i * dy;
8478           int sx = SCREENX(xx);
8479           int sy = SCREENY(yy);
8480           int flame_graphic = graphic + (i - 1);
8481
8482           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8483             break;
8484
8485           if (MovDelay[x][y])
8486           {
8487             int flamed = MovingOrBlocked2Element(xx, yy);
8488
8489             /* !!! */
8490 #if 0
8491             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8492               Bang(xx, yy);
8493             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8494               RemoveMovingField(xx, yy);
8495             else
8496               RemoveField(xx, yy);
8497 #else
8498             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8499               Bang(xx, yy);
8500             else
8501               RemoveMovingField(xx, yy);
8502 #endif
8503
8504             ChangeDelay[xx][yy] = 0;
8505
8506             Feld[xx][yy] = EL_FLAMES;
8507
8508             if (IN_SCR_FIELD(sx, sy))
8509             {
8510               TEST_DrawLevelFieldCrumbled(xx, yy);
8511               DrawGraphic(sx, sy, flame_graphic, frame);
8512             }
8513           }
8514           else
8515           {
8516             if (Feld[xx][yy] == EL_FLAMES)
8517               Feld[xx][yy] = EL_EMPTY;
8518             TEST_DrawLevelField(xx, yy);
8519           }
8520         }
8521       }
8522
8523       if (MovDelay[x][y])       /* element still has to wait some time */
8524       {
8525         PlayLevelSoundAction(x, y, ACTION_WAITING);
8526
8527         return;
8528       }
8529     }
8530
8531     /* now make next step */
8532
8533     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8534
8535     if (DONT_COLLIDE_WITH(element) &&
8536         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8537         !PLAYER_ENEMY_PROTECTED(newx, newy))
8538     {
8539       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8540
8541       return;
8542     }
8543
8544     else if (CAN_MOVE_INTO_ACID(element) &&
8545              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8546              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8547              (MovDir[x][y] == MV_DOWN ||
8548               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8549     {
8550       SplashAcid(newx, newy);
8551       Store[x][y] = EL_ACID;
8552     }
8553     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8554     {
8555       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8556           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8557           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8558           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8559       {
8560         RemoveField(x, y);
8561         TEST_DrawLevelField(x, y);
8562
8563         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8564         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8565           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8566
8567         local_player->friends_still_needed--;
8568         if (!local_player->friends_still_needed &&
8569             !local_player->GameOver && AllPlayersGone)
8570           PlayerWins(local_player);
8571
8572         return;
8573       }
8574       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8575       {
8576         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8577           TEST_DrawLevelField(newx, newy);
8578         else
8579           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8580       }
8581       else if (!IS_FREE(newx, newy))
8582       {
8583         GfxAction[x][y] = ACTION_WAITING;
8584
8585         if (IS_PLAYER(x, y))
8586           DrawPlayerField(x, y);
8587         else
8588           TEST_DrawLevelField(x, y);
8589
8590         return;
8591       }
8592     }
8593     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8594     {
8595       if (IS_FOOD_PIG(Feld[newx][newy]))
8596       {
8597         if (IS_MOVING(newx, newy))
8598           RemoveMovingField(newx, newy);
8599         else
8600         {
8601           Feld[newx][newy] = EL_EMPTY;
8602           TEST_DrawLevelField(newx, newy);
8603         }
8604
8605         PlayLevelSound(x, y, SND_PIG_DIGGING);
8606       }
8607       else if (!IS_FREE(newx, newy))
8608       {
8609         if (IS_PLAYER(x, y))
8610           DrawPlayerField(x, y);
8611         else
8612           TEST_DrawLevelField(x, y);
8613
8614         return;
8615       }
8616     }
8617     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8618     {
8619       if (Store[x][y] != EL_EMPTY)
8620       {
8621         boolean can_clone = FALSE;
8622         int xx, yy;
8623
8624         /* check if element to clone is still there */
8625         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8626         {
8627           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8628           {
8629             can_clone = TRUE;
8630
8631             break;
8632           }
8633         }
8634
8635         /* cannot clone or target field not free anymore -- do not clone */
8636         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8637           Store[x][y] = EL_EMPTY;
8638       }
8639
8640       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8641       {
8642         if (IS_MV_DIAGONAL(MovDir[x][y]))
8643         {
8644           int diagonal_move_dir = MovDir[x][y];
8645           int stored = Store[x][y];
8646           int change_delay = 8;
8647           int graphic;
8648
8649           /* android is moving diagonally */
8650
8651           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8652
8653           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8654           GfxElement[x][y] = EL_EMC_ANDROID;
8655           GfxAction[x][y] = ACTION_SHRINKING;
8656           GfxDir[x][y] = diagonal_move_dir;
8657           ChangeDelay[x][y] = change_delay;
8658
8659           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8660                                    GfxDir[x][y]);
8661
8662           DrawLevelGraphicAnimation(x, y, graphic);
8663           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8664
8665           if (Feld[newx][newy] == EL_ACID)
8666           {
8667             SplashAcid(newx, newy);
8668
8669             return;
8670           }
8671
8672           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8673
8674           Store[newx][newy] = EL_EMC_ANDROID;
8675           GfxElement[newx][newy] = EL_EMC_ANDROID;
8676           GfxAction[newx][newy] = ACTION_GROWING;
8677           GfxDir[newx][newy] = diagonal_move_dir;
8678           ChangeDelay[newx][newy] = change_delay;
8679
8680           graphic = el_act_dir2img(GfxElement[newx][newy],
8681                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8682
8683           DrawLevelGraphicAnimation(newx, newy, graphic);
8684           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8685
8686           return;
8687         }
8688         else
8689         {
8690           Feld[newx][newy] = EL_EMPTY;
8691           TEST_DrawLevelField(newx, newy);
8692
8693           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8694         }
8695       }
8696       else if (!IS_FREE(newx, newy))
8697       {
8698 #if 0
8699         if (IS_PLAYER(x, y))
8700           DrawPlayerField(x, y);
8701         else
8702           TEST_DrawLevelField(x, y);
8703 #endif
8704
8705         return;
8706       }
8707     }
8708     else if (IS_CUSTOM_ELEMENT(element) &&
8709              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8710     {
8711 #if 1
8712       if (!DigFieldByCE(newx, newy, element))
8713         return;
8714 #else
8715       int new_element = Feld[newx][newy];
8716
8717       if (!IS_FREE(newx, newy))
8718       {
8719         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8720                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8721                       ACTION_BREAKING);
8722
8723         /* no element can dig solid indestructible elements */
8724         if (IS_INDESTRUCTIBLE(new_element) &&
8725             !IS_DIGGABLE(new_element) &&
8726             !IS_COLLECTIBLE(new_element))
8727           return;
8728
8729         if (AmoebaNr[newx][newy] &&
8730             (new_element == EL_AMOEBA_FULL ||
8731              new_element == EL_BD_AMOEBA ||
8732              new_element == EL_AMOEBA_GROWING))
8733         {
8734           AmoebaCnt[AmoebaNr[newx][newy]]--;
8735           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8736         }
8737
8738         if (IS_MOVING(newx, newy))
8739           RemoveMovingField(newx, newy);
8740         else
8741         {
8742           RemoveField(newx, newy);
8743           TEST_DrawLevelField(newx, newy);
8744         }
8745
8746         /* if digged element was about to explode, prevent the explosion */
8747         ExplodeField[newx][newy] = EX_TYPE_NONE;
8748
8749         PlayLevelSoundAction(x, y, action);
8750       }
8751
8752       Store[newx][newy] = EL_EMPTY;
8753
8754 #if 1
8755       /* this makes it possible to leave the removed element again */
8756       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8757         Store[newx][newy] = new_element;
8758 #else
8759       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8760       {
8761         int move_leave_element = element_info[element].move_leave_element;
8762
8763         /* this makes it possible to leave the removed element again */
8764         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8765                              new_element : move_leave_element);
8766       }
8767 #endif
8768
8769 #endif
8770
8771       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8772       {
8773         RunnerVisit[x][y] = FrameCounter;
8774         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8775       }
8776     }
8777     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8778     {
8779       if (!IS_FREE(newx, newy))
8780       {
8781         if (IS_PLAYER(x, y))
8782           DrawPlayerField(x, y);
8783         else
8784           TEST_DrawLevelField(x, y);
8785
8786         return;
8787       }
8788       else
8789       {
8790         boolean wanna_flame = !RND(10);
8791         int dx = newx - x, dy = newy - y;
8792         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8793         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8794         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8795                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8796         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8797                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8798
8799         if ((wanna_flame ||
8800              IS_CLASSIC_ENEMY(element1) ||
8801              IS_CLASSIC_ENEMY(element2)) &&
8802             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8803             element1 != EL_FLAMES && element2 != EL_FLAMES)
8804         {
8805           ResetGfxAnimation(x, y);
8806           GfxAction[x][y] = ACTION_ATTACKING;
8807
8808           if (IS_PLAYER(x, y))
8809             DrawPlayerField(x, y);
8810           else
8811             TEST_DrawLevelField(x, y);
8812
8813           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8814
8815           MovDelay[x][y] = 50;
8816
8817           /* !!! */
8818 #if 0
8819           RemoveField(newx, newy);
8820 #endif
8821           Feld[newx][newy] = EL_FLAMES;
8822           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8823           {
8824 #if 0
8825             RemoveField(newx1, newy1);
8826 #endif
8827             Feld[newx1][newy1] = EL_FLAMES;
8828           }
8829           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8830           {
8831 #if 0
8832             RemoveField(newx2, newy2);
8833 #endif
8834             Feld[newx2][newy2] = EL_FLAMES;
8835           }
8836
8837           return;
8838         }
8839       }
8840     }
8841     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8842              Feld[newx][newy] == EL_DIAMOND)
8843     {
8844       if (IS_MOVING(newx, newy))
8845         RemoveMovingField(newx, newy);
8846       else
8847       {
8848         Feld[newx][newy] = EL_EMPTY;
8849         TEST_DrawLevelField(newx, newy);
8850       }
8851
8852       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8853     }
8854     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8855              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8856     {
8857       if (AmoebaNr[newx][newy])
8858       {
8859         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8860         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8861             Feld[newx][newy] == EL_BD_AMOEBA)
8862           AmoebaCnt[AmoebaNr[newx][newy]]--;
8863       }
8864
8865 #if 0
8866       /* !!! test !!! */
8867       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8868       {
8869         RemoveMovingField(newx, newy);
8870       }
8871 #else
8872       if (IS_MOVING(newx, newy))
8873       {
8874         RemoveMovingField(newx, newy);
8875       }
8876 #endif
8877       else
8878       {
8879         Feld[newx][newy] = EL_EMPTY;
8880         TEST_DrawLevelField(newx, newy);
8881       }
8882
8883       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8884     }
8885     else if ((element == EL_PACMAN || element == EL_MOLE)
8886              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8887     {
8888       if (AmoebaNr[newx][newy])
8889       {
8890         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8891         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8892             Feld[newx][newy] == EL_BD_AMOEBA)
8893           AmoebaCnt[AmoebaNr[newx][newy]]--;
8894       }
8895
8896       if (element == EL_MOLE)
8897       {
8898         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8899         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8900
8901         ResetGfxAnimation(x, y);
8902         GfxAction[x][y] = ACTION_DIGGING;
8903         TEST_DrawLevelField(x, y);
8904
8905         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8906
8907         return;                         /* wait for shrinking amoeba */
8908       }
8909       else      /* element == EL_PACMAN */
8910       {
8911         Feld[newx][newy] = EL_EMPTY;
8912         TEST_DrawLevelField(newx, newy);
8913         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8914       }
8915     }
8916     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8917              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8918               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8919     {
8920       /* wait for shrinking amoeba to completely disappear */
8921       return;
8922     }
8923     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8924     {
8925       /* object was running against a wall */
8926
8927       TurnRound(x, y);
8928
8929 #if 0
8930       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8931       if (move_pattern & MV_ANY_DIRECTION &&
8932           move_pattern == MovDir[x][y])
8933       {
8934         int blocking_element =
8935           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8936
8937         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8938                                  MovDir[x][y]);
8939
8940         element = Feld[x][y];   /* element might have changed */
8941       }
8942 #endif
8943
8944       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8945         DrawLevelElementAnimation(x, y, element);
8946
8947       if (DONT_TOUCH(element))
8948         TestIfBadThingTouchesPlayer(x, y);
8949
8950       return;
8951     }
8952
8953     InitMovingField(x, y, MovDir[x][y]);
8954
8955     PlayLevelSoundAction(x, y, ACTION_MOVING);
8956   }
8957
8958   if (MovDir[x][y])
8959     ContinueMoving(x, y);
8960 }
8961
8962 void ContinueMoving(int x, int y)
8963 {
8964   int element = Feld[x][y];
8965   struct ElementInfo *ei = &element_info[element];
8966   int direction = MovDir[x][y];
8967   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8968   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8969   int newx = x + dx, newy = y + dy;
8970   int stored = Store[x][y];
8971   int stored_new = Store[newx][newy];
8972   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8973   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8974   boolean last_line = (newy == lev_fieldy - 1);
8975
8976   MovPos[x][y] += getElementMoveStepsize(x, y);
8977
8978   if (pushed_by_player) /* special case: moving object pushed by player */
8979     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8980
8981   if (ABS(MovPos[x][y]) < TILEX)
8982   {
8983 #if 0
8984     int ee = Feld[x][y];
8985     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8986     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8987
8988     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8989            x, y, ABS(MovPos[x][y]),
8990            ee, gg, ff,
8991            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8992 #endif
8993
8994     TEST_DrawLevelField(x, y);
8995
8996     return;     /* element is still moving */
8997   }
8998
8999   /* element reached destination field */
9000
9001   Feld[x][y] = EL_EMPTY;
9002   Feld[newx][newy] = element;
9003   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9004
9005   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9006   {
9007     element = Feld[newx][newy] = EL_ACID;
9008   }
9009   else if (element == EL_MOLE)
9010   {
9011     Feld[x][y] = EL_SAND;
9012
9013     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9014   }
9015   else if (element == EL_QUICKSAND_FILLING)
9016   {
9017     element = Feld[newx][newy] = get_next_element(element);
9018     Store[newx][newy] = Store[x][y];
9019   }
9020   else if (element == EL_QUICKSAND_EMPTYING)
9021   {
9022     Feld[x][y] = get_next_element(element);
9023     element = Feld[newx][newy] = Store[x][y];
9024   }
9025   else if (element == EL_QUICKSAND_FAST_FILLING)
9026   {
9027     element = Feld[newx][newy] = get_next_element(element);
9028     Store[newx][newy] = Store[x][y];
9029   }
9030   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9031   {
9032     Feld[x][y] = get_next_element(element);
9033     element = Feld[newx][newy] = Store[x][y];
9034   }
9035   else if (element == EL_MAGIC_WALL_FILLING)
9036   {
9037     element = Feld[newx][newy] = get_next_element(element);
9038     if (!game.magic_wall_active)
9039       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9040     Store[newx][newy] = Store[x][y];
9041   }
9042   else if (element == EL_MAGIC_WALL_EMPTYING)
9043   {
9044     Feld[x][y] = get_next_element(element);
9045     if (!game.magic_wall_active)
9046       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9047     element = Feld[newx][newy] = Store[x][y];
9048
9049 #if USE_NEW_CUSTOM_VALUE
9050     InitField(newx, newy, FALSE);
9051 #endif
9052   }
9053   else if (element == EL_BD_MAGIC_WALL_FILLING)
9054   {
9055     element = Feld[newx][newy] = get_next_element(element);
9056     if (!game.magic_wall_active)
9057       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9058     Store[newx][newy] = Store[x][y];
9059   }
9060   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9061   {
9062     Feld[x][y] = get_next_element(element);
9063     if (!game.magic_wall_active)
9064       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9065     element = Feld[newx][newy] = Store[x][y];
9066
9067 #if USE_NEW_CUSTOM_VALUE
9068     InitField(newx, newy, FALSE);
9069 #endif
9070   }
9071   else if (element == EL_DC_MAGIC_WALL_FILLING)
9072   {
9073     element = Feld[newx][newy] = get_next_element(element);
9074     if (!game.magic_wall_active)
9075       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9076     Store[newx][newy] = Store[x][y];
9077   }
9078   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9079   {
9080     Feld[x][y] = get_next_element(element);
9081     if (!game.magic_wall_active)
9082       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9083     element = Feld[newx][newy] = Store[x][y];
9084
9085 #if USE_NEW_CUSTOM_VALUE
9086     InitField(newx, newy, FALSE);
9087 #endif
9088   }
9089   else if (element == EL_AMOEBA_DROPPING)
9090   {
9091     Feld[x][y] = get_next_element(element);
9092     element = Feld[newx][newy] = Store[x][y];
9093   }
9094   else if (element == EL_SOKOBAN_OBJECT)
9095   {
9096     if (Back[x][y])
9097       Feld[x][y] = Back[x][y];
9098
9099     if (Back[newx][newy])
9100       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9101
9102     Back[x][y] = Back[newx][newy] = 0;
9103   }
9104
9105   Store[x][y] = EL_EMPTY;
9106   MovPos[x][y] = 0;
9107   MovDir[x][y] = 0;
9108   MovDelay[x][y] = 0;
9109
9110   MovDelay[newx][newy] = 0;
9111
9112   if (CAN_CHANGE_OR_HAS_ACTION(element))
9113   {
9114     /* copy element change control values to new field */
9115     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9116     ChangePage[newx][newy]  = ChangePage[x][y];
9117     ChangeCount[newx][newy] = ChangeCount[x][y];
9118     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9119   }
9120
9121 #if USE_NEW_CUSTOM_VALUE
9122   CustomValue[newx][newy] = CustomValue[x][y];
9123 #endif
9124
9125   ChangeDelay[x][y] = 0;
9126   ChangePage[x][y] = -1;
9127   ChangeCount[x][y] = 0;
9128   ChangeEvent[x][y] = -1;
9129
9130 #if USE_NEW_CUSTOM_VALUE
9131   CustomValue[x][y] = 0;
9132 #endif
9133
9134   /* copy animation control values to new field */
9135   GfxFrame[newx][newy]  = GfxFrame[x][y];
9136   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9137   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9138   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9139
9140   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9141
9142   /* some elements can leave other elements behind after moving */
9143 #if 1
9144   if (ei->move_leave_element != EL_EMPTY &&
9145       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9146       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9147 #else
9148   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9149       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9150       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9151 #endif
9152   {
9153     int move_leave_element = ei->move_leave_element;
9154
9155 #if 1
9156 #if 1
9157     /* this makes it possible to leave the removed element again */
9158     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9159       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9160 #else
9161     /* this makes it possible to leave the removed element again */
9162     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9163       move_leave_element = stored;
9164 #endif
9165 #else
9166     /* this makes it possible to leave the removed element again */
9167     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9168         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9169       move_leave_element = stored;
9170 #endif
9171
9172     Feld[x][y] = move_leave_element;
9173
9174     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9175       MovDir[x][y] = direction;
9176
9177     InitField(x, y, FALSE);
9178
9179     if (GFX_CRUMBLED(Feld[x][y]))
9180       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9181
9182     if (ELEM_IS_PLAYER(move_leave_element))
9183       RelocatePlayer(x, y, move_leave_element);
9184   }
9185
9186   /* do this after checking for left-behind element */
9187   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9188
9189   if (!CAN_MOVE(element) ||
9190       (CAN_FALL(element) && direction == MV_DOWN &&
9191        (element == EL_SPRING ||
9192         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9193         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9194     GfxDir[x][y] = MovDir[newx][newy] = 0;
9195
9196   TEST_DrawLevelField(x, y);
9197   TEST_DrawLevelField(newx, newy);
9198
9199   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9200
9201   /* prevent pushed element from moving on in pushed direction */
9202   if (pushed_by_player && CAN_MOVE(element) &&
9203       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9204       !(element_info[element].move_pattern & direction))
9205     TurnRound(newx, newy);
9206
9207   /* prevent elements on conveyor belt from moving on in last direction */
9208   if (pushed_by_conveyor && CAN_FALL(element) &&
9209       direction & MV_HORIZONTAL)
9210     MovDir[newx][newy] = 0;
9211
9212   if (!pushed_by_player)
9213   {
9214     int nextx = newx + dx, nexty = newy + dy;
9215     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9216
9217     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9218
9219     if (CAN_FALL(element) && direction == MV_DOWN)
9220       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9221
9222     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9223       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9224
9225 #if USE_FIX_IMPACT_COLLISION
9226     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9227       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9228 #endif
9229   }
9230
9231   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9232   {
9233     TestIfBadThingTouchesPlayer(newx, newy);
9234     TestIfBadThingTouchesFriend(newx, newy);
9235
9236     if (!IS_CUSTOM_ELEMENT(element))
9237       TestIfBadThingTouchesOtherBadThing(newx, newy);
9238   }
9239   else if (element == EL_PENGUIN)
9240     TestIfFriendTouchesBadThing(newx, newy);
9241
9242   if (DONT_GET_HIT_BY(element))
9243   {
9244     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9245   }
9246
9247   /* give the player one last chance (one more frame) to move away */
9248   if (CAN_FALL(element) && direction == MV_DOWN &&
9249       (last_line || (!IS_FREE(x, newy + 1) &&
9250                      (!IS_PLAYER(x, newy + 1) ||
9251                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9252     Impact(x, newy);
9253
9254   if (pushed_by_player && !game.use_change_when_pushing_bug)
9255   {
9256     int push_side = MV_DIR_OPPOSITE(direction);
9257     struct PlayerInfo *player = PLAYERINFO(x, y);
9258
9259     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9260                                player->index_bit, push_side);
9261     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9262                                         player->index_bit, push_side);
9263   }
9264
9265   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9266     MovDelay[newx][newy] = 1;
9267
9268   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9269
9270   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9271
9272 #if 0
9273   if (ChangePage[newx][newy] != -1)             /* delayed change */
9274   {
9275     int page = ChangePage[newx][newy];
9276     struct ElementChangeInfo *change = &ei->change_page[page];
9277
9278     ChangePage[newx][newy] = -1;
9279
9280     if (change->can_change)
9281     {
9282       if (ChangeElement(newx, newy, element, page))
9283       {
9284         if (change->post_change_function)
9285           change->post_change_function(newx, newy);
9286       }
9287     }
9288
9289     if (change->has_action)
9290       ExecuteCustomElementAction(newx, newy, element, page);
9291   }
9292 #endif
9293
9294   TestIfElementHitsCustomElement(newx, newy, direction);
9295   TestIfPlayerTouchesCustomElement(newx, newy);
9296   TestIfElementTouchesCustomElement(newx, newy);
9297
9298   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9299       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9300     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9301                              MV_DIR_OPPOSITE(direction));
9302 }
9303
9304 int AmoebeNachbarNr(int ax, int ay)
9305 {
9306   int i;
9307   int element = Feld[ax][ay];
9308   int group_nr = 0;
9309   static int xy[4][2] =
9310   {
9311     { 0, -1 },
9312     { -1, 0 },
9313     { +1, 0 },
9314     { 0, +1 }
9315   };
9316
9317   for (i = 0; i < NUM_DIRECTIONS; i++)
9318   {
9319     int x = ax + xy[i][0];
9320     int y = ay + xy[i][1];
9321
9322     if (!IN_LEV_FIELD(x, y))
9323       continue;
9324
9325     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9326       group_nr = AmoebaNr[x][y];
9327   }
9328
9329   return group_nr;
9330 }
9331
9332 void AmoebenVereinigen(int ax, int ay)
9333 {
9334   int i, x, y, xx, yy;
9335   int new_group_nr = AmoebaNr[ax][ay];
9336   static int xy[4][2] =
9337   {
9338     { 0, -1 },
9339     { -1, 0 },
9340     { +1, 0 },
9341     { 0, +1 }
9342   };
9343
9344   if (new_group_nr == 0)
9345     return;
9346
9347   for (i = 0; i < NUM_DIRECTIONS; i++)
9348   {
9349     x = ax + xy[i][0];
9350     y = ay + xy[i][1];
9351
9352     if (!IN_LEV_FIELD(x, y))
9353       continue;
9354
9355     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9356          Feld[x][y] == EL_BD_AMOEBA ||
9357          Feld[x][y] == EL_AMOEBA_DEAD) &&
9358         AmoebaNr[x][y] != new_group_nr)
9359     {
9360       int old_group_nr = AmoebaNr[x][y];
9361
9362       if (old_group_nr == 0)
9363         return;
9364
9365       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9366       AmoebaCnt[old_group_nr] = 0;
9367       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9368       AmoebaCnt2[old_group_nr] = 0;
9369
9370       SCAN_PLAYFIELD(xx, yy)
9371       {
9372         if (AmoebaNr[xx][yy] == old_group_nr)
9373           AmoebaNr[xx][yy] = new_group_nr;
9374       }
9375     }
9376   }
9377 }
9378
9379 void AmoebeUmwandeln(int ax, int ay)
9380 {
9381   int i, x, y;
9382
9383   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9384   {
9385     int group_nr = AmoebaNr[ax][ay];
9386
9387 #ifdef DEBUG
9388     if (group_nr == 0)
9389     {
9390       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9391       printf("AmoebeUmwandeln(): This should never happen!\n");
9392       return;
9393     }
9394 #endif
9395
9396     SCAN_PLAYFIELD(x, y)
9397     {
9398       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9399       {
9400         AmoebaNr[x][y] = 0;
9401         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9402       }
9403     }
9404
9405     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9406                             SND_AMOEBA_TURNING_TO_GEM :
9407                             SND_AMOEBA_TURNING_TO_ROCK));
9408     Bang(ax, ay);
9409   }
9410   else
9411   {
9412     static int xy[4][2] =
9413     {
9414       { 0, -1 },
9415       { -1, 0 },
9416       { +1, 0 },
9417       { 0, +1 }
9418     };
9419
9420     for (i = 0; i < NUM_DIRECTIONS; i++)
9421     {
9422       x = ax + xy[i][0];
9423       y = ay + xy[i][1];
9424
9425       if (!IN_LEV_FIELD(x, y))
9426         continue;
9427
9428       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9429       {
9430         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9431                               SND_AMOEBA_TURNING_TO_GEM :
9432                               SND_AMOEBA_TURNING_TO_ROCK));
9433         Bang(x, y);
9434       }
9435     }
9436   }
9437 }
9438
9439 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9440 {
9441   int x, y;
9442   int group_nr = AmoebaNr[ax][ay];
9443   boolean done = FALSE;
9444
9445 #ifdef DEBUG
9446   if (group_nr == 0)
9447   {
9448     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9449     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9450     return;
9451   }
9452 #endif
9453
9454   SCAN_PLAYFIELD(x, y)
9455   {
9456     if (AmoebaNr[x][y] == group_nr &&
9457         (Feld[x][y] == EL_AMOEBA_DEAD ||
9458          Feld[x][y] == EL_BD_AMOEBA ||
9459          Feld[x][y] == EL_AMOEBA_GROWING))
9460     {
9461       AmoebaNr[x][y] = 0;
9462       Feld[x][y] = new_element;
9463       InitField(x, y, FALSE);
9464       TEST_DrawLevelField(x, y);
9465       done = TRUE;
9466     }
9467   }
9468
9469   if (done)
9470     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9471                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9472                             SND_BD_AMOEBA_TURNING_TO_GEM));
9473 }
9474
9475 void AmoebeWaechst(int x, int y)
9476 {
9477   static unsigned int sound_delay = 0;
9478   static unsigned int sound_delay_value = 0;
9479
9480   if (!MovDelay[x][y])          /* start new growing cycle */
9481   {
9482     MovDelay[x][y] = 7;
9483
9484     if (DelayReached(&sound_delay, sound_delay_value))
9485     {
9486       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9487       sound_delay_value = 30;
9488     }
9489   }
9490
9491   if (MovDelay[x][y])           /* wait some time before growing bigger */
9492   {
9493     MovDelay[x][y]--;
9494     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9495     {
9496       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9497                                            6 - MovDelay[x][y]);
9498
9499       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9500     }
9501
9502     if (!MovDelay[x][y])
9503     {
9504       Feld[x][y] = Store[x][y];
9505       Store[x][y] = 0;
9506       TEST_DrawLevelField(x, y);
9507     }
9508   }
9509 }
9510
9511 void AmoebaDisappearing(int x, int y)
9512 {
9513   static unsigned int sound_delay = 0;
9514   static unsigned int sound_delay_value = 0;
9515
9516   if (!MovDelay[x][y])          /* start new shrinking cycle */
9517   {
9518     MovDelay[x][y] = 7;
9519
9520     if (DelayReached(&sound_delay, sound_delay_value))
9521       sound_delay_value = 30;
9522   }
9523
9524   if (MovDelay[x][y])           /* wait some time before shrinking */
9525   {
9526     MovDelay[x][y]--;
9527     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9528     {
9529       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9530                                            6 - MovDelay[x][y]);
9531
9532       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9533     }
9534
9535     if (!MovDelay[x][y])
9536     {
9537       Feld[x][y] = EL_EMPTY;
9538       TEST_DrawLevelField(x, y);
9539
9540       /* don't let mole enter this field in this cycle;
9541          (give priority to objects falling to this field from above) */
9542       Stop[x][y] = TRUE;
9543     }
9544   }
9545 }
9546
9547 void AmoebeAbleger(int ax, int ay)
9548 {
9549   int i;
9550   int element = Feld[ax][ay];
9551   int graphic = el2img(element);
9552   int newax = ax, neway = ay;
9553   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9554   static int xy[4][2] =
9555   {
9556     { 0, -1 },
9557     { -1, 0 },
9558     { +1, 0 },
9559     { 0, +1 }
9560   };
9561
9562   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9563   {
9564     Feld[ax][ay] = EL_AMOEBA_DEAD;
9565     TEST_DrawLevelField(ax, ay);
9566     return;
9567   }
9568
9569   if (IS_ANIMATED(graphic))
9570     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9571
9572   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9573     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9574
9575   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9576   {
9577     MovDelay[ax][ay]--;
9578     if (MovDelay[ax][ay])
9579       return;
9580   }
9581
9582   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9583   {
9584     int start = RND(4);
9585     int x = ax + xy[start][0];
9586     int y = ay + xy[start][1];
9587
9588     if (!IN_LEV_FIELD(x, y))
9589       return;
9590
9591     if (IS_FREE(x, y) ||
9592         CAN_GROW_INTO(Feld[x][y]) ||
9593         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9594         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9595     {
9596       newax = x;
9597       neway = y;
9598     }
9599
9600     if (newax == ax && neway == ay)
9601       return;
9602   }
9603   else                          /* normal or "filled" (BD style) amoeba */
9604   {
9605     int start = RND(4);
9606     boolean waiting_for_player = FALSE;
9607
9608     for (i = 0; i < NUM_DIRECTIONS; i++)
9609     {
9610       int j = (start + i) % 4;
9611       int x = ax + xy[j][0];
9612       int y = ay + xy[j][1];
9613
9614       if (!IN_LEV_FIELD(x, y))
9615         continue;
9616
9617       if (IS_FREE(x, y) ||
9618           CAN_GROW_INTO(Feld[x][y]) ||
9619           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9620           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9621       {
9622         newax = x;
9623         neway = y;
9624         break;
9625       }
9626       else if (IS_PLAYER(x, y))
9627         waiting_for_player = TRUE;
9628     }
9629
9630     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9631     {
9632       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9633       {
9634         Feld[ax][ay] = EL_AMOEBA_DEAD;
9635         TEST_DrawLevelField(ax, ay);
9636         AmoebaCnt[AmoebaNr[ax][ay]]--;
9637
9638         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9639         {
9640           if (element == EL_AMOEBA_FULL)
9641             AmoebeUmwandeln(ax, ay);
9642           else if (element == EL_BD_AMOEBA)
9643             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9644         }
9645       }
9646       return;
9647     }
9648     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9649     {
9650       /* amoeba gets larger by growing in some direction */
9651
9652       int new_group_nr = AmoebaNr[ax][ay];
9653
9654 #ifdef DEBUG
9655   if (new_group_nr == 0)
9656   {
9657     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9658     printf("AmoebeAbleger(): This should never happen!\n");
9659     return;
9660   }
9661 #endif
9662
9663       AmoebaNr[newax][neway] = new_group_nr;
9664       AmoebaCnt[new_group_nr]++;
9665       AmoebaCnt2[new_group_nr]++;
9666
9667       /* if amoeba touches other amoeba(s) after growing, unify them */
9668       AmoebenVereinigen(newax, neway);
9669
9670       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9671       {
9672         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9673         return;
9674       }
9675     }
9676   }
9677
9678   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9679       (neway == lev_fieldy - 1 && newax != ax))
9680   {
9681     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9682     Store[newax][neway] = element;
9683   }
9684   else if (neway == ay || element == EL_EMC_DRIPPER)
9685   {
9686     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9687
9688     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9689   }
9690   else
9691   {
9692     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9693     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9694     Store[ax][ay] = EL_AMOEBA_DROP;
9695     ContinueMoving(ax, ay);
9696     return;
9697   }
9698
9699   TEST_DrawLevelField(newax, neway);
9700 }
9701
9702 void Life(int ax, int ay)
9703 {
9704   int x1, y1, x2, y2;
9705   int life_time = 40;
9706   int element = Feld[ax][ay];
9707   int graphic = el2img(element);
9708   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9709                          level.biomaze);
9710   boolean changed = FALSE;
9711
9712   if (IS_ANIMATED(graphic))
9713     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9714
9715   if (Stop[ax][ay])
9716     return;
9717
9718   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9719     MovDelay[ax][ay] = life_time;
9720
9721   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9722   {
9723     MovDelay[ax][ay]--;
9724     if (MovDelay[ax][ay])
9725       return;
9726   }
9727
9728   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9729   {
9730     int xx = ax+x1, yy = ay+y1;
9731     int nachbarn = 0;
9732
9733     if (!IN_LEV_FIELD(xx, yy))
9734       continue;
9735
9736     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9737     {
9738       int x = xx+x2, y = yy+y2;
9739
9740       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9741         continue;
9742
9743       if (((Feld[x][y] == element ||
9744             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9745            !Stop[x][y]) ||
9746           (IS_FREE(x, y) && Stop[x][y]))
9747         nachbarn++;
9748     }
9749
9750     if (xx == ax && yy == ay)           /* field in the middle */
9751     {
9752       if (nachbarn < life_parameter[0] ||
9753           nachbarn > life_parameter[1])
9754       {
9755         Feld[xx][yy] = EL_EMPTY;
9756         if (!Stop[xx][yy])
9757           TEST_DrawLevelField(xx, yy);
9758         Stop[xx][yy] = TRUE;
9759         changed = TRUE;
9760       }
9761     }
9762     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9763     {                                   /* free border field */
9764       if (nachbarn >= life_parameter[2] &&
9765           nachbarn <= life_parameter[3])
9766       {
9767         Feld[xx][yy] = element;
9768         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9769         if (!Stop[xx][yy])
9770           TEST_DrawLevelField(xx, yy);
9771         Stop[xx][yy] = TRUE;
9772         changed = TRUE;
9773       }
9774     }
9775   }
9776
9777   if (changed)
9778     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9779                    SND_GAME_OF_LIFE_GROWING);
9780 }
9781
9782 static void InitRobotWheel(int x, int y)
9783 {
9784   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9785 }
9786
9787 static void RunRobotWheel(int x, int y)
9788 {
9789   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9790 }
9791
9792 static void StopRobotWheel(int x, int y)
9793 {
9794   if (ZX == x && ZY == y)
9795   {
9796     ZX = ZY = -1;
9797
9798     game.robot_wheel_active = FALSE;
9799   }
9800 }
9801
9802 static void InitTimegateWheel(int x, int y)
9803 {
9804   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9805 }
9806
9807 static void RunTimegateWheel(int x, int y)
9808 {
9809   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9810 }
9811
9812 static void InitMagicBallDelay(int x, int y)
9813 {
9814 #if 1
9815   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9816 #else
9817   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9818 #endif
9819 }
9820
9821 static void ActivateMagicBall(int bx, int by)
9822 {
9823   int x, y;
9824
9825   if (level.ball_random)
9826   {
9827     int pos_border = RND(8);    /* select one of the eight border elements */
9828     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9829     int xx = pos_content % 3;
9830     int yy = pos_content / 3;
9831
9832     x = bx - 1 + xx;
9833     y = by - 1 + yy;
9834
9835     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9836       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9837   }
9838   else
9839   {
9840     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9841     {
9842       int xx = x - bx + 1;
9843       int yy = y - by + 1;
9844
9845       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9846         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9847     }
9848   }
9849
9850   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9851 }
9852
9853 void CheckExit(int x, int y)
9854 {
9855   if (local_player->gems_still_needed > 0 ||
9856       local_player->sokobanfields_still_needed > 0 ||
9857       local_player->lights_still_needed > 0)
9858   {
9859     int element = Feld[x][y];
9860     int graphic = el2img(element);
9861
9862     if (IS_ANIMATED(graphic))
9863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9864
9865     return;
9866   }
9867
9868   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9869     return;
9870
9871   Feld[x][y] = EL_EXIT_OPENING;
9872
9873   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9874 }
9875
9876 void CheckExitEM(int x, int y)
9877 {
9878   if (local_player->gems_still_needed > 0 ||
9879       local_player->sokobanfields_still_needed > 0 ||
9880       local_player->lights_still_needed > 0)
9881   {
9882     int element = Feld[x][y];
9883     int graphic = el2img(element);
9884
9885     if (IS_ANIMATED(graphic))
9886       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9887
9888     return;
9889   }
9890
9891   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9892     return;
9893
9894   Feld[x][y] = EL_EM_EXIT_OPENING;
9895
9896   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9897 }
9898
9899 void CheckExitSteel(int x, int y)
9900 {
9901   if (local_player->gems_still_needed > 0 ||
9902       local_player->sokobanfields_still_needed > 0 ||
9903       local_player->lights_still_needed > 0)
9904   {
9905     int element = Feld[x][y];
9906     int graphic = el2img(element);
9907
9908     if (IS_ANIMATED(graphic))
9909       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9910
9911     return;
9912   }
9913
9914   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9915     return;
9916
9917   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9918
9919   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9920 }
9921
9922 void CheckExitSteelEM(int x, int y)
9923 {
9924   if (local_player->gems_still_needed > 0 ||
9925       local_player->sokobanfields_still_needed > 0 ||
9926       local_player->lights_still_needed > 0)
9927   {
9928     int element = Feld[x][y];
9929     int graphic = el2img(element);
9930
9931     if (IS_ANIMATED(graphic))
9932       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9933
9934     return;
9935   }
9936
9937   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9938     return;
9939
9940   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9941
9942   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9943 }
9944
9945 void CheckExitSP(int x, int y)
9946 {
9947   if (local_player->gems_still_needed > 0)
9948   {
9949     int element = Feld[x][y];
9950     int graphic = el2img(element);
9951
9952     if (IS_ANIMATED(graphic))
9953       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9954
9955     return;
9956   }
9957
9958   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9959     return;
9960
9961   Feld[x][y] = EL_SP_EXIT_OPENING;
9962
9963   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9964 }
9965
9966 static void CloseAllOpenTimegates()
9967 {
9968   int x, y;
9969
9970   SCAN_PLAYFIELD(x, y)
9971   {
9972     int element = Feld[x][y];
9973
9974     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9975     {
9976       Feld[x][y] = EL_TIMEGATE_CLOSING;
9977
9978       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9979     }
9980   }
9981 }
9982
9983 void DrawTwinkleOnField(int x, int y)
9984 {
9985   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9986     return;
9987
9988   if (Feld[x][y] == EL_BD_DIAMOND)
9989     return;
9990
9991   if (MovDelay[x][y] == 0)      /* next animation frame */
9992     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9993
9994   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9995   {
9996     MovDelay[x][y]--;
9997
9998     DrawLevelElementAnimation(x, y, Feld[x][y]);
9999
10000     if (MovDelay[x][y] != 0)
10001     {
10002       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10003                                            10 - MovDelay[x][y]);
10004
10005       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10006     }
10007   }
10008 }
10009
10010 void MauerWaechst(int x, int y)
10011 {
10012   int delay = 6;
10013
10014   if (!MovDelay[x][y])          /* next animation frame */
10015     MovDelay[x][y] = 3 * delay;
10016
10017   if (MovDelay[x][y])           /* wait some time before next frame */
10018   {
10019     MovDelay[x][y]--;
10020
10021     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10022     {
10023       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10024       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10025
10026       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10027     }
10028
10029     if (!MovDelay[x][y])
10030     {
10031       if (MovDir[x][y] == MV_LEFT)
10032       {
10033         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10034           TEST_DrawLevelField(x - 1, y);
10035       }
10036       else if (MovDir[x][y] == MV_RIGHT)
10037       {
10038         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10039           TEST_DrawLevelField(x + 1, y);
10040       }
10041       else if (MovDir[x][y] == MV_UP)
10042       {
10043         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10044           TEST_DrawLevelField(x, y - 1);
10045       }
10046       else
10047       {
10048         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10049           TEST_DrawLevelField(x, y + 1);
10050       }
10051
10052       Feld[x][y] = Store[x][y];
10053       Store[x][y] = 0;
10054       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10055       TEST_DrawLevelField(x, y);
10056     }
10057   }
10058 }
10059
10060 void MauerAbleger(int ax, int ay)
10061 {
10062   int element = Feld[ax][ay];
10063   int graphic = el2img(element);
10064   boolean oben_frei = FALSE, unten_frei = FALSE;
10065   boolean links_frei = FALSE, rechts_frei = FALSE;
10066   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10067   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10068   boolean new_wall = FALSE;
10069
10070   if (IS_ANIMATED(graphic))
10071     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10072
10073   if (!MovDelay[ax][ay])        /* start building new wall */
10074     MovDelay[ax][ay] = 6;
10075
10076   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10077   {
10078     MovDelay[ax][ay]--;
10079     if (MovDelay[ax][ay])
10080       return;
10081   }
10082
10083   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10084     oben_frei = TRUE;
10085   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10086     unten_frei = TRUE;
10087   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10088     links_frei = TRUE;
10089   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10090     rechts_frei = TRUE;
10091
10092   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10093       element == EL_EXPANDABLE_WALL_ANY)
10094   {
10095     if (oben_frei)
10096     {
10097       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10098       Store[ax][ay-1] = element;
10099       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10100       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10101         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10102                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10103       new_wall = TRUE;
10104     }
10105     if (unten_frei)
10106     {
10107       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10108       Store[ax][ay+1] = element;
10109       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10110       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10111         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10112                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10113       new_wall = TRUE;
10114     }
10115   }
10116
10117   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10118       element == EL_EXPANDABLE_WALL_ANY ||
10119       element == EL_EXPANDABLE_WALL ||
10120       element == EL_BD_EXPANDABLE_WALL)
10121   {
10122     if (links_frei)
10123     {
10124       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10125       Store[ax-1][ay] = element;
10126       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10127       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10128         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10129                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10130       new_wall = TRUE;
10131     }
10132
10133     if (rechts_frei)
10134     {
10135       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136       Store[ax+1][ay] = element;
10137       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10138       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10139         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10140                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10141       new_wall = TRUE;
10142     }
10143   }
10144
10145   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10146     TEST_DrawLevelField(ax, ay);
10147
10148   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10149     oben_massiv = TRUE;
10150   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10151     unten_massiv = TRUE;
10152   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10153     links_massiv = TRUE;
10154   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10155     rechts_massiv = TRUE;
10156
10157   if (((oben_massiv && unten_massiv) ||
10158        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10159        element == EL_EXPANDABLE_WALL) &&
10160       ((links_massiv && rechts_massiv) ||
10161        element == EL_EXPANDABLE_WALL_VERTICAL))
10162     Feld[ax][ay] = EL_WALL;
10163
10164   if (new_wall)
10165     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10166 }
10167
10168 void MauerAblegerStahl(int ax, int ay)
10169 {
10170   int element = Feld[ax][ay];
10171   int graphic = el2img(element);
10172   boolean oben_frei = FALSE, unten_frei = FALSE;
10173   boolean links_frei = FALSE, rechts_frei = FALSE;
10174   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10175   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10176   boolean new_wall = FALSE;
10177
10178   if (IS_ANIMATED(graphic))
10179     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10180
10181   if (!MovDelay[ax][ay])        /* start building new wall */
10182     MovDelay[ax][ay] = 6;
10183
10184   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10185   {
10186     MovDelay[ax][ay]--;
10187     if (MovDelay[ax][ay])
10188       return;
10189   }
10190
10191   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10192     oben_frei = TRUE;
10193   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10194     unten_frei = TRUE;
10195   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10196     links_frei = TRUE;
10197   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10198     rechts_frei = TRUE;
10199
10200   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10201       element == EL_EXPANDABLE_STEELWALL_ANY)
10202   {
10203     if (oben_frei)
10204     {
10205       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10206       Store[ax][ay-1] = element;
10207       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10208       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10209         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10210                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10211       new_wall = TRUE;
10212     }
10213     if (unten_frei)
10214     {
10215       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10216       Store[ax][ay+1] = element;
10217       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10218       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10219         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10220                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10221       new_wall = TRUE;
10222     }
10223   }
10224
10225   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10226       element == EL_EXPANDABLE_STEELWALL_ANY)
10227   {
10228     if (links_frei)
10229     {
10230       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10231       Store[ax-1][ay] = element;
10232       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10233       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10234         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10235                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10236       new_wall = TRUE;
10237     }
10238
10239     if (rechts_frei)
10240     {
10241       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242       Store[ax+1][ay] = element;
10243       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10244       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10245         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10246                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10247       new_wall = TRUE;
10248     }
10249   }
10250
10251   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10252     oben_massiv = TRUE;
10253   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10254     unten_massiv = TRUE;
10255   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10256     links_massiv = TRUE;
10257   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10258     rechts_massiv = TRUE;
10259
10260   if (((oben_massiv && unten_massiv) ||
10261        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10262       ((links_massiv && rechts_massiv) ||
10263        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10264     Feld[ax][ay] = EL_STEELWALL;
10265
10266   if (new_wall)
10267     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10268 }
10269
10270 void CheckForDragon(int x, int y)
10271 {
10272   int i, j;
10273   boolean dragon_found = FALSE;
10274   static int xy[4][2] =
10275   {
10276     { 0, -1 },
10277     { -1, 0 },
10278     { +1, 0 },
10279     { 0, +1 }
10280   };
10281
10282   for (i = 0; i < NUM_DIRECTIONS; i++)
10283   {
10284     for (j = 0; j < 4; j++)
10285     {
10286       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10287
10288       if (IN_LEV_FIELD(xx, yy) &&
10289           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10290       {
10291         if (Feld[xx][yy] == EL_DRAGON)
10292           dragon_found = TRUE;
10293       }
10294       else
10295         break;
10296     }
10297   }
10298
10299   if (!dragon_found)
10300   {
10301     for (i = 0; i < NUM_DIRECTIONS; i++)
10302     {
10303       for (j = 0; j < 3; j++)
10304       {
10305         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10306   
10307         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10308         {
10309           Feld[xx][yy] = EL_EMPTY;
10310           TEST_DrawLevelField(xx, yy);
10311         }
10312         else
10313           break;
10314       }
10315     }
10316   }
10317 }
10318
10319 static void InitBuggyBase(int x, int y)
10320 {
10321   int element = Feld[x][y];
10322   int activating_delay = FRAMES_PER_SECOND / 4;
10323
10324   ChangeDelay[x][y] =
10325     (element == EL_SP_BUGGY_BASE ?
10326      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10327      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10328      activating_delay :
10329      element == EL_SP_BUGGY_BASE_ACTIVE ?
10330      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10331 }
10332
10333 static void WarnBuggyBase(int x, int y)
10334 {
10335   int i;
10336   static int xy[4][2] =
10337   {
10338     { 0, -1 },
10339     { -1, 0 },
10340     { +1, 0 },
10341     { 0, +1 }
10342   };
10343
10344   for (i = 0; i < NUM_DIRECTIONS; i++)
10345   {
10346     int xx = x + xy[i][0];
10347     int yy = y + xy[i][1];
10348
10349     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10350     {
10351       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10352
10353       break;
10354     }
10355   }
10356 }
10357
10358 static void InitTrap(int x, int y)
10359 {
10360   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10361 }
10362
10363 static void ActivateTrap(int x, int y)
10364 {
10365   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10366 }
10367
10368 static void ChangeActiveTrap(int x, int y)
10369 {
10370   int graphic = IMG_TRAP_ACTIVE;
10371
10372   /* if new animation frame was drawn, correct crumbled sand border */
10373   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10374     TEST_DrawLevelFieldCrumbled(x, y);
10375 }
10376
10377 static int getSpecialActionElement(int element, int number, int base_element)
10378 {
10379   return (element != EL_EMPTY ? element :
10380           number != -1 ? base_element + number - 1 :
10381           EL_EMPTY);
10382 }
10383
10384 static int getModifiedActionNumber(int value_old, int operator, int operand,
10385                                    int value_min, int value_max)
10386 {
10387   int value_new = (operator == CA_MODE_SET      ? operand :
10388                    operator == CA_MODE_ADD      ? value_old + operand :
10389                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10390                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10391                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10392                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10393                    value_old);
10394
10395   return (value_new < value_min ? value_min :
10396           value_new > value_max ? value_max :
10397           value_new);
10398 }
10399
10400 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10401 {
10402   struct ElementInfo *ei = &element_info[element];
10403   struct ElementChangeInfo *change = &ei->change_page[page];
10404   int target_element = change->target_element;
10405   int action_type = change->action_type;
10406   int action_mode = change->action_mode;
10407   int action_arg = change->action_arg;
10408   int action_element = change->action_element;
10409   int i;
10410
10411   if (!change->has_action)
10412     return;
10413
10414   /* ---------- determine action paramater values -------------------------- */
10415
10416   int level_time_value =
10417     (level.time > 0 ? TimeLeft :
10418      TimePlayed);
10419
10420   int action_arg_element_raw =
10421     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10422      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10423      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10424      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10425      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10426      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10427      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10428      EL_EMPTY);
10429   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10430
10431 #if 0
10432   if (action_arg_element_raw == EL_GROUP_START)
10433     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10434 #endif
10435
10436   int action_arg_direction =
10437     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10438      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10439      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10440      change->actual_trigger_side :
10441      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10442      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10443      MV_NONE);
10444
10445   int action_arg_number_min =
10446     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10447      CA_ARG_MIN);
10448
10449   int action_arg_number_max =
10450     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10451      action_type == CA_SET_LEVEL_GEMS ? 999 :
10452      action_type == CA_SET_LEVEL_TIME ? 9999 :
10453      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10454      action_type == CA_SET_CE_VALUE ? 9999 :
10455      action_type == CA_SET_CE_SCORE ? 9999 :
10456      CA_ARG_MAX);
10457
10458   int action_arg_number_reset =
10459     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10460      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10461      action_type == CA_SET_LEVEL_TIME ? level.time :
10462      action_type == CA_SET_LEVEL_SCORE ? 0 :
10463 #if USE_NEW_CUSTOM_VALUE
10464      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10465 #else
10466      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10467 #endif
10468      action_type == CA_SET_CE_SCORE ? 0 :
10469      0);
10470
10471   int action_arg_number =
10472     (action_arg <= CA_ARG_MAX ? action_arg :
10473      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10474      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10475      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10476      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10477      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10478      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10479 #if USE_NEW_CUSTOM_VALUE
10480      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10481 #else
10482      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10483 #endif
10484      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10485      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10486      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10487      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10488      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10489      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10490      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10491      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10492      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10493      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10494      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10495      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10496      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10497      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10498      -1);
10499
10500   int action_arg_number_old =
10501     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10502      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10503      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10504      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10505      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10506      0);
10507
10508   int action_arg_number_new =
10509     getModifiedActionNumber(action_arg_number_old,
10510                             action_mode, action_arg_number,
10511                             action_arg_number_min, action_arg_number_max);
10512
10513 #if 1
10514   int trigger_player_bits =
10515     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10516      change->actual_trigger_player_bits : change->trigger_player);
10517 #else
10518   int trigger_player_bits =
10519     (change->actual_trigger_player >= EL_PLAYER_1 &&
10520      change->actual_trigger_player <= EL_PLAYER_4 ?
10521      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10522      PLAYER_BITS_ANY);
10523 #endif
10524
10525   int action_arg_player_bits =
10526     (action_arg >= CA_ARG_PLAYER_1 &&
10527      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10528      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10529      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10530      PLAYER_BITS_ANY);
10531
10532   /* ---------- execute action  -------------------------------------------- */
10533
10534   switch (action_type)
10535   {
10536     case CA_NO_ACTION:
10537     {
10538       return;
10539     }
10540
10541     /* ---------- level actions  ------------------------------------------- */
10542
10543     case CA_RESTART_LEVEL:
10544     {
10545       game.restart_level = TRUE;
10546
10547       break;
10548     }
10549
10550     case CA_SHOW_ENVELOPE:
10551     {
10552       int element = getSpecialActionElement(action_arg_element,
10553                                             action_arg_number, EL_ENVELOPE_1);
10554
10555       if (IS_ENVELOPE(element))
10556         local_player->show_envelope = element;
10557
10558       break;
10559     }
10560
10561     case CA_SET_LEVEL_TIME:
10562     {
10563       if (level.time > 0)       /* only modify limited time value */
10564       {
10565         TimeLeft = action_arg_number_new;
10566
10567 #if 1
10568         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10569
10570         DisplayGameControlValues();
10571 #else
10572         DrawGameValue_Time(TimeLeft);
10573 #endif
10574
10575         if (!TimeLeft && setup.time_limit)
10576           for (i = 0; i < MAX_PLAYERS; i++)
10577             KillPlayer(&stored_player[i]);
10578       }
10579
10580       break;
10581     }
10582
10583     case CA_SET_LEVEL_SCORE:
10584     {
10585       local_player->score = action_arg_number_new;
10586
10587 #if 1
10588       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10589
10590       DisplayGameControlValues();
10591 #else
10592       DrawGameValue_Score(local_player->score);
10593 #endif
10594
10595       break;
10596     }
10597
10598     case CA_SET_LEVEL_GEMS:
10599     {
10600       local_player->gems_still_needed = action_arg_number_new;
10601
10602 #if 1
10603       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10604
10605       DisplayGameControlValues();
10606 #else
10607       DrawGameValue_Emeralds(local_player->gems_still_needed);
10608 #endif
10609
10610       break;
10611     }
10612
10613 #if !USE_PLAYER_GRAVITY
10614     case CA_SET_LEVEL_GRAVITY:
10615     {
10616       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10617                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10618                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10619                       game.gravity);
10620       break;
10621     }
10622 #endif
10623
10624     case CA_SET_LEVEL_WIND:
10625     {
10626       game.wind_direction = action_arg_direction;
10627
10628       break;
10629     }
10630
10631     case CA_SET_LEVEL_RANDOM_SEED:
10632     {
10633 #if 1
10634       /* ensure that setting a new random seed while playing is predictable */
10635       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10636 #else
10637       InitRND(action_arg_number_new);
10638 #endif
10639
10640 #if 0
10641       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10642 #endif
10643
10644 #if 0
10645       {
10646         int i;
10647
10648         printf("::: ");
10649         for (i = 0; i < 9; i++)
10650           printf("%d, ", RND(2));
10651         printf("\n");
10652       }
10653 #endif
10654
10655       break;
10656     }
10657
10658     /* ---------- player actions  ------------------------------------------ */
10659
10660     case CA_MOVE_PLAYER:
10661     {
10662       /* automatically move to the next field in specified direction */
10663       for (i = 0; i < MAX_PLAYERS; i++)
10664         if (trigger_player_bits & (1 << i))
10665           stored_player[i].programmed_action = action_arg_direction;
10666
10667       break;
10668     }
10669
10670     case CA_EXIT_PLAYER:
10671     {
10672       for (i = 0; i < MAX_PLAYERS; i++)
10673         if (action_arg_player_bits & (1 << i))
10674           PlayerWins(&stored_player[i]);
10675
10676       break;
10677     }
10678
10679     case CA_KILL_PLAYER:
10680     {
10681       for (i = 0; i < MAX_PLAYERS; i++)
10682         if (action_arg_player_bits & (1 << i))
10683           KillPlayer(&stored_player[i]);
10684
10685       break;
10686     }
10687
10688     case CA_SET_PLAYER_KEYS:
10689     {
10690       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10691       int element = getSpecialActionElement(action_arg_element,
10692                                             action_arg_number, EL_KEY_1);
10693
10694       if (IS_KEY(element))
10695       {
10696         for (i = 0; i < MAX_PLAYERS; i++)
10697         {
10698           if (trigger_player_bits & (1 << i))
10699           {
10700             stored_player[i].key[KEY_NR(element)] = key_state;
10701
10702             DrawGameDoorValues();
10703           }
10704         }
10705       }
10706
10707       break;
10708     }
10709
10710     case CA_SET_PLAYER_SPEED:
10711     {
10712 #if 0
10713       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10714 #endif
10715
10716       for (i = 0; i < MAX_PLAYERS; i++)
10717       {
10718         if (trigger_player_bits & (1 << i))
10719         {
10720           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10721
10722           if (action_arg == CA_ARG_SPEED_FASTER &&
10723               stored_player[i].cannot_move)
10724           {
10725             action_arg_number = STEPSIZE_VERY_SLOW;
10726           }
10727           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10728                    action_arg == CA_ARG_SPEED_FASTER)
10729           {
10730             action_arg_number = 2;
10731             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10732                            CA_MODE_MULTIPLY);
10733           }
10734           else if (action_arg == CA_ARG_NUMBER_RESET)
10735           {
10736             action_arg_number = level.initial_player_stepsize[i];
10737           }
10738
10739           move_stepsize =
10740             getModifiedActionNumber(move_stepsize,
10741                                     action_mode,
10742                                     action_arg_number,
10743                                     action_arg_number_min,
10744                                     action_arg_number_max);
10745
10746           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10747         }
10748       }
10749
10750       break;
10751     }
10752
10753     case CA_SET_PLAYER_SHIELD:
10754     {
10755       for (i = 0; i < MAX_PLAYERS; i++)
10756       {
10757         if (trigger_player_bits & (1 << i))
10758         {
10759           if (action_arg == CA_ARG_SHIELD_OFF)
10760           {
10761             stored_player[i].shield_normal_time_left = 0;
10762             stored_player[i].shield_deadly_time_left = 0;
10763           }
10764           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10765           {
10766             stored_player[i].shield_normal_time_left = 999999;
10767           }
10768           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10769           {
10770             stored_player[i].shield_normal_time_left = 999999;
10771             stored_player[i].shield_deadly_time_left = 999999;
10772           }
10773         }
10774       }
10775
10776       break;
10777     }
10778
10779 #if USE_PLAYER_GRAVITY
10780     case CA_SET_PLAYER_GRAVITY:
10781     {
10782       for (i = 0; i < MAX_PLAYERS; i++)
10783       {
10784         if (trigger_player_bits & (1 << i))
10785         {
10786           stored_player[i].gravity =
10787             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10788              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10789              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10790              stored_player[i].gravity);
10791         }
10792       }
10793
10794       break;
10795     }
10796 #endif
10797
10798     case CA_SET_PLAYER_ARTWORK:
10799     {
10800       for (i = 0; i < MAX_PLAYERS; i++)
10801       {
10802         if (trigger_player_bits & (1 << i))
10803         {
10804           int artwork_element = action_arg_element;
10805
10806           if (action_arg == CA_ARG_ELEMENT_RESET)
10807             artwork_element =
10808               (level.use_artwork_element[i] ? level.artwork_element[i] :
10809                stored_player[i].element_nr);
10810
10811 #if USE_GFX_RESET_PLAYER_ARTWORK
10812           if (stored_player[i].artwork_element != artwork_element)
10813             stored_player[i].Frame = 0;
10814 #endif
10815
10816           stored_player[i].artwork_element = artwork_element;
10817
10818           SetPlayerWaiting(&stored_player[i], FALSE);
10819
10820           /* set number of special actions for bored and sleeping animation */
10821           stored_player[i].num_special_action_bored =
10822             get_num_special_action(artwork_element,
10823                                    ACTION_BORING_1, ACTION_BORING_LAST);
10824           stored_player[i].num_special_action_sleeping =
10825             get_num_special_action(artwork_element,
10826                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10827         }
10828       }
10829
10830       break;
10831     }
10832
10833     case CA_SET_PLAYER_INVENTORY:
10834     {
10835       for (i = 0; i < MAX_PLAYERS; i++)
10836       {
10837         struct PlayerInfo *player = &stored_player[i];
10838         int j, k;
10839
10840         if (trigger_player_bits & (1 << i))
10841         {
10842           int inventory_element = action_arg_element;
10843
10844           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10845               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10846               action_arg == CA_ARG_ELEMENT_ACTION)
10847           {
10848             int element = inventory_element;
10849             int collect_count = element_info[element].collect_count_initial;
10850
10851             if (!IS_CUSTOM_ELEMENT(element))
10852               collect_count = 1;
10853
10854             if (collect_count == 0)
10855               player->inventory_infinite_element = element;
10856             else
10857               for (k = 0; k < collect_count; k++)
10858                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10859                   player->inventory_element[player->inventory_size++] =
10860                     element;
10861           }
10862           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10863                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10864                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10865           {
10866             if (player->inventory_infinite_element != EL_UNDEFINED &&
10867                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10868                                      action_arg_element_raw))
10869               player->inventory_infinite_element = EL_UNDEFINED;
10870
10871             for (k = 0, j = 0; j < player->inventory_size; j++)
10872             {
10873               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10874                                         action_arg_element_raw))
10875                 player->inventory_element[k++] = player->inventory_element[j];
10876             }
10877
10878             player->inventory_size = k;
10879           }
10880           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10881           {
10882             if (player->inventory_size > 0)
10883             {
10884               for (j = 0; j < player->inventory_size - 1; j++)
10885                 player->inventory_element[j] = player->inventory_element[j + 1];
10886
10887               player->inventory_size--;
10888             }
10889           }
10890           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10891           {
10892             if (player->inventory_size > 0)
10893               player->inventory_size--;
10894           }
10895           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10896           {
10897             player->inventory_infinite_element = EL_UNDEFINED;
10898             player->inventory_size = 0;
10899           }
10900           else if (action_arg == CA_ARG_INVENTORY_RESET)
10901           {
10902             player->inventory_infinite_element = EL_UNDEFINED;
10903             player->inventory_size = 0;
10904
10905             if (level.use_initial_inventory[i])
10906             {
10907               for (j = 0; j < level.initial_inventory_size[i]; j++)
10908               {
10909                 int element = level.initial_inventory_content[i][j];
10910                 int collect_count = element_info[element].collect_count_initial;
10911
10912                 if (!IS_CUSTOM_ELEMENT(element))
10913                   collect_count = 1;
10914
10915                 if (collect_count == 0)
10916                   player->inventory_infinite_element = element;
10917                 else
10918                   for (k = 0; k < collect_count; k++)
10919                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10920                       player->inventory_element[player->inventory_size++] =
10921                         element;
10922               }
10923             }
10924           }
10925         }
10926       }
10927
10928       break;
10929     }
10930
10931     /* ---------- CE actions  ---------------------------------------------- */
10932
10933     case CA_SET_CE_VALUE:
10934     {
10935 #if USE_NEW_CUSTOM_VALUE
10936       int last_ce_value = CustomValue[x][y];
10937
10938       CustomValue[x][y] = action_arg_number_new;
10939
10940       if (CustomValue[x][y] != last_ce_value)
10941       {
10942         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10943         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10944
10945         if (CustomValue[x][y] == 0)
10946         {
10947           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10948           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10949         }
10950       }
10951 #endif
10952
10953       break;
10954     }
10955
10956     case CA_SET_CE_SCORE:
10957     {
10958 #if USE_NEW_CUSTOM_VALUE
10959       int last_ce_score = ei->collect_score;
10960
10961       ei->collect_score = action_arg_number_new;
10962
10963       if (ei->collect_score != last_ce_score)
10964       {
10965         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10966         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10967
10968         if (ei->collect_score == 0)
10969         {
10970           int xx, yy;
10971
10972           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10973           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10974
10975           /*
10976             This is a very special case that seems to be a mixture between
10977             CheckElementChange() and CheckTriggeredElementChange(): while
10978             the first one only affects single elements that are triggered
10979             directly, the second one affects multiple elements in the playfield
10980             that are triggered indirectly by another element. This is a third
10981             case: Changing the CE score always affects multiple identical CEs,
10982             so every affected CE must be checked, not only the single CE for
10983             which the CE score was changed in the first place (as every instance
10984             of that CE shares the same CE score, and therefore also can change)!
10985           */
10986           SCAN_PLAYFIELD(xx, yy)
10987           {
10988             if (Feld[xx][yy] == element)
10989               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10990                                  CE_SCORE_GETS_ZERO);
10991           }
10992         }
10993       }
10994 #endif
10995
10996       break;
10997     }
10998
10999     case CA_SET_CE_ARTWORK:
11000     {
11001       int artwork_element = action_arg_element;
11002       boolean reset_frame = FALSE;
11003       int xx, yy;
11004
11005       if (action_arg == CA_ARG_ELEMENT_RESET)
11006         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11007                            element);
11008
11009       if (ei->gfx_element != artwork_element)
11010         reset_frame = TRUE;
11011
11012       ei->gfx_element = artwork_element;
11013
11014       SCAN_PLAYFIELD(xx, yy)
11015       {
11016         if (Feld[xx][yy] == element)
11017         {
11018           if (reset_frame)
11019           {
11020             ResetGfxAnimation(xx, yy);
11021             ResetRandomAnimationValue(xx, yy);
11022           }
11023
11024           TEST_DrawLevelField(xx, yy);
11025         }
11026       }
11027
11028       break;
11029     }
11030
11031     /* ---------- engine actions  ------------------------------------------ */
11032
11033     case CA_SET_ENGINE_SCAN_MODE:
11034     {
11035       InitPlayfieldScanMode(action_arg);
11036
11037       break;
11038     }
11039
11040     default:
11041       break;
11042   }
11043 }
11044
11045 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11046 {
11047   int old_element = Feld[x][y];
11048   int new_element = GetElementFromGroupElement(element);
11049   int previous_move_direction = MovDir[x][y];
11050 #if USE_NEW_CUSTOM_VALUE
11051   int last_ce_value = CustomValue[x][y];
11052 #endif
11053   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11054   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11055   boolean add_player_onto_element = (new_element_is_player &&
11056 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11057                                      /* this breaks SnakeBite when a snake is
11058                                         halfway through a door that closes */
11059                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11060                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11061 #endif
11062                                      IS_WALKABLE(old_element));
11063
11064 #if 0
11065   /* check if element under the player changes from accessible to unaccessible
11066      (needed for special case of dropping element which then changes) */
11067   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11068       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11069   {
11070     Bang(x, y);
11071
11072     return;
11073   }
11074 #endif
11075
11076   if (!add_player_onto_element)
11077   {
11078     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11079       RemoveMovingField(x, y);
11080     else
11081       RemoveField(x, y);
11082
11083     Feld[x][y] = new_element;
11084
11085 #if !USE_GFX_RESET_GFX_ANIMATION
11086     ResetGfxAnimation(x, y);
11087     ResetRandomAnimationValue(x, y);
11088 #endif
11089
11090     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11091       MovDir[x][y] = previous_move_direction;
11092
11093 #if USE_NEW_CUSTOM_VALUE
11094     if (element_info[new_element].use_last_ce_value)
11095       CustomValue[x][y] = last_ce_value;
11096 #endif
11097
11098     InitField_WithBug1(x, y, FALSE);
11099
11100     new_element = Feld[x][y];   /* element may have changed */
11101
11102 #if USE_GFX_RESET_GFX_ANIMATION
11103     ResetGfxAnimation(x, y);
11104     ResetRandomAnimationValue(x, y);
11105 #endif
11106
11107     TEST_DrawLevelField(x, y);
11108
11109     if (GFX_CRUMBLED(new_element))
11110       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11111   }
11112
11113 #if 1
11114   /* check if element under the player changes from accessible to unaccessible
11115      (needed for special case of dropping element which then changes) */
11116   /* (must be checked after creating new element for walkable group elements) */
11117 #if USE_FIX_KILLED_BY_NON_WALKABLE
11118   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11119       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11120   {
11121     Bang(x, y);
11122
11123     return;
11124   }
11125 #else
11126   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11127       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11128   {
11129     Bang(x, y);
11130
11131     return;
11132   }
11133 #endif
11134 #endif
11135
11136   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11137   if (new_element_is_player)
11138     RelocatePlayer(x, y, new_element);
11139
11140   if (is_change)
11141     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11142
11143   TestIfBadThingTouchesPlayer(x, y);
11144   TestIfPlayerTouchesCustomElement(x, y);
11145   TestIfElementTouchesCustomElement(x, y);
11146 }
11147
11148 static void CreateField(int x, int y, int element)
11149 {
11150   CreateFieldExt(x, y, element, FALSE);
11151 }
11152
11153 static void CreateElementFromChange(int x, int y, int element)
11154 {
11155   element = GET_VALID_RUNTIME_ELEMENT(element);
11156
11157 #if USE_STOP_CHANGED_ELEMENTS
11158   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11159   {
11160     int old_element = Feld[x][y];
11161
11162     /* prevent changed element from moving in same engine frame
11163        unless both old and new element can either fall or move */
11164     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11165         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11166       Stop[x][y] = TRUE;
11167   }
11168 #endif
11169
11170   CreateFieldExt(x, y, element, TRUE);
11171 }
11172
11173 static boolean ChangeElement(int x, int y, int element, int page)
11174 {
11175   struct ElementInfo *ei = &element_info[element];
11176   struct ElementChangeInfo *change = &ei->change_page[page];
11177   int ce_value = CustomValue[x][y];
11178   int ce_score = ei->collect_score;
11179   int target_element;
11180   int old_element = Feld[x][y];
11181
11182   /* always use default change event to prevent running into a loop */
11183   if (ChangeEvent[x][y] == -1)
11184     ChangeEvent[x][y] = CE_DELAY;
11185
11186   if (ChangeEvent[x][y] == CE_DELAY)
11187   {
11188     /* reset actual trigger element, trigger player and action element */
11189     change->actual_trigger_element = EL_EMPTY;
11190     change->actual_trigger_player = EL_EMPTY;
11191     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11192     change->actual_trigger_side = CH_SIDE_NONE;
11193     change->actual_trigger_ce_value = 0;
11194     change->actual_trigger_ce_score = 0;
11195   }
11196
11197   /* do not change elements more than a specified maximum number of changes */
11198   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11199     return FALSE;
11200
11201   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11202
11203   if (change->explode)
11204   {
11205     Bang(x, y);
11206
11207     return TRUE;
11208   }
11209
11210   if (change->use_target_content)
11211   {
11212     boolean complete_replace = TRUE;
11213     boolean can_replace[3][3];
11214     int xx, yy;
11215
11216     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11217     {
11218       boolean is_empty;
11219       boolean is_walkable;
11220       boolean is_diggable;
11221       boolean is_collectible;
11222       boolean is_removable;
11223       boolean is_destructible;
11224       int ex = x + xx - 1;
11225       int ey = y + yy - 1;
11226       int content_element = change->target_content.e[xx][yy];
11227       int e;
11228
11229       can_replace[xx][yy] = TRUE;
11230
11231       if (ex == x && ey == y)   /* do not check changing element itself */
11232         continue;
11233
11234       if (content_element == EL_EMPTY_SPACE)
11235       {
11236         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11237
11238         continue;
11239       }
11240
11241       if (!IN_LEV_FIELD(ex, ey))
11242       {
11243         can_replace[xx][yy] = FALSE;
11244         complete_replace = FALSE;
11245
11246         continue;
11247       }
11248
11249       e = Feld[ex][ey];
11250
11251       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11252         e = MovingOrBlocked2Element(ex, ey);
11253
11254       is_empty = (IS_FREE(ex, ey) ||
11255                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11256
11257       is_walkable     = (is_empty || IS_WALKABLE(e));
11258       is_diggable     = (is_empty || IS_DIGGABLE(e));
11259       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11260       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11261       is_removable    = (is_diggable || is_collectible);
11262
11263       can_replace[xx][yy] =
11264         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11265           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11266           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11267           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11268           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11269           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11270          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11271
11272       if (!can_replace[xx][yy])
11273         complete_replace = FALSE;
11274     }
11275
11276     if (!change->only_if_complete || complete_replace)
11277     {
11278       boolean something_has_changed = FALSE;
11279
11280       if (change->only_if_complete && change->use_random_replace &&
11281           RND(100) < change->random_percentage)
11282         return FALSE;
11283
11284       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11285       {
11286         int ex = x + xx - 1;
11287         int ey = y + yy - 1;
11288         int content_element;
11289
11290         if (can_replace[xx][yy] && (!change->use_random_replace ||
11291                                     RND(100) < change->random_percentage))
11292         {
11293           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11294             RemoveMovingField(ex, ey);
11295
11296           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11297
11298           content_element = change->target_content.e[xx][yy];
11299           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11300                                               ce_value, ce_score);
11301
11302           CreateElementFromChange(ex, ey, target_element);
11303
11304           something_has_changed = TRUE;
11305
11306           /* for symmetry reasons, freeze newly created border elements */
11307           if (ex != x || ey != y)
11308             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11309         }
11310       }
11311
11312       if (something_has_changed)
11313       {
11314         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11315         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11316       }
11317     }
11318   }
11319   else
11320   {
11321     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11322                                         ce_value, ce_score);
11323
11324     if (element == EL_DIAGONAL_GROWING ||
11325         element == EL_DIAGONAL_SHRINKING)
11326     {
11327       target_element = Store[x][y];
11328
11329       Store[x][y] = EL_EMPTY;
11330     }
11331
11332     CreateElementFromChange(x, y, target_element);
11333
11334     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11335     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11336   }
11337
11338   /* this uses direct change before indirect change */
11339   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11340
11341   return TRUE;
11342 }
11343
11344 #if USE_NEW_DELAYED_ACTION
11345
11346 static void HandleElementChange(int x, int y, int page)
11347 {
11348   int element = MovingOrBlocked2Element(x, y);
11349   struct ElementInfo *ei = &element_info[element];
11350   struct ElementChangeInfo *change = &ei->change_page[page];
11351   boolean handle_action_before_change = FALSE;
11352
11353 #ifdef DEBUG
11354   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11355       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11356   {
11357     printf("\n\n");
11358     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11359            x, y, element, element_info[element].token_name);
11360     printf("HandleElementChange(): This should never happen!\n");
11361     printf("\n\n");
11362   }
11363 #endif
11364
11365   /* this can happen with classic bombs on walkable, changing elements */
11366   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11367   {
11368 #if 0
11369     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11370       ChangeDelay[x][y] = 0;
11371 #endif
11372
11373     return;
11374   }
11375
11376   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11377   {
11378     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11379
11380     if (change->can_change)
11381     {
11382 #if 1
11383       /* !!! not clear why graphic animation should be reset at all here !!! */
11384       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11385 #if USE_GFX_RESET_WHEN_NOT_MOVING
11386       /* when a custom element is about to change (for example by change delay),
11387          do not reset graphic animation when the custom element is moving */
11388       if (!IS_MOVING(x, y))
11389 #endif
11390       {
11391         ResetGfxAnimation(x, y);
11392         ResetRandomAnimationValue(x, y);
11393       }
11394 #endif
11395
11396       if (change->pre_change_function)
11397         change->pre_change_function(x, y);
11398     }
11399   }
11400
11401   ChangeDelay[x][y]--;
11402
11403   if (ChangeDelay[x][y] != 0)           /* continue element change */
11404   {
11405     if (change->can_change)
11406     {
11407       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11408
11409       if (IS_ANIMATED(graphic))
11410         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11411
11412       if (change->change_function)
11413         change->change_function(x, y);
11414     }
11415   }
11416   else                                  /* finish element change */
11417   {
11418     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11419     {
11420       page = ChangePage[x][y];
11421       ChangePage[x][y] = -1;
11422
11423       change = &ei->change_page[page];
11424     }
11425
11426     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11427     {
11428       ChangeDelay[x][y] = 1;            /* try change after next move step */
11429       ChangePage[x][y] = page;          /* remember page to use for change */
11430
11431       return;
11432     }
11433
11434 #if 1
11435     /* special case: set new level random seed before changing element */
11436     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11437       handle_action_before_change = TRUE;
11438
11439     if (change->has_action && handle_action_before_change)
11440       ExecuteCustomElementAction(x, y, element, page);
11441 #endif
11442
11443     if (change->can_change)
11444     {
11445       if (ChangeElement(x, y, element, page))
11446       {
11447         if (change->post_change_function)
11448           change->post_change_function(x, y);
11449       }
11450     }
11451
11452     if (change->has_action && !handle_action_before_change)
11453       ExecuteCustomElementAction(x, y, element, page);
11454   }
11455 }
11456
11457 #else
11458
11459 static void HandleElementChange(int x, int y, int page)
11460 {
11461   int element = MovingOrBlocked2Element(x, y);
11462   struct ElementInfo *ei = &element_info[element];
11463   struct ElementChangeInfo *change = &ei->change_page[page];
11464
11465 #ifdef DEBUG
11466   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11467   {
11468     printf("\n\n");
11469     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11470            x, y, element, element_info[element].token_name);
11471     printf("HandleElementChange(): This should never happen!\n");
11472     printf("\n\n");
11473   }
11474 #endif
11475
11476   /* this can happen with classic bombs on walkable, changing elements */
11477   if (!CAN_CHANGE(element))
11478   {
11479 #if 0
11480     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11481       ChangeDelay[x][y] = 0;
11482 #endif
11483
11484     return;
11485   }
11486
11487   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11488   {
11489     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11490
11491     ResetGfxAnimation(x, y);
11492     ResetRandomAnimationValue(x, y);
11493
11494     if (change->pre_change_function)
11495       change->pre_change_function(x, y);
11496   }
11497
11498   ChangeDelay[x][y]--;
11499
11500   if (ChangeDelay[x][y] != 0)           /* continue element change */
11501   {
11502     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11503
11504     if (IS_ANIMATED(graphic))
11505       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506
11507     if (change->change_function)
11508       change->change_function(x, y);
11509   }
11510   else                                  /* finish element change */
11511   {
11512     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11513     {
11514       page = ChangePage[x][y];
11515       ChangePage[x][y] = -1;
11516
11517       change = &ei->change_page[page];
11518     }
11519
11520     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11521     {
11522       ChangeDelay[x][y] = 1;            /* try change after next move step */
11523       ChangePage[x][y] = page;          /* remember page to use for change */
11524
11525       return;
11526     }
11527
11528     if (ChangeElement(x, y, element, page))
11529     {
11530       if (change->post_change_function)
11531         change->post_change_function(x, y);
11532     }
11533   }
11534 }
11535
11536 #endif
11537
11538 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11539                                               int trigger_element,
11540                                               int trigger_event,
11541                                               int trigger_player,
11542                                               int trigger_side,
11543                                               int trigger_page)
11544 {
11545   boolean change_done_any = FALSE;
11546   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11547   int i;
11548
11549   if (!(trigger_events[trigger_element][trigger_event]))
11550     return FALSE;
11551
11552 #if 0
11553   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11554          trigger_event, recursion_loop_depth, recursion_loop_detected,
11555          recursion_loop_element, EL_NAME(recursion_loop_element));
11556 #endif
11557
11558   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11559
11560   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11561   {
11562     int element = EL_CUSTOM_START + i;
11563     boolean change_done = FALSE;
11564     int p;
11565
11566     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11567         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11568       continue;
11569
11570     for (p = 0; p < element_info[element].num_change_pages; p++)
11571     {
11572       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11573
11574       if (change->can_change_or_has_action &&
11575           change->has_event[trigger_event] &&
11576           change->trigger_side & trigger_side &&
11577           change->trigger_player & trigger_player &&
11578           change->trigger_page & trigger_page_bits &&
11579           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11580       {
11581         change->actual_trigger_element = trigger_element;
11582         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11583         change->actual_trigger_player_bits = trigger_player;
11584         change->actual_trigger_side = trigger_side;
11585         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11586         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11587
11588 #if 0
11589         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11590                element, EL_NAME(element), p);
11591 #endif
11592
11593         if ((change->can_change && !change_done) || change->has_action)
11594         {
11595           int x, y;
11596
11597           SCAN_PLAYFIELD(x, y)
11598           {
11599             if (Feld[x][y] == element)
11600             {
11601               if (change->can_change && !change_done)
11602               {
11603 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11604                 /* if element already changed in this frame, not only prevent
11605                    another element change (checked in ChangeElement()), but
11606                    also prevent additional element actions for this element */
11607
11608                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11609                     !level.use_action_after_change_bug)
11610                   continue;
11611 #endif
11612
11613 #if 0
11614                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11615                        element, EL_NAME(element), p);
11616 #endif
11617
11618                 ChangeDelay[x][y] = 1;
11619                 ChangeEvent[x][y] = trigger_event;
11620
11621                 HandleElementChange(x, y, p);
11622               }
11623 #if USE_NEW_DELAYED_ACTION
11624               else if (change->has_action)
11625               {
11626 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11627                 /* if element already changed in this frame, not only prevent
11628                    another element change (checked in ChangeElement()), but
11629                    also prevent additional element actions for this element */
11630
11631                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11632                     !level.use_action_after_change_bug)
11633                   continue;
11634 #endif
11635
11636
11637 #if 0
11638                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11639                        element, EL_NAME(element), p);
11640 #endif
11641
11642                 ExecuteCustomElementAction(x, y, element, p);
11643                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11644               }
11645 #else
11646               if (change->has_action)
11647               {
11648                 ExecuteCustomElementAction(x, y, element, p);
11649                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11650               }
11651 #endif
11652             }
11653           }
11654
11655           if (change->can_change)
11656           {
11657             change_done = TRUE;
11658             change_done_any = TRUE;
11659
11660 #if 0
11661             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11662                    element, EL_NAME(element), p);
11663 #endif
11664
11665           }
11666         }
11667       }
11668     }
11669   }
11670
11671   RECURSION_LOOP_DETECTION_END();
11672
11673   return change_done_any;
11674 }
11675
11676 static boolean CheckElementChangeExt(int x, int y,
11677                                      int element,
11678                                      int trigger_element,
11679                                      int trigger_event,
11680                                      int trigger_player,
11681                                      int trigger_side)
11682 {
11683   boolean change_done = FALSE;
11684   int p;
11685
11686   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11687       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11688     return FALSE;
11689
11690   if (Feld[x][y] == EL_BLOCKED)
11691   {
11692     Blocked2Moving(x, y, &x, &y);
11693     element = Feld[x][y];
11694   }
11695
11696 #if 0
11697   /* check if element has already changed */
11698   if (Feld[x][y] != element)
11699     return FALSE;
11700 #else
11701   /* check if element has already changed or is about to change after moving */
11702   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11703        Feld[x][y] != element) ||
11704
11705       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11706        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11707         ChangePage[x][y] != -1)))
11708     return FALSE;
11709 #endif
11710
11711 #if 0
11712   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11713          trigger_event, recursion_loop_depth, recursion_loop_detected,
11714          recursion_loop_element, EL_NAME(recursion_loop_element));
11715 #endif
11716
11717   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11718
11719 #if 0
11720   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11721 #endif
11722
11723   for (p = 0; p < element_info[element].num_change_pages; p++)
11724   {
11725     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11726
11727     /* check trigger element for all events where the element that is checked
11728        for changing interacts with a directly adjacent element -- this is
11729        different to element changes that affect other elements to change on the
11730        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11731     boolean check_trigger_element =
11732       (trigger_event == CE_TOUCHING_X ||
11733        trigger_event == CE_HITTING_X ||
11734        trigger_event == CE_HIT_BY_X ||
11735 #if 1
11736        /* this one was forgotten until 3.2.3 */
11737        trigger_event == CE_DIGGING_X);
11738 #endif
11739
11740     if (change->can_change_or_has_action &&
11741         change->has_event[trigger_event] &&
11742         change->trigger_side & trigger_side &&
11743         change->trigger_player & trigger_player &&
11744         (!check_trigger_element ||
11745          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11746     {
11747       change->actual_trigger_element = trigger_element;
11748       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11749       change->actual_trigger_player_bits = trigger_player;
11750       change->actual_trigger_side = trigger_side;
11751       change->actual_trigger_ce_value = CustomValue[x][y];
11752       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11753
11754       /* special case: trigger element not at (x,y) position for some events */
11755       if (check_trigger_element)
11756       {
11757         static struct
11758         {
11759           int dx, dy;
11760         } move_xy[] =
11761           {
11762             {  0,  0 },
11763             { -1,  0 },
11764             { +1,  0 },
11765             {  0,  0 },
11766             {  0, -1 },
11767             {  0,  0 }, { 0, 0 }, { 0, 0 },
11768             {  0, +1 }
11769           };
11770
11771         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11772         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11773
11774         change->actual_trigger_ce_value = CustomValue[xx][yy];
11775         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11776       }
11777
11778       if (change->can_change && !change_done)
11779       {
11780         ChangeDelay[x][y] = 1;
11781         ChangeEvent[x][y] = trigger_event;
11782
11783         HandleElementChange(x, y, p);
11784
11785         change_done = TRUE;
11786       }
11787 #if USE_NEW_DELAYED_ACTION
11788       else if (change->has_action)
11789       {
11790         ExecuteCustomElementAction(x, y, element, p);
11791         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11792       }
11793 #else
11794       if (change->has_action)
11795       {
11796         ExecuteCustomElementAction(x, y, element, p);
11797         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11798       }
11799 #endif
11800     }
11801   }
11802
11803   RECURSION_LOOP_DETECTION_END();
11804
11805   return change_done;
11806 }
11807
11808 static void PlayPlayerSound(struct PlayerInfo *player)
11809 {
11810   int jx = player->jx, jy = player->jy;
11811   int sound_element = player->artwork_element;
11812   int last_action = player->last_action_waiting;
11813   int action = player->action_waiting;
11814
11815   if (player->is_waiting)
11816   {
11817     if (action != last_action)
11818       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11819     else
11820       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11821   }
11822   else
11823   {
11824     if (action != last_action)
11825       StopSound(element_info[sound_element].sound[last_action]);
11826
11827     if (last_action == ACTION_SLEEPING)
11828       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11829   }
11830 }
11831
11832 static void PlayAllPlayersSound()
11833 {
11834   int i;
11835
11836   for (i = 0; i < MAX_PLAYERS; i++)
11837     if (stored_player[i].active)
11838       PlayPlayerSound(&stored_player[i]);
11839 }
11840
11841 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11842 {
11843   boolean last_waiting = player->is_waiting;
11844   int move_dir = player->MovDir;
11845
11846   player->dir_waiting = move_dir;
11847   player->last_action_waiting = player->action_waiting;
11848
11849   if (is_waiting)
11850   {
11851     if (!last_waiting)          /* not waiting -> waiting */
11852     {
11853       player->is_waiting = TRUE;
11854
11855       player->frame_counter_bored =
11856         FrameCounter +
11857         game.player_boring_delay_fixed +
11858         GetSimpleRandom(game.player_boring_delay_random);
11859       player->frame_counter_sleeping =
11860         FrameCounter +
11861         game.player_sleeping_delay_fixed +
11862         GetSimpleRandom(game.player_sleeping_delay_random);
11863
11864       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11865     }
11866
11867     if (game.player_sleeping_delay_fixed +
11868         game.player_sleeping_delay_random > 0 &&
11869         player->anim_delay_counter == 0 &&
11870         player->post_delay_counter == 0 &&
11871         FrameCounter >= player->frame_counter_sleeping)
11872       player->is_sleeping = TRUE;
11873     else if (game.player_boring_delay_fixed +
11874              game.player_boring_delay_random > 0 &&
11875              FrameCounter >= player->frame_counter_bored)
11876       player->is_bored = TRUE;
11877
11878     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11879                               player->is_bored ? ACTION_BORING :
11880                               ACTION_WAITING);
11881
11882     if (player->is_sleeping && player->use_murphy)
11883     {
11884       /* special case for sleeping Murphy when leaning against non-free tile */
11885
11886       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11887           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11888            !IS_MOVING(player->jx - 1, player->jy)))
11889         move_dir = MV_LEFT;
11890       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11891                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11892                 !IS_MOVING(player->jx + 1, player->jy)))
11893         move_dir = MV_RIGHT;
11894       else
11895         player->is_sleeping = FALSE;
11896
11897       player->dir_waiting = move_dir;
11898     }
11899
11900     if (player->is_sleeping)
11901     {
11902       if (player->num_special_action_sleeping > 0)
11903       {
11904         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11905         {
11906           int last_special_action = player->special_action_sleeping;
11907           int num_special_action = player->num_special_action_sleeping;
11908           int special_action =
11909             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11910              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11911              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11912              last_special_action + 1 : ACTION_SLEEPING);
11913           int special_graphic =
11914             el_act_dir2img(player->artwork_element, special_action, move_dir);
11915
11916           player->anim_delay_counter =
11917             graphic_info[special_graphic].anim_delay_fixed +
11918             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11919           player->post_delay_counter =
11920             graphic_info[special_graphic].post_delay_fixed +
11921             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11922
11923           player->special_action_sleeping = special_action;
11924         }
11925
11926         if (player->anim_delay_counter > 0)
11927         {
11928           player->action_waiting = player->special_action_sleeping;
11929           player->anim_delay_counter--;
11930         }
11931         else if (player->post_delay_counter > 0)
11932         {
11933           player->post_delay_counter--;
11934         }
11935       }
11936     }
11937     else if (player->is_bored)
11938     {
11939       if (player->num_special_action_bored > 0)
11940       {
11941         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11942         {
11943           int special_action =
11944             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11945           int special_graphic =
11946             el_act_dir2img(player->artwork_element, special_action, move_dir);
11947
11948           player->anim_delay_counter =
11949             graphic_info[special_graphic].anim_delay_fixed +
11950             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11951           player->post_delay_counter =
11952             graphic_info[special_graphic].post_delay_fixed +
11953             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11954
11955           player->special_action_bored = special_action;
11956         }
11957
11958         if (player->anim_delay_counter > 0)
11959         {
11960           player->action_waiting = player->special_action_bored;
11961           player->anim_delay_counter--;
11962         }
11963         else if (player->post_delay_counter > 0)
11964         {
11965           player->post_delay_counter--;
11966         }
11967       }
11968     }
11969   }
11970   else if (last_waiting)        /* waiting -> not waiting */
11971   {
11972     player->is_waiting = FALSE;
11973     player->is_bored = FALSE;
11974     player->is_sleeping = FALSE;
11975
11976     player->frame_counter_bored = -1;
11977     player->frame_counter_sleeping = -1;
11978
11979     player->anim_delay_counter = 0;
11980     player->post_delay_counter = 0;
11981
11982     player->dir_waiting = player->MovDir;
11983     player->action_waiting = ACTION_DEFAULT;
11984
11985     player->special_action_bored = ACTION_DEFAULT;
11986     player->special_action_sleeping = ACTION_DEFAULT;
11987   }
11988 }
11989
11990 static void CheckSingleStepMode(struct PlayerInfo *player)
11991 {
11992   if (tape.single_step && tape.recording && !tape.pausing)
11993   {
11994     /* as it is called "single step mode", just return to pause mode when the
11995        player stopped moving after one tile (or never starts moving at all) */
11996     if (!player->is_moving && !player->is_pushing)
11997     {
11998       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11999       SnapField(player, 0, 0);                  /* stop snapping */
12000     }
12001   }
12002 }
12003
12004 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12005 {
12006   int left      = player_action & JOY_LEFT;
12007   int right     = player_action & JOY_RIGHT;
12008   int up        = player_action & JOY_UP;
12009   int down      = player_action & JOY_DOWN;
12010   int button1   = player_action & JOY_BUTTON_1;
12011   int button2   = player_action & JOY_BUTTON_2;
12012   int dx        = (left ? -1 : right ? 1 : 0);
12013   int dy        = (up   ? -1 : down  ? 1 : 0);
12014
12015   if (!player->active || tape.pausing)
12016     return 0;
12017
12018   if (player_action)
12019   {
12020     if (button1)
12021       SnapField(player, dx, dy);
12022     else
12023     {
12024       if (button2)
12025         DropElement(player);
12026
12027       MovePlayer(player, dx, dy);
12028     }
12029
12030     CheckSingleStepMode(player);
12031
12032     SetPlayerWaiting(player, FALSE);
12033
12034     return player_action;
12035   }
12036   else
12037   {
12038     /* no actions for this player (no input at player's configured device) */
12039
12040     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12041     SnapField(player, 0, 0);
12042     CheckGravityMovementWhenNotMoving(player);
12043
12044     if (player->MovPos == 0)
12045       SetPlayerWaiting(player, TRUE);
12046
12047     if (player->MovPos == 0)    /* needed for tape.playing */
12048       player->is_moving = FALSE;
12049
12050     player->is_dropping = FALSE;
12051     player->is_dropping_pressed = FALSE;
12052     player->drop_pressed_delay = 0;
12053
12054     CheckSingleStepMode(player);
12055
12056     return 0;
12057   }
12058 }
12059
12060 static void CheckLevelTime()
12061 {
12062   int i;
12063
12064   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12065   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12066   {
12067     if (level.native_em_level->lev->home == 0)  /* all players at home */
12068     {
12069       PlayerWins(local_player);
12070
12071       AllPlayersGone = TRUE;
12072
12073       level.native_em_level->lev->home = -1;
12074     }
12075
12076     if (level.native_em_level->ply[0]->alive == 0 &&
12077         level.native_em_level->ply[1]->alive == 0 &&
12078         level.native_em_level->ply[2]->alive == 0 &&
12079         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12080       AllPlayersGone = TRUE;
12081   }
12082   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12083   {
12084     if (game_sp.LevelSolved &&
12085         !game_sp.GameOver)                              /* game won */
12086     {
12087       PlayerWins(local_player);
12088
12089       game_sp.GameOver = TRUE;
12090
12091       AllPlayersGone = TRUE;
12092     }
12093
12094     if (game_sp.GameOver)                               /* game lost */
12095       AllPlayersGone = TRUE;
12096   }
12097
12098   if (TimeFrames >= FRAMES_PER_SECOND)
12099   {
12100     TimeFrames = 0;
12101     TapeTime++;
12102
12103     for (i = 0; i < MAX_PLAYERS; i++)
12104     {
12105       struct PlayerInfo *player = &stored_player[i];
12106
12107       if (SHIELD_ON(player))
12108       {
12109         player->shield_normal_time_left--;
12110
12111         if (player->shield_deadly_time_left > 0)
12112           player->shield_deadly_time_left--;
12113       }
12114     }
12115
12116     if (!local_player->LevelSolved && !level.use_step_counter)
12117     {
12118       TimePlayed++;
12119
12120       if (TimeLeft > 0)
12121       {
12122         TimeLeft--;
12123
12124         if (TimeLeft <= 10 && setup.time_limit)
12125           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12126
12127 #if 1
12128         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12129            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12130
12131         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12132
12133         /* (already called by UpdateAndDisplayGameControlValues() below) */
12134         // DisplayGameControlValues();
12135 #else
12136         DrawGameValue_Time(TimeLeft);
12137 #endif
12138
12139         if (!TimeLeft && setup.time_limit)
12140         {
12141           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12142             level.native_em_level->lev->killed_out_of_time = TRUE;
12143           else
12144             for (i = 0; i < MAX_PLAYERS; i++)
12145               KillPlayer(&stored_player[i]);
12146         }
12147       }
12148 #if 1
12149       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12150       {
12151         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12152
12153         /* (already called by UpdateAndDisplayGameControlValues() below) */
12154         // DisplayGameControlValues();
12155       }
12156 #else
12157       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12158         DrawGameValue_Time(TimePlayed);
12159 #endif
12160
12161       level.native_em_level->lev->time =
12162         (game.no_time_limit ? TimePlayed : TimeLeft);
12163     }
12164
12165     if (tape.recording || tape.playing)
12166       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12167   }
12168
12169 #if 1
12170   UpdateAndDisplayGameControlValues();
12171 #else
12172   UpdateGameDoorValues();
12173   DrawGameDoorValues();
12174 #endif
12175 }
12176
12177 void AdvanceFrameAndPlayerCounters(int player_nr)
12178 {
12179   int i;
12180
12181   /* advance frame counters (global frame counter and time frame counter) */
12182   FrameCounter++;
12183   TimeFrames++;
12184
12185   /* advance player counters (counters for move delay, move animation etc.) */
12186   for (i = 0; i < MAX_PLAYERS; i++)
12187   {
12188     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12189     int move_delay_value = stored_player[i].move_delay_value;
12190     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12191
12192     if (!advance_player_counters)       /* not all players may be affected */
12193       continue;
12194
12195 #if USE_NEW_PLAYER_ANIM
12196     if (move_frames == 0)       /* less than one move per game frame */
12197     {
12198       int stepsize = TILEX / move_delay_value;
12199       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12200       int count = (stored_player[i].is_moving ?
12201                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12202
12203       if (count % delay == 0)
12204         move_frames = 1;
12205     }
12206 #endif
12207
12208     stored_player[i].Frame += move_frames;
12209
12210     if (stored_player[i].MovPos != 0)
12211       stored_player[i].StepFrame += move_frames;
12212
12213     if (stored_player[i].move_delay > 0)
12214       stored_player[i].move_delay--;
12215
12216     /* due to bugs in previous versions, counter must count up, not down */
12217     if (stored_player[i].push_delay != -1)
12218       stored_player[i].push_delay++;
12219
12220     if (stored_player[i].drop_delay > 0)
12221       stored_player[i].drop_delay--;
12222
12223     if (stored_player[i].is_dropping_pressed)
12224       stored_player[i].drop_pressed_delay++;
12225   }
12226 }
12227
12228 void StartGameActions(boolean init_network_game, boolean record_tape,
12229                       int random_seed)
12230 {
12231   unsigned int new_random_seed = InitRND(random_seed);
12232
12233   if (record_tape)
12234     TapeStartRecording(new_random_seed);
12235
12236 #if defined(NETWORK_AVALIABLE)
12237   if (init_network_game)
12238   {
12239     SendToServer_StartPlaying();
12240
12241     return;
12242   }
12243 #endif
12244
12245   InitGame();
12246 }
12247
12248 void GameActions()
12249 {
12250   static unsigned int game_frame_delay = 0;
12251   unsigned int game_frame_delay_value;
12252   byte *recorded_player_action;
12253   byte summarized_player_action = 0;
12254   byte tape_action[MAX_PLAYERS];
12255   int i;
12256
12257   /* detect endless loops, caused by custom element programming */
12258   if (recursion_loop_detected && recursion_loop_depth == 0)
12259   {
12260     char *message = getStringCat3("Internal Error! Element ",
12261                                   EL_NAME(recursion_loop_element),
12262                                   " caused endless loop! Quit the game?");
12263
12264     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12265           EL_NAME(recursion_loop_element));
12266
12267     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12268
12269     recursion_loop_detected = FALSE;    /* if game should be continued */
12270
12271     free(message);
12272
12273     return;
12274   }
12275
12276   if (game.restart_level)
12277     StartGameActions(options.network, setup.autorecord, level.random_seed);
12278
12279   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12280   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12281   {
12282     if (level.native_em_level->lev->home == 0)  /* all players at home */
12283     {
12284       PlayerWins(local_player);
12285
12286       AllPlayersGone = TRUE;
12287
12288       level.native_em_level->lev->home = -1;
12289     }
12290
12291     if (level.native_em_level->ply[0]->alive == 0 &&
12292         level.native_em_level->ply[1]->alive == 0 &&
12293         level.native_em_level->ply[2]->alive == 0 &&
12294         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12295       AllPlayersGone = TRUE;
12296   }
12297   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12298   {
12299     if (game_sp.LevelSolved &&
12300         !game_sp.GameOver)                              /* game won */
12301     {
12302       PlayerWins(local_player);
12303
12304       game_sp.GameOver = TRUE;
12305
12306       AllPlayersGone = TRUE;
12307     }
12308
12309     if (game_sp.GameOver)                               /* game lost */
12310       AllPlayersGone = TRUE;
12311   }
12312
12313   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12314     GameWon();
12315
12316   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12317     TapeStop();
12318
12319   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12320     return;
12321
12322   game_frame_delay_value =
12323     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12324
12325   if (tape.playing && tape.warp_forward && !tape.pausing)
12326     game_frame_delay_value = 0;
12327
12328   /* ---------- main game synchronization point ---------- */
12329
12330   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12331
12332   if (network_playing && !network_player_action_received)
12333   {
12334     /* try to get network player actions in time */
12335
12336 #if defined(NETWORK_AVALIABLE)
12337     /* last chance to get network player actions without main loop delay */
12338     HandleNetworking();
12339 #endif
12340
12341     /* game was quit by network peer */
12342     if (game_status != GAME_MODE_PLAYING)
12343       return;
12344
12345     if (!network_player_action_received)
12346       return;           /* failed to get network player actions in time */
12347
12348     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12349   }
12350
12351   if (tape.pausing)
12352     return;
12353
12354   /* at this point we know that we really continue executing the game */
12355
12356   network_player_action_received = FALSE;
12357
12358   /* when playing tape, read previously recorded player input from tape data */
12359   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12360
12361 #if 1
12362   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12363   if (tape.pausing)
12364     return;
12365 #endif
12366
12367   if (tape.set_centered_player)
12368   {
12369     game.centered_player_nr_next = tape.centered_player_nr_next;
12370     game.set_centered_player = TRUE;
12371   }
12372
12373   for (i = 0; i < MAX_PLAYERS; i++)
12374   {
12375     summarized_player_action |= stored_player[i].action;
12376
12377 #if 1
12378     if (!network_playing && (setup.team_mode || tape.playing))
12379       stored_player[i].effective_action = stored_player[i].action;
12380 #else
12381     if (!network_playing)
12382       stored_player[i].effective_action = stored_player[i].action;
12383 #endif
12384   }
12385
12386 #if defined(NETWORK_AVALIABLE)
12387   if (network_playing)
12388     SendToServer_MovePlayer(summarized_player_action);
12389 #endif
12390
12391   if (!options.network && !setup.team_mode)
12392     local_player->effective_action = summarized_player_action;
12393
12394   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12395   {
12396     for (i = 0; i < MAX_PLAYERS; i++)
12397       stored_player[i].effective_action =
12398         (i == game.centered_player_nr ? summarized_player_action : 0);
12399   }
12400
12401   if (recorded_player_action != NULL)
12402     for (i = 0; i < MAX_PLAYERS; i++)
12403       stored_player[i].effective_action = recorded_player_action[i];
12404
12405   for (i = 0; i < MAX_PLAYERS; i++)
12406   {
12407     tape_action[i] = stored_player[i].effective_action;
12408
12409 #if 1
12410     /* (this can only happen in the R'n'D game engine) */
12411     if (setup.team_mode &&
12412         tape.recording &&
12413         tape_action[i] &&
12414         !tape.player_participates[i])
12415       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12416 #else
12417     /* (this can only happen in the R'n'D game engine) */
12418     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12419       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12420 #endif
12421   }
12422
12423   /* only record actions from input devices, but not programmed actions */
12424   if (tape.recording)
12425     TapeRecordAction(tape_action);
12426
12427 #if USE_NEW_PLAYER_ASSIGNMENTS
12428 #if 1
12429   // if (setup.team_mode)
12430   if (TEST_game_team_mode)
12431 #endif
12432   {
12433     byte mapped_action[MAX_PLAYERS];
12434
12435 #if DEBUG_PLAYER_ACTIONS
12436     printf(":::");
12437     for (i = 0; i < MAX_PLAYERS; i++)
12438       printf(" %d, ", stored_player[i].effective_action);
12439 #endif
12440
12441     for (i = 0; i < MAX_PLAYERS; i++)
12442       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12443
12444     for (i = 0; i < MAX_PLAYERS; i++)
12445       stored_player[i].effective_action = mapped_action[i];
12446
12447 #if DEBUG_PLAYER_ACTIONS
12448     printf(" =>");
12449     for (i = 0; i < MAX_PLAYERS; i++)
12450       printf(" %d, ", stored_player[i].effective_action);
12451     printf("\n");
12452 #endif
12453   }
12454 #if DEBUG_PLAYER_ACTIONS
12455   else
12456   {
12457     printf(":::");
12458     for (i = 0; i < MAX_PLAYERS; i++)
12459       printf(" %d, ", stored_player[i].effective_action);
12460     printf("\n");
12461   }
12462 #endif
12463 #endif
12464
12465 #if 0
12466   if (!options.network && !setup.team_mode)
12467     local_player->effective_action = summarized_player_action;
12468 #endif
12469
12470 #if 0
12471   printf("::: summarized_player_action == %d\n",
12472          local_player->effective_action);
12473 #endif
12474
12475
12476
12477
12478 #if 0
12479 #if DEBUG_INIT_PLAYER
12480     if (options.debug)
12481     {
12482       printf("Player status (final):\n");
12483
12484       for (i = 0; i < MAX_PLAYERS; i++)
12485       {
12486         struct PlayerInfo *player = &stored_player[i];
12487
12488         printf("- player %d: present == %d, connected == %d, active == %d",
12489                i + 1,
12490                player->present,
12491                player->connected,
12492                player->active);
12493
12494         if (local_player == player)
12495           printf(" (local player)");
12496
12497         printf("\n");
12498       }
12499     }
12500 #endif
12501 #endif
12502
12503
12504
12505   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12506   {
12507     GameActions_EM_Main();
12508   }
12509   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12510   {
12511     GameActions_SP_Main();
12512   }
12513   else
12514   {
12515     GameActions_RND();
12516   }
12517 }
12518
12519 void GameActions_EM_Main()
12520 {
12521   byte effective_action[MAX_PLAYERS];
12522   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12523   int i;
12524
12525   for (i = 0; i < MAX_PLAYERS; i++)
12526     effective_action[i] = stored_player[i].effective_action;
12527
12528   GameActions_EM(effective_action, warp_mode);
12529
12530   CheckLevelTime();
12531
12532   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12533 }
12534
12535 void GameActions_SP_Main()
12536 {
12537   byte effective_action[MAX_PLAYERS];
12538   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12539   int i;
12540
12541   for (i = 0; i < MAX_PLAYERS; i++)
12542     effective_action[i] = stored_player[i].effective_action;
12543
12544   GameActions_SP(effective_action, warp_mode);
12545
12546   CheckLevelTime();
12547
12548   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12549 }
12550
12551 void GameActions_RND()
12552 {
12553   int magic_wall_x = 0, magic_wall_y = 0;
12554   int i, x, y, element, graphic;
12555
12556   InitPlayfieldScanModeVars();
12557
12558 #if USE_ONE_MORE_CHANGE_PER_FRAME
12559   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12560   {
12561     SCAN_PLAYFIELD(x, y)
12562     {
12563       ChangeCount[x][y] = 0;
12564       ChangeEvent[x][y] = -1;
12565     }
12566   }
12567 #endif
12568
12569   if (game.set_centered_player)
12570   {
12571     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12572
12573     /* switching to "all players" only possible if all players fit to screen */
12574     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12575     {
12576       game.centered_player_nr_next = game.centered_player_nr;
12577       game.set_centered_player = FALSE;
12578     }
12579
12580     /* do not switch focus to non-existing (or non-active) player */
12581     if (game.centered_player_nr_next >= 0 &&
12582         !stored_player[game.centered_player_nr_next].active)
12583     {
12584       game.centered_player_nr_next = game.centered_player_nr;
12585       game.set_centered_player = FALSE;
12586     }
12587   }
12588
12589   if (game.set_centered_player &&
12590       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12591   {
12592     int sx, sy;
12593
12594     if (game.centered_player_nr_next == -1)
12595     {
12596       setScreenCenteredToAllPlayers(&sx, &sy);
12597     }
12598     else
12599     {
12600       sx = stored_player[game.centered_player_nr_next].jx;
12601       sy = stored_player[game.centered_player_nr_next].jy;
12602     }
12603
12604     game.centered_player_nr = game.centered_player_nr_next;
12605     game.set_centered_player = FALSE;
12606
12607     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12608     DrawGameDoorValues();
12609   }
12610
12611   for (i = 0; i < MAX_PLAYERS; i++)
12612   {
12613     int actual_player_action = stored_player[i].effective_action;
12614
12615 #if 1
12616     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12617        - rnd_equinox_tetrachloride 048
12618        - rnd_equinox_tetrachloride_ii 096
12619        - rnd_emanuel_schmieg 002
12620        - doctor_sloan_ww 001, 020
12621     */
12622     if (stored_player[i].MovPos == 0)
12623       CheckGravityMovement(&stored_player[i]);
12624 #endif
12625
12626     /* overwrite programmed action with tape action */
12627     if (stored_player[i].programmed_action)
12628       actual_player_action = stored_player[i].programmed_action;
12629
12630     PlayerActions(&stored_player[i], actual_player_action);
12631
12632     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12633   }
12634
12635   ScrollScreen(NULL, SCROLL_GO_ON);
12636
12637   /* for backwards compatibility, the following code emulates a fixed bug that
12638      occured when pushing elements (causing elements that just made their last
12639      pushing step to already (if possible) make their first falling step in the
12640      same game frame, which is bad); this code is also needed to use the famous
12641      "spring push bug" which is used in older levels and might be wanted to be
12642      used also in newer levels, but in this case the buggy pushing code is only
12643      affecting the "spring" element and no other elements */
12644
12645   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12646   {
12647     for (i = 0; i < MAX_PLAYERS; i++)
12648     {
12649       struct PlayerInfo *player = &stored_player[i];
12650       int x = player->jx;
12651       int y = player->jy;
12652
12653       if (player->active && player->is_pushing && player->is_moving &&
12654           IS_MOVING(x, y) &&
12655           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12656            Feld[x][y] == EL_SPRING))
12657       {
12658         ContinueMoving(x, y);
12659
12660         /* continue moving after pushing (this is actually a bug) */
12661         if (!IS_MOVING(x, y))
12662           Stop[x][y] = FALSE;
12663       }
12664     }
12665   }
12666
12667 #if 0
12668   debug_print_timestamp(0, "start main loop profiling");
12669 #endif
12670
12671   SCAN_PLAYFIELD(x, y)
12672   {
12673     ChangeCount[x][y] = 0;
12674     ChangeEvent[x][y] = -1;
12675
12676     /* this must be handled before main playfield loop */
12677     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12678     {
12679       MovDelay[x][y]--;
12680       if (MovDelay[x][y] <= 0)
12681         RemoveField(x, y);
12682     }
12683
12684 #if USE_NEW_SNAP_DELAY
12685     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12686     {
12687       MovDelay[x][y]--;
12688       if (MovDelay[x][y] <= 0)
12689       {
12690         RemoveField(x, y);
12691         TEST_DrawLevelField(x, y);
12692
12693         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12694       }
12695     }
12696 #endif
12697
12698 #if DEBUG
12699     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12700     {
12701       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12702       printf("GameActions(): This should never happen!\n");
12703
12704       ChangePage[x][y] = -1;
12705     }
12706 #endif
12707
12708     Stop[x][y] = FALSE;
12709     if (WasJustMoving[x][y] > 0)
12710       WasJustMoving[x][y]--;
12711     if (WasJustFalling[x][y] > 0)
12712       WasJustFalling[x][y]--;
12713     if (CheckCollision[x][y] > 0)
12714       CheckCollision[x][y]--;
12715     if (CheckImpact[x][y] > 0)
12716       CheckImpact[x][y]--;
12717
12718     GfxFrame[x][y]++;
12719
12720     /* reset finished pushing action (not done in ContinueMoving() to allow
12721        continuous pushing animation for elements with zero push delay) */
12722     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12723     {
12724       ResetGfxAnimation(x, y);
12725       TEST_DrawLevelField(x, y);
12726     }
12727
12728 #if DEBUG
12729     if (IS_BLOCKED(x, y))
12730     {
12731       int oldx, oldy;
12732
12733       Blocked2Moving(x, y, &oldx, &oldy);
12734       if (!IS_MOVING(oldx, oldy))
12735       {
12736         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12737         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12738         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12739         printf("GameActions(): This should never happen!\n");
12740       }
12741     }
12742 #endif
12743   }
12744
12745 #if 0
12746   debug_print_timestamp(0, "- time for pre-main loop:");
12747 #endif
12748
12749 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12750   SCAN_PLAYFIELD(x, y)
12751   {
12752     element = Feld[x][y];
12753     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12754
12755 #if 1
12756     {
12757 #if 1
12758       int element2 = element;
12759       int graphic2 = graphic;
12760 #else
12761       int element2 = Feld[x][y];
12762       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12763 #endif
12764       int last_gfx_frame = GfxFrame[x][y];
12765
12766       if (graphic_info[graphic2].anim_global_sync)
12767         GfxFrame[x][y] = FrameCounter;
12768       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12769         GfxFrame[x][y] = CustomValue[x][y];
12770       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12771         GfxFrame[x][y] = element_info[element2].collect_score;
12772       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12773         GfxFrame[x][y] = ChangeDelay[x][y];
12774
12775       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12776         DrawLevelGraphicAnimation(x, y, graphic2);
12777     }
12778 #else
12779     ResetGfxFrame(x, y, TRUE);
12780 #endif
12781
12782 #if 1
12783     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12784         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12785       ResetRandomAnimationValue(x, y);
12786 #endif
12787
12788 #if 1
12789     SetRandomAnimationValue(x, y);
12790 #endif
12791
12792 #if 1
12793     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12794 #endif
12795   }
12796 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12797
12798 #if 0
12799   debug_print_timestamp(0, "- time for TEST loop:     -->");
12800 #endif
12801
12802   SCAN_PLAYFIELD(x, y)
12803   {
12804     element = Feld[x][y];
12805     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12806
12807     ResetGfxFrame(x, y, TRUE);
12808
12809     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12810         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12811       ResetRandomAnimationValue(x, y);
12812
12813     SetRandomAnimationValue(x, y);
12814
12815     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12816
12817     if (IS_INACTIVE(element))
12818     {
12819       if (IS_ANIMATED(graphic))
12820         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12821
12822       continue;
12823     }
12824
12825     /* this may take place after moving, so 'element' may have changed */
12826     if (IS_CHANGING(x, y) &&
12827         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12828     {
12829       int page = element_info[element].event_page_nr[CE_DELAY];
12830
12831 #if 1
12832       HandleElementChange(x, y, page);
12833 #else
12834       if (CAN_CHANGE(element))
12835         HandleElementChange(x, y, page);
12836
12837       if (HAS_ACTION(element))
12838         ExecuteCustomElementAction(x, y, element, page);
12839 #endif
12840
12841       element = Feld[x][y];
12842       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12843     }
12844
12845 #if 0   // ---------------------------------------------------------------------
12846
12847     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12848     {
12849       StartMoving(x, y);
12850
12851       element = Feld[x][y];
12852       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12853
12854       if (IS_ANIMATED(graphic) &&
12855           !IS_MOVING(x, y) &&
12856           !Stop[x][y])
12857         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12858
12859       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12860         TEST_DrawTwinkleOnField(x, y);
12861     }
12862     else if (IS_MOVING(x, y))
12863       ContinueMoving(x, y);
12864     else
12865     {
12866       switch (element)
12867       {
12868         case EL_ACID:
12869         case EL_EXIT_OPEN:
12870         case EL_EM_EXIT_OPEN:
12871         case EL_SP_EXIT_OPEN:
12872         case EL_STEEL_EXIT_OPEN:
12873         case EL_EM_STEEL_EXIT_OPEN:
12874         case EL_SP_TERMINAL:
12875         case EL_SP_TERMINAL_ACTIVE:
12876         case EL_EXTRA_TIME:
12877         case EL_SHIELD_NORMAL:
12878         case EL_SHIELD_DEADLY:
12879           if (IS_ANIMATED(graphic))
12880             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12881           break;
12882
12883         case EL_DYNAMITE_ACTIVE:
12884         case EL_EM_DYNAMITE_ACTIVE:
12885         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12886         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12887         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12888         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12889         case EL_SP_DISK_RED_ACTIVE:
12890           CheckDynamite(x, y);
12891           break;
12892
12893         case EL_AMOEBA_GROWING:
12894           AmoebeWaechst(x, y);
12895           break;
12896
12897         case EL_AMOEBA_SHRINKING:
12898           AmoebaDisappearing(x, y);
12899           break;
12900
12901 #if !USE_NEW_AMOEBA_CODE
12902         case EL_AMOEBA_WET:
12903         case EL_AMOEBA_DRY:
12904         case EL_AMOEBA_FULL:
12905         case EL_BD_AMOEBA:
12906         case EL_EMC_DRIPPER:
12907           AmoebeAbleger(x, y);
12908           break;
12909 #endif
12910
12911         case EL_GAME_OF_LIFE:
12912         case EL_BIOMAZE:
12913           Life(x, y);
12914           break;
12915
12916         case EL_EXIT_CLOSED:
12917           CheckExit(x, y);
12918           break;
12919
12920         case EL_EM_EXIT_CLOSED:
12921           CheckExitEM(x, y);
12922           break;
12923
12924         case EL_STEEL_EXIT_CLOSED:
12925           CheckExitSteel(x, y);
12926           break;
12927
12928         case EL_EM_STEEL_EXIT_CLOSED:
12929           CheckExitSteelEM(x, y);
12930           break;
12931
12932         case EL_SP_EXIT_CLOSED:
12933           CheckExitSP(x, y);
12934           break;
12935
12936         case EL_EXPANDABLE_WALL_GROWING:
12937         case EL_EXPANDABLE_STEELWALL_GROWING:
12938           MauerWaechst(x, y);
12939           break;
12940
12941         case EL_EXPANDABLE_WALL:
12942         case EL_EXPANDABLE_WALL_HORIZONTAL:
12943         case EL_EXPANDABLE_WALL_VERTICAL:
12944         case EL_EXPANDABLE_WALL_ANY:
12945         case EL_BD_EXPANDABLE_WALL:
12946           MauerAbleger(x, y);
12947           break;
12948
12949         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12950         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12951         case EL_EXPANDABLE_STEELWALL_ANY:
12952           MauerAblegerStahl(x, y);
12953           break;
12954
12955         case EL_FLAMES:
12956           CheckForDragon(x, y);
12957           break;
12958
12959         case EL_EXPLOSION:
12960           break;
12961
12962         case EL_ELEMENT_SNAPPING:
12963         case EL_DIAGONAL_SHRINKING:
12964         case EL_DIAGONAL_GROWING:
12965         {
12966           graphic =
12967             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12968
12969           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12970           break;
12971         }
12972
12973         default:
12974           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12975             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12976           break;
12977       }
12978     }
12979
12980 #else   // ---------------------------------------------------------------------
12981
12982     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12983     {
12984       StartMoving(x, y);
12985
12986       element = Feld[x][y];
12987       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12988
12989       if (IS_ANIMATED(graphic) &&
12990           !IS_MOVING(x, y) &&
12991           !Stop[x][y])
12992         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12993
12994       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12995         TEST_DrawTwinkleOnField(x, y);
12996     }
12997     else if ((element == EL_ACID ||
12998               element == EL_EXIT_OPEN ||
12999               element == EL_EM_EXIT_OPEN ||
13000               element == EL_SP_EXIT_OPEN ||
13001               element == EL_STEEL_EXIT_OPEN ||
13002               element == EL_EM_STEEL_EXIT_OPEN ||
13003               element == EL_SP_TERMINAL ||
13004               element == EL_SP_TERMINAL_ACTIVE ||
13005               element == EL_EXTRA_TIME ||
13006               element == EL_SHIELD_NORMAL ||
13007               element == EL_SHIELD_DEADLY) &&
13008              IS_ANIMATED(graphic))
13009       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13010     else if (IS_MOVING(x, y))
13011       ContinueMoving(x, y);
13012     else if (IS_ACTIVE_BOMB(element))
13013       CheckDynamite(x, y);
13014     else if (element == EL_AMOEBA_GROWING)
13015       AmoebeWaechst(x, y);
13016     else if (element == EL_AMOEBA_SHRINKING)
13017       AmoebaDisappearing(x, y);
13018
13019 #if !USE_NEW_AMOEBA_CODE
13020     else if (IS_AMOEBALIVE(element))
13021       AmoebeAbleger(x, y);
13022 #endif
13023
13024     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13025       Life(x, y);
13026     else if (element == EL_EXIT_CLOSED)
13027       CheckExit(x, y);
13028     else if (element == EL_EM_EXIT_CLOSED)
13029       CheckExitEM(x, y);
13030     else if (element == EL_STEEL_EXIT_CLOSED)
13031       CheckExitSteel(x, y);
13032     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13033       CheckExitSteelEM(x, y);
13034     else if (element == EL_SP_EXIT_CLOSED)
13035       CheckExitSP(x, y);
13036     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13037              element == EL_EXPANDABLE_STEELWALL_GROWING)
13038       MauerWaechst(x, y);
13039     else if (element == EL_EXPANDABLE_WALL ||
13040              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13041              element == EL_EXPANDABLE_WALL_VERTICAL ||
13042              element == EL_EXPANDABLE_WALL_ANY ||
13043              element == EL_BD_EXPANDABLE_WALL)
13044       MauerAbleger(x, y);
13045     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13046              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13047              element == EL_EXPANDABLE_STEELWALL_ANY)
13048       MauerAblegerStahl(x, y);
13049     else if (element == EL_FLAMES)
13050       CheckForDragon(x, y);
13051     else if (element == EL_EXPLOSION)
13052       ; /* drawing of correct explosion animation is handled separately */
13053     else if (element == EL_ELEMENT_SNAPPING ||
13054              element == EL_DIAGONAL_SHRINKING ||
13055              element == EL_DIAGONAL_GROWING)
13056     {
13057       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13058
13059       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13060     }
13061     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13062       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13063
13064 #endif  // ---------------------------------------------------------------------
13065
13066     if (IS_BELT_ACTIVE(element))
13067       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13068
13069     if (game.magic_wall_active)
13070     {
13071       int jx = local_player->jx, jy = local_player->jy;
13072
13073       /* play the element sound at the position nearest to the player */
13074       if ((element == EL_MAGIC_WALL_FULL ||
13075            element == EL_MAGIC_WALL_ACTIVE ||
13076            element == EL_MAGIC_WALL_EMPTYING ||
13077            element == EL_BD_MAGIC_WALL_FULL ||
13078            element == EL_BD_MAGIC_WALL_ACTIVE ||
13079            element == EL_BD_MAGIC_WALL_EMPTYING ||
13080            element == EL_DC_MAGIC_WALL_FULL ||
13081            element == EL_DC_MAGIC_WALL_ACTIVE ||
13082            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13083           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13084       {
13085         magic_wall_x = x;
13086         magic_wall_y = y;
13087       }
13088     }
13089   }
13090
13091 #if 0
13092   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13093 #endif
13094
13095 #if USE_NEW_AMOEBA_CODE
13096   /* new experimental amoeba growth stuff */
13097   if (!(FrameCounter % 8))
13098   {
13099     static unsigned int random = 1684108901;
13100
13101     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13102     {
13103       x = RND(lev_fieldx);
13104       y = RND(lev_fieldy);
13105       element = Feld[x][y];
13106
13107       if (!IS_PLAYER(x,y) &&
13108           (element == EL_EMPTY ||
13109            CAN_GROW_INTO(element) ||
13110            element == EL_QUICKSAND_EMPTY ||
13111            element == EL_QUICKSAND_FAST_EMPTY ||
13112            element == EL_ACID_SPLASH_LEFT ||
13113            element == EL_ACID_SPLASH_RIGHT))
13114       {
13115         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13116             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13117             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13118             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13119           Feld[x][y] = EL_AMOEBA_DROP;
13120       }
13121
13122       random = random * 129 + 1;
13123     }
13124   }
13125 #endif
13126
13127 #if 0
13128   if (game.explosions_delayed)
13129 #endif
13130   {
13131     game.explosions_delayed = FALSE;
13132
13133     SCAN_PLAYFIELD(x, y)
13134     {
13135       element = Feld[x][y];
13136
13137       if (ExplodeField[x][y])
13138         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13139       else if (element == EL_EXPLOSION)
13140         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13141
13142       ExplodeField[x][y] = EX_TYPE_NONE;
13143     }
13144
13145     game.explosions_delayed = TRUE;
13146   }
13147
13148   if (game.magic_wall_active)
13149   {
13150     if (!(game.magic_wall_time_left % 4))
13151     {
13152       int element = Feld[magic_wall_x][magic_wall_y];
13153
13154       if (element == EL_BD_MAGIC_WALL_FULL ||
13155           element == EL_BD_MAGIC_WALL_ACTIVE ||
13156           element == EL_BD_MAGIC_WALL_EMPTYING)
13157         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13158       else if (element == EL_DC_MAGIC_WALL_FULL ||
13159                element == EL_DC_MAGIC_WALL_ACTIVE ||
13160                element == EL_DC_MAGIC_WALL_EMPTYING)
13161         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13162       else
13163         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13164     }
13165
13166     if (game.magic_wall_time_left > 0)
13167     {
13168       game.magic_wall_time_left--;
13169
13170       if (!game.magic_wall_time_left)
13171       {
13172         SCAN_PLAYFIELD(x, y)
13173         {
13174           element = Feld[x][y];
13175
13176           if (element == EL_MAGIC_WALL_ACTIVE ||
13177               element == EL_MAGIC_WALL_FULL)
13178           {
13179             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13180             TEST_DrawLevelField(x, y);
13181           }
13182           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13183                    element == EL_BD_MAGIC_WALL_FULL)
13184           {
13185             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13186             TEST_DrawLevelField(x, y);
13187           }
13188           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13189                    element == EL_DC_MAGIC_WALL_FULL)
13190           {
13191             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13192             TEST_DrawLevelField(x, y);
13193           }
13194         }
13195
13196         game.magic_wall_active = FALSE;
13197       }
13198     }
13199   }
13200
13201   if (game.light_time_left > 0)
13202   {
13203     game.light_time_left--;
13204
13205     if (game.light_time_left == 0)
13206       RedrawAllLightSwitchesAndInvisibleElements();
13207   }
13208
13209   if (game.timegate_time_left > 0)
13210   {
13211     game.timegate_time_left--;
13212
13213     if (game.timegate_time_left == 0)
13214       CloseAllOpenTimegates();
13215   }
13216
13217   if (game.lenses_time_left > 0)
13218   {
13219     game.lenses_time_left--;
13220
13221     if (game.lenses_time_left == 0)
13222       RedrawAllInvisibleElementsForLenses();
13223   }
13224
13225   if (game.magnify_time_left > 0)
13226   {
13227     game.magnify_time_left--;
13228
13229     if (game.magnify_time_left == 0)
13230       RedrawAllInvisibleElementsForMagnifier();
13231   }
13232
13233   for (i = 0; i < MAX_PLAYERS; i++)
13234   {
13235     struct PlayerInfo *player = &stored_player[i];
13236
13237     if (SHIELD_ON(player))
13238     {
13239       if (player->shield_deadly_time_left)
13240         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13241       else if (player->shield_normal_time_left)
13242         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13243     }
13244   }
13245
13246 #if USE_DELAYED_GFX_REDRAW
13247   SCAN_PLAYFIELD(x, y)
13248   {
13249 #if 1
13250     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13251 #else
13252     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13253         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13254 #endif
13255     {
13256       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13257          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13258
13259       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13260         DrawLevelField(x, y);
13261
13262       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13263         DrawLevelFieldCrumbled(x, y);
13264
13265       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13266         DrawLevelFieldCrumbledNeighbours(x, y);
13267
13268       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13269         DrawTwinkleOnField(x, y);
13270     }
13271
13272     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13273   }
13274 #endif
13275
13276   CheckLevelTime();
13277
13278   DrawAllPlayers();
13279   PlayAllPlayersSound();
13280
13281   if (options.debug)                    /* calculate frames per second */
13282   {
13283     static unsigned int fps_counter = 0;
13284     static int fps_frames = 0;
13285     unsigned int fps_delay_ms = Counter() - fps_counter;
13286
13287     fps_frames++;
13288
13289     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13290     {
13291       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13292
13293       fps_frames = 0;
13294       fps_counter = Counter();
13295     }
13296
13297     redraw_mask |= REDRAW_FPS;
13298   }
13299
13300   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13301
13302   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13303   {
13304     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13305
13306     local_player->show_envelope = 0;
13307   }
13308
13309 #if 0
13310   debug_print_timestamp(0, "stop main loop profiling ");
13311   printf("----------------------------------------------------------\n");
13312 #endif
13313
13314   /* use random number generator in every frame to make it less predictable */
13315   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13316     RND(1);
13317 }
13318
13319 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13320 {
13321   int min_x = x, min_y = y, max_x = x, max_y = y;
13322   int i;
13323
13324   for (i = 0; i < MAX_PLAYERS; i++)
13325   {
13326     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13327
13328     if (!stored_player[i].active || &stored_player[i] == player)
13329       continue;
13330
13331     min_x = MIN(min_x, jx);
13332     min_y = MIN(min_y, jy);
13333     max_x = MAX(max_x, jx);
13334     max_y = MAX(max_y, jy);
13335   }
13336
13337   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13338 }
13339
13340 static boolean AllPlayersInVisibleScreen()
13341 {
13342   int i;
13343
13344   for (i = 0; i < MAX_PLAYERS; i++)
13345   {
13346     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13347
13348     if (!stored_player[i].active)
13349       continue;
13350
13351     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13352       return FALSE;
13353   }
13354
13355   return TRUE;
13356 }
13357
13358 void ScrollLevel(int dx, int dy)
13359 {
13360 #if 0
13361   /* (directly solved in BlitBitmap() now) */
13362   static Bitmap *bitmap_db_field2 = NULL;
13363   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13364   int x, y;
13365 #else
13366   int x, y;
13367 #endif
13368
13369 #if 0
13370   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13371   /* only horizontal XOR vertical scroll direction allowed */
13372   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13373     return;
13374 #endif
13375
13376 #if 0
13377   /* (directly solved in BlitBitmap() now) */
13378   if (bitmap_db_field2 == NULL)
13379     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13380
13381   /* needed when blitting directly to same bitmap -- should not be needed with
13382      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13383   BlitBitmap(drawto_field, bitmap_db_field2,
13384              FX + TILEX * (dx == -1) - softscroll_offset,
13385              FY + TILEY * (dy == -1) - softscroll_offset,
13386              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13387              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13388              FX + TILEX * (dx == 1) - softscroll_offset,
13389              FY + TILEY * (dy == 1) - softscroll_offset);
13390   BlitBitmap(bitmap_db_field2, drawto_field,
13391              FX + TILEX * (dx == 1) - softscroll_offset,
13392              FY + TILEY * (dy == 1) - softscroll_offset,
13393              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13394              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13395              FX + TILEX * (dx == 1) - softscroll_offset,
13396              FY + TILEY * (dy == 1) - softscroll_offset);
13397
13398 #else
13399
13400 #if 0
13401   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13402   int xsize = (BX2 - BX1 + 1);
13403   int ysize = (BY2 - BY1 + 1);
13404   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13405   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13406   int step  = (start < end ? +1 : -1);
13407
13408   for (i = start; i != end; i += step)
13409   {
13410     BlitBitmap(drawto_field, drawto_field,
13411                FX + TILEX * (dx != 0 ? i + step : 0),
13412                FY + TILEY * (dy != 0 ? i + step : 0),
13413                TILEX * (dx != 0 ? 1 : xsize),
13414                TILEY * (dy != 0 ? 1 : ysize),
13415                FX + TILEX * (dx != 0 ? i : 0),
13416                FY + TILEY * (dy != 0 ? i : 0));
13417   }
13418
13419 #else
13420
13421 #if NEW_TILESIZE
13422 #if NEW_SCROLL
13423   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13424 #else
13425   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13426 #endif
13427 #else
13428 #if NEW_SCROLL
13429   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13430 #else
13431   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13432 #endif
13433 #endif
13434
13435 #if NEW_TILESIZE
13436   BlitBitmap(drawto_field, drawto_field,
13437              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13438              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13439              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13440              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13441              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13442              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13443 #else
13444   BlitBitmap(drawto_field, drawto_field,
13445              FX + TILEX * (dx == -1) - softscroll_offset,
13446              FY + TILEY * (dy == -1) - softscroll_offset,
13447              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13448              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13449              FX + TILEX * (dx == 1) - softscroll_offset,
13450              FY + TILEY * (dy == 1) - softscroll_offset);
13451 #endif
13452
13453 #endif
13454 #endif
13455
13456   if (dx != 0)
13457   {
13458     x = (dx == 1 ? BX1 : BX2);
13459     for (y = BY1; y <= BY2; y++)
13460       DrawScreenField(x, y);
13461   }
13462
13463   if (dy != 0)
13464   {
13465     y = (dy == 1 ? BY1 : BY2);
13466     for (x = BX1; x <= BX2; x++)
13467       DrawScreenField(x, y);
13468   }
13469
13470   redraw_mask |= REDRAW_FIELD;
13471 }
13472
13473 static boolean canFallDown(struct PlayerInfo *player)
13474 {
13475   int jx = player->jx, jy = player->jy;
13476
13477   return (IN_LEV_FIELD(jx, jy + 1) &&
13478           (IS_FREE(jx, jy + 1) ||
13479            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13480           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13481           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13482 }
13483
13484 static boolean canPassField(int x, int y, int move_dir)
13485 {
13486   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13487   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13488   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13489   int nextx = x + dx;
13490   int nexty = y + dy;
13491   int element = Feld[x][y];
13492
13493   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13494           !CAN_MOVE(element) &&
13495           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13496           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13497           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13498 }
13499
13500 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13501 {
13502   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13503   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13504   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13505   int newx = x + dx;
13506   int newy = y + dy;
13507
13508   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13509           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13510           (IS_DIGGABLE(Feld[newx][newy]) ||
13511            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13512            canPassField(newx, newy, move_dir)));
13513 }
13514
13515 static void CheckGravityMovement(struct PlayerInfo *player)
13516 {
13517 #if USE_PLAYER_GRAVITY
13518   if (player->gravity && !player->programmed_action)
13519 #else
13520   if (game.gravity && !player->programmed_action)
13521 #endif
13522   {
13523     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13524     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13525     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13526     int jx = player->jx, jy = player->jy;
13527     boolean player_is_moving_to_valid_field =
13528       (!player_is_snapping &&
13529        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13530         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13531     boolean player_can_fall_down = canFallDown(player);
13532
13533     if (player_can_fall_down &&
13534         !player_is_moving_to_valid_field)
13535       player->programmed_action = MV_DOWN;
13536   }
13537 }
13538
13539 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13540 {
13541   return CheckGravityMovement(player);
13542
13543 #if USE_PLAYER_GRAVITY
13544   if (player->gravity && !player->programmed_action)
13545 #else
13546   if (game.gravity && !player->programmed_action)
13547 #endif
13548   {
13549     int jx = player->jx, jy = player->jy;
13550     boolean field_under_player_is_free =
13551       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13552     boolean player_is_standing_on_valid_field =
13553       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13554        (IS_WALKABLE(Feld[jx][jy]) &&
13555         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13556
13557     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13558       player->programmed_action = MV_DOWN;
13559   }
13560 }
13561
13562 /*
13563   MovePlayerOneStep()
13564   -----------------------------------------------------------------------------
13565   dx, dy:               direction (non-diagonal) to try to move the player to
13566   real_dx, real_dy:     direction as read from input device (can be diagonal)
13567 */
13568
13569 boolean MovePlayerOneStep(struct PlayerInfo *player,
13570                           int dx, int dy, int real_dx, int real_dy)
13571 {
13572   int jx = player->jx, jy = player->jy;
13573   int new_jx = jx + dx, new_jy = jy + dy;
13574 #if !USE_FIXED_DONT_RUN_INTO
13575   int element;
13576 #endif
13577   int can_move;
13578   boolean player_can_move = !player->cannot_move;
13579
13580   if (!player->active || (!dx && !dy))
13581     return MP_NO_ACTION;
13582
13583   player->MovDir = (dx < 0 ? MV_LEFT :
13584                     dx > 0 ? MV_RIGHT :
13585                     dy < 0 ? MV_UP :
13586                     dy > 0 ? MV_DOWN :  MV_NONE);
13587
13588   if (!IN_LEV_FIELD(new_jx, new_jy))
13589     return MP_NO_ACTION;
13590
13591   if (!player_can_move)
13592   {
13593     if (player->MovPos == 0)
13594     {
13595       player->is_moving = FALSE;
13596       player->is_digging = FALSE;
13597       player->is_collecting = FALSE;
13598       player->is_snapping = FALSE;
13599       player->is_pushing = FALSE;
13600     }
13601   }
13602
13603 #if 1
13604   if (!options.network && game.centered_player_nr == -1 &&
13605       !AllPlayersInSight(player, new_jx, new_jy))
13606     return MP_NO_ACTION;
13607 #else
13608   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13609     return MP_NO_ACTION;
13610 #endif
13611
13612 #if !USE_FIXED_DONT_RUN_INTO
13613   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13614
13615   /* (moved to DigField()) */
13616   if (player_can_move && DONT_RUN_INTO(element))
13617   {
13618     if (element == EL_ACID && dx == 0 && dy == 1)
13619     {
13620       SplashAcid(new_jx, new_jy);
13621       Feld[jx][jy] = EL_PLAYER_1;
13622       InitMovingField(jx, jy, MV_DOWN);
13623       Store[jx][jy] = EL_ACID;
13624       ContinueMoving(jx, jy);
13625       BuryPlayer(player);
13626     }
13627     else
13628       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13629
13630     return MP_MOVING;
13631   }
13632 #endif
13633
13634   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13635   if (can_move != MP_MOVING)
13636     return can_move;
13637
13638   /* check if DigField() has caused relocation of the player */
13639   if (player->jx != jx || player->jy != jy)
13640     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13641
13642   StorePlayer[jx][jy] = 0;
13643   player->last_jx = jx;
13644   player->last_jy = jy;
13645   player->jx = new_jx;
13646   player->jy = new_jy;
13647   StorePlayer[new_jx][new_jy] = player->element_nr;
13648
13649   if (player->move_delay_value_next != -1)
13650   {
13651     player->move_delay_value = player->move_delay_value_next;
13652     player->move_delay_value_next = -1;
13653   }
13654
13655   player->MovPos =
13656     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13657
13658   player->step_counter++;
13659
13660   PlayerVisit[jx][jy] = FrameCounter;
13661
13662 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13663   player->is_moving = TRUE;
13664 #endif
13665
13666 #if 1
13667   /* should better be called in MovePlayer(), but this breaks some tapes */
13668   ScrollPlayer(player, SCROLL_INIT);
13669 #endif
13670
13671   return MP_MOVING;
13672 }
13673
13674 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13675 {
13676   int jx = player->jx, jy = player->jy;
13677   int old_jx = jx, old_jy = jy;
13678   int moved = MP_NO_ACTION;
13679
13680   if (!player->active)
13681     return FALSE;
13682
13683   if (!dx && !dy)
13684   {
13685     if (player->MovPos == 0)
13686     {
13687       player->is_moving = FALSE;
13688       player->is_digging = FALSE;
13689       player->is_collecting = FALSE;
13690       player->is_snapping = FALSE;
13691       player->is_pushing = FALSE;
13692     }
13693
13694     return FALSE;
13695   }
13696
13697   if (player->move_delay > 0)
13698     return FALSE;
13699
13700   player->move_delay = -1;              /* set to "uninitialized" value */
13701
13702   /* store if player is automatically moved to next field */
13703   player->is_auto_moving = (player->programmed_action != MV_NONE);
13704
13705   /* remove the last programmed player action */
13706   player->programmed_action = 0;
13707
13708   if (player->MovPos)
13709   {
13710     /* should only happen if pre-1.2 tape recordings are played */
13711     /* this is only for backward compatibility */
13712
13713     int original_move_delay_value = player->move_delay_value;
13714
13715 #if DEBUG
13716     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13717            tape.counter);
13718 #endif
13719
13720     /* scroll remaining steps with finest movement resolution */
13721     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13722
13723     while (player->MovPos)
13724     {
13725       ScrollPlayer(player, SCROLL_GO_ON);
13726       ScrollScreen(NULL, SCROLL_GO_ON);
13727
13728       AdvanceFrameAndPlayerCounters(player->index_nr);
13729
13730       DrawAllPlayers();
13731       BackToFront();
13732     }
13733
13734     player->move_delay_value = original_move_delay_value;
13735   }
13736
13737   player->is_active = FALSE;
13738
13739   if (player->last_move_dir & MV_HORIZONTAL)
13740   {
13741     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13742       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13743   }
13744   else
13745   {
13746     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13747       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13748   }
13749
13750 #if USE_FIXED_BORDER_RUNNING_GFX
13751   if (!moved && !player->is_active)
13752   {
13753     player->is_moving = FALSE;
13754     player->is_digging = FALSE;
13755     player->is_collecting = FALSE;
13756     player->is_snapping = FALSE;
13757     player->is_pushing = FALSE;
13758   }
13759 #endif
13760
13761   jx = player->jx;
13762   jy = player->jy;
13763
13764 #if 1
13765   if (moved & MP_MOVING && !ScreenMovPos &&
13766       (player->index_nr == game.centered_player_nr ||
13767        game.centered_player_nr == -1))
13768 #else
13769   if (moved & MP_MOVING && !ScreenMovPos &&
13770       (player == local_player || !options.network))
13771 #endif
13772   {
13773     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13774     int offset = game.scroll_delay_value;
13775
13776     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13777     {
13778       /* actual player has left the screen -- scroll in that direction */
13779       if (jx != old_jx)         /* player has moved horizontally */
13780         scroll_x += (jx - old_jx);
13781       else                      /* player has moved vertically */
13782         scroll_y += (jy - old_jy);
13783     }
13784     else
13785     {
13786       if (jx != old_jx)         /* player has moved horizontally */
13787       {
13788         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13789             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13790           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13791
13792         /* don't scroll over playfield boundaries */
13793         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13794           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13795
13796         /* don't scroll more than one field at a time */
13797         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13798
13799         /* don't scroll against the player's moving direction */
13800         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13801             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13802           scroll_x = old_scroll_x;
13803       }
13804       else                      /* player has moved vertically */
13805       {
13806         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13807             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13808           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13809
13810         /* don't scroll over playfield boundaries */
13811         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13812           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13813
13814         /* don't scroll more than one field at a time */
13815         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13816
13817         /* don't scroll against the player's moving direction */
13818         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13819             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13820           scroll_y = old_scroll_y;
13821       }
13822     }
13823
13824     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13825     {
13826 #if 1
13827       if (!options.network && game.centered_player_nr == -1 &&
13828           !AllPlayersInVisibleScreen())
13829       {
13830         scroll_x = old_scroll_x;
13831         scroll_y = old_scroll_y;
13832       }
13833       else
13834 #else
13835       if (!options.network && !AllPlayersInVisibleScreen())
13836       {
13837         scroll_x = old_scroll_x;
13838         scroll_y = old_scroll_y;
13839       }
13840       else
13841 #endif
13842       {
13843         ScrollScreen(player, SCROLL_INIT);
13844         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13845       }
13846     }
13847   }
13848
13849   player->StepFrame = 0;
13850
13851   if (moved & MP_MOVING)
13852   {
13853     if (old_jx != jx && old_jy == jy)
13854       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13855     else if (old_jx == jx && old_jy != jy)
13856       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13857
13858     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13859
13860     player->last_move_dir = player->MovDir;
13861     player->is_moving = TRUE;
13862     player->is_snapping = FALSE;
13863     player->is_switching = FALSE;
13864     player->is_dropping = FALSE;
13865     player->is_dropping_pressed = FALSE;
13866     player->drop_pressed_delay = 0;
13867
13868 #if 0
13869     /* should better be called here than above, but this breaks some tapes */
13870     ScrollPlayer(player, SCROLL_INIT);
13871 #endif
13872   }
13873   else
13874   {
13875     CheckGravityMovementWhenNotMoving(player);
13876
13877     player->is_moving = FALSE;
13878
13879     /* at this point, the player is allowed to move, but cannot move right now
13880        (e.g. because of something blocking the way) -- ensure that the player
13881        is also allowed to move in the next frame (in old versions before 3.1.1,
13882        the player was forced to wait again for eight frames before next try) */
13883
13884     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13885       player->move_delay = 0;   /* allow direct movement in the next frame */
13886   }
13887
13888   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13889     player->move_delay = player->move_delay_value;
13890
13891   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13892   {
13893     TestIfPlayerTouchesBadThing(jx, jy);
13894     TestIfPlayerTouchesCustomElement(jx, jy);
13895   }
13896
13897   if (!player->active)
13898     RemovePlayer(player);
13899
13900   return moved;
13901 }
13902
13903 void ScrollPlayer(struct PlayerInfo *player, int mode)
13904 {
13905   int jx = player->jx, jy = player->jy;
13906   int last_jx = player->last_jx, last_jy = player->last_jy;
13907   int move_stepsize = TILEX / player->move_delay_value;
13908
13909 #if USE_NEW_PLAYER_SPEED
13910   if (!player->active)
13911     return;
13912
13913   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13914     return;
13915 #else
13916   if (!player->active || player->MovPos == 0)
13917     return;
13918 #endif
13919
13920   if (mode == SCROLL_INIT)
13921   {
13922     player->actual_frame_counter = FrameCounter;
13923     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13924
13925     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13926         Feld[last_jx][last_jy] == EL_EMPTY)
13927     {
13928       int last_field_block_delay = 0;   /* start with no blocking at all */
13929       int block_delay_adjustment = player->block_delay_adjustment;
13930
13931       /* if player blocks last field, add delay for exactly one move */
13932       if (player->block_last_field)
13933       {
13934         last_field_block_delay += player->move_delay_value;
13935
13936         /* when blocking enabled, prevent moving up despite gravity */
13937 #if USE_PLAYER_GRAVITY
13938         if (player->gravity && player->MovDir == MV_UP)
13939           block_delay_adjustment = -1;
13940 #else
13941         if (game.gravity && player->MovDir == MV_UP)
13942           block_delay_adjustment = -1;
13943 #endif
13944       }
13945
13946       /* add block delay adjustment (also possible when not blocking) */
13947       last_field_block_delay += block_delay_adjustment;
13948
13949       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13950       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13951     }
13952
13953 #if USE_NEW_PLAYER_SPEED
13954     if (player->MovPos != 0)    /* player has not yet reached destination */
13955       return;
13956 #else
13957     return;
13958 #endif
13959   }
13960   else if (!FrameReached(&player->actual_frame_counter, 1))
13961     return;
13962
13963 #if USE_NEW_PLAYER_SPEED
13964   if (player->MovPos != 0)
13965   {
13966     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13967     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13968
13969     /* before DrawPlayer() to draw correct player graphic for this case */
13970     if (player->MovPos == 0)
13971       CheckGravityMovement(player);
13972   }
13973 #else
13974   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13975   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13976
13977   /* before DrawPlayer() to draw correct player graphic for this case */
13978   if (player->MovPos == 0)
13979     CheckGravityMovement(player);
13980 #endif
13981
13982   if (player->MovPos == 0)      /* player reached destination field */
13983   {
13984     if (player->move_delay_reset_counter > 0)
13985     {
13986       player->move_delay_reset_counter--;
13987
13988       if (player->move_delay_reset_counter == 0)
13989       {
13990         /* continue with normal speed after quickly moving through gate */
13991         HALVE_PLAYER_SPEED(player);
13992
13993         /* be able to make the next move without delay */
13994         player->move_delay = 0;
13995       }
13996     }
13997
13998     player->last_jx = jx;
13999     player->last_jy = jy;
14000
14001     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14002         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14003 #if 1
14004         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14005 #endif
14006         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14007         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14008 #if 1
14009         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14010 #endif
14011         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14012         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14013     {
14014       DrawPlayer(player);       /* needed here only to cleanup last field */
14015       RemovePlayer(player);
14016
14017       if (local_player->friends_still_needed == 0 ||
14018           IS_SP_ELEMENT(Feld[jx][jy]))
14019         PlayerWins(player);
14020     }
14021
14022     /* this breaks one level: "machine", level 000 */
14023     {
14024       int move_direction = player->MovDir;
14025       int enter_side = MV_DIR_OPPOSITE(move_direction);
14026       int leave_side = move_direction;
14027       int old_jx = last_jx;
14028       int old_jy = last_jy;
14029       int old_element = Feld[old_jx][old_jy];
14030       int new_element = Feld[jx][jy];
14031
14032       if (IS_CUSTOM_ELEMENT(old_element))
14033         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14034                                    CE_LEFT_BY_PLAYER,
14035                                    player->index_bit, leave_side);
14036
14037       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14038                                           CE_PLAYER_LEAVES_X,
14039                                           player->index_bit, leave_side);
14040
14041       if (IS_CUSTOM_ELEMENT(new_element))
14042         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14043                                    player->index_bit, enter_side);
14044
14045       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14046                                           CE_PLAYER_ENTERS_X,
14047                                           player->index_bit, enter_side);
14048
14049 #if USE_FIX_CE_ACTION_WITH_PLAYER
14050       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14051                                         CE_MOVE_OF_X, move_direction);
14052 #else
14053       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14054                                         CE_MOVE_OF_X, move_direction);
14055 #endif
14056     }
14057
14058     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14059     {
14060       TestIfPlayerTouchesBadThing(jx, jy);
14061       TestIfPlayerTouchesCustomElement(jx, jy);
14062
14063       /* needed because pushed element has not yet reached its destination,
14064          so it would trigger a change event at its previous field location */
14065       if (!player->is_pushing)
14066         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14067
14068       if (!player->active)
14069         RemovePlayer(player);
14070     }
14071
14072     if (!local_player->LevelSolved && level.use_step_counter)
14073     {
14074       int i;
14075
14076       TimePlayed++;
14077
14078       if (TimeLeft > 0)
14079       {
14080         TimeLeft--;
14081
14082         if (TimeLeft <= 10 && setup.time_limit)
14083           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14084
14085 #if 1
14086         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14087
14088         DisplayGameControlValues();
14089 #else
14090         DrawGameValue_Time(TimeLeft);
14091 #endif
14092
14093         if (!TimeLeft && setup.time_limit)
14094           for (i = 0; i < MAX_PLAYERS; i++)
14095             KillPlayer(&stored_player[i]);
14096       }
14097 #if 1
14098       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14099       {
14100         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14101
14102         DisplayGameControlValues();
14103       }
14104 #else
14105       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14106         DrawGameValue_Time(TimePlayed);
14107 #endif
14108     }
14109
14110     if (tape.single_step && tape.recording && !tape.pausing &&
14111         !player->programmed_action)
14112       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14113   }
14114 }
14115
14116 void ScrollScreen(struct PlayerInfo *player, int mode)
14117 {
14118   static unsigned int screen_frame_counter = 0;
14119
14120   if (mode == SCROLL_INIT)
14121   {
14122     /* set scrolling step size according to actual player's moving speed */
14123     ScrollStepSize = TILEX / player->move_delay_value;
14124
14125     screen_frame_counter = FrameCounter;
14126     ScreenMovDir = player->MovDir;
14127     ScreenMovPos = player->MovPos;
14128     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14129     return;
14130   }
14131   else if (!FrameReached(&screen_frame_counter, 1))
14132     return;
14133
14134   if (ScreenMovPos)
14135   {
14136     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14137     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14138     redraw_mask |= REDRAW_FIELD;
14139   }
14140   else
14141     ScreenMovDir = MV_NONE;
14142 }
14143
14144 void TestIfPlayerTouchesCustomElement(int x, int y)
14145 {
14146   static int xy[4][2] =
14147   {
14148     { 0, -1 },
14149     { -1, 0 },
14150     { +1, 0 },
14151     { 0, +1 }
14152   };
14153   static int trigger_sides[4][2] =
14154   {
14155     /* center side       border side */
14156     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14157     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14158     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14159     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14160   };
14161   static int touch_dir[4] =
14162   {
14163     MV_LEFT | MV_RIGHT,
14164     MV_UP   | MV_DOWN,
14165     MV_UP   | MV_DOWN,
14166     MV_LEFT | MV_RIGHT
14167   };
14168   int center_element = Feld[x][y];      /* should always be non-moving! */
14169   int i;
14170
14171   for (i = 0; i < NUM_DIRECTIONS; i++)
14172   {
14173     int xx = x + xy[i][0];
14174     int yy = y + xy[i][1];
14175     int center_side = trigger_sides[i][0];
14176     int border_side = trigger_sides[i][1];
14177     int border_element;
14178
14179     if (!IN_LEV_FIELD(xx, yy))
14180       continue;
14181
14182     if (IS_PLAYER(x, y))                /* player found at center element */
14183     {
14184       struct PlayerInfo *player = PLAYERINFO(x, y);
14185
14186       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14187         border_element = Feld[xx][yy];          /* may be moving! */
14188       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14189         border_element = Feld[xx][yy];
14190       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14191         border_element = MovingOrBlocked2Element(xx, yy);
14192       else
14193         continue;               /* center and border element do not touch */
14194
14195       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14196                                  player->index_bit, border_side);
14197       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14198                                           CE_PLAYER_TOUCHES_X,
14199                                           player->index_bit, border_side);
14200
14201 #if USE_FIX_CE_ACTION_WITH_PLAYER
14202       {
14203         /* use player element that is initially defined in the level playfield,
14204            not the player element that corresponds to the runtime player number
14205            (example: a level that contains EL_PLAYER_3 as the only player would
14206            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14207         int player_element = PLAYERINFO(x, y)->initial_element;
14208
14209         CheckElementChangeBySide(xx, yy, border_element, player_element,
14210                                  CE_TOUCHING_X, border_side);
14211       }
14212 #endif
14213     }
14214     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14215     {
14216       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14217
14218       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14219       {
14220         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14221           continue;             /* center and border element do not touch */
14222       }
14223
14224       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14225                                  player->index_bit, center_side);
14226       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14227                                           CE_PLAYER_TOUCHES_X,
14228                                           player->index_bit, center_side);
14229
14230 #if USE_FIX_CE_ACTION_WITH_PLAYER
14231       {
14232         /* use player element that is initially defined in the level playfield,
14233            not the player element that corresponds to the runtime player number
14234            (example: a level that contains EL_PLAYER_3 as the only player would
14235            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14236         int player_element = PLAYERINFO(xx, yy)->initial_element;
14237
14238         CheckElementChangeBySide(x, y, center_element, player_element,
14239                                  CE_TOUCHING_X, center_side);
14240       }
14241 #endif
14242
14243       break;
14244     }
14245   }
14246 }
14247
14248 #if USE_ELEMENT_TOUCHING_BUGFIX
14249
14250 void TestIfElementTouchesCustomElement(int x, int y)
14251 {
14252   static int xy[4][2] =
14253   {
14254     { 0, -1 },
14255     { -1, 0 },
14256     { +1, 0 },
14257     { 0, +1 }
14258   };
14259   static int trigger_sides[4][2] =
14260   {
14261     /* center side      border side */
14262     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14263     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14264     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14265     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14266   };
14267   static int touch_dir[4] =
14268   {
14269     MV_LEFT | MV_RIGHT,
14270     MV_UP   | MV_DOWN,
14271     MV_UP   | MV_DOWN,
14272     MV_LEFT | MV_RIGHT
14273   };
14274   boolean change_center_element = FALSE;
14275   int center_element = Feld[x][y];      /* should always be non-moving! */
14276   int border_element_old[NUM_DIRECTIONS];
14277   int i;
14278
14279   for (i = 0; i < NUM_DIRECTIONS; i++)
14280   {
14281     int xx = x + xy[i][0];
14282     int yy = y + xy[i][1];
14283     int border_element;
14284
14285     border_element_old[i] = -1;
14286
14287     if (!IN_LEV_FIELD(xx, yy))
14288       continue;
14289
14290     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14291       border_element = Feld[xx][yy];    /* may be moving! */
14292     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14293       border_element = Feld[xx][yy];
14294     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14295       border_element = MovingOrBlocked2Element(xx, yy);
14296     else
14297       continue;                 /* center and border element do not touch */
14298
14299     border_element_old[i] = border_element;
14300   }
14301
14302   for (i = 0; i < NUM_DIRECTIONS; i++)
14303   {
14304     int xx = x + xy[i][0];
14305     int yy = y + xy[i][1];
14306     int center_side = trigger_sides[i][0];
14307     int border_element = border_element_old[i];
14308
14309     if (border_element == -1)
14310       continue;
14311
14312     /* check for change of border element */
14313     CheckElementChangeBySide(xx, yy, border_element, center_element,
14314                              CE_TOUCHING_X, center_side);
14315
14316     /* (center element cannot be player, so we dont have to check this here) */
14317   }
14318
14319   for (i = 0; i < NUM_DIRECTIONS; i++)
14320   {
14321     int xx = x + xy[i][0];
14322     int yy = y + xy[i][1];
14323     int border_side = trigger_sides[i][1];
14324     int border_element = border_element_old[i];
14325
14326     if (border_element == -1)
14327       continue;
14328
14329     /* check for change of center element (but change it only once) */
14330     if (!change_center_element)
14331       change_center_element =
14332         CheckElementChangeBySide(x, y, center_element, border_element,
14333                                  CE_TOUCHING_X, border_side);
14334
14335 #if USE_FIX_CE_ACTION_WITH_PLAYER
14336     if (IS_PLAYER(xx, yy))
14337     {
14338       /* use player element that is initially defined in the level playfield,
14339          not the player element that corresponds to the runtime player number
14340          (example: a level that contains EL_PLAYER_3 as the only player would
14341          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14342       int player_element = PLAYERINFO(xx, yy)->initial_element;
14343
14344       CheckElementChangeBySide(x, y, center_element, player_element,
14345                                CE_TOUCHING_X, border_side);
14346     }
14347 #endif
14348   }
14349 }
14350
14351 #else
14352
14353 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14354 {
14355   static int xy[4][2] =
14356   {
14357     { 0, -1 },
14358     { -1, 0 },
14359     { +1, 0 },
14360     { 0, +1 }
14361   };
14362   static int trigger_sides[4][2] =
14363   {
14364     /* center side      border side */
14365     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14366     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14367     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14368     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14369   };
14370   static int touch_dir[4] =
14371   {
14372     MV_LEFT | MV_RIGHT,
14373     MV_UP   | MV_DOWN,
14374     MV_UP   | MV_DOWN,
14375     MV_LEFT | MV_RIGHT
14376   };
14377   boolean change_center_element = FALSE;
14378   int center_element = Feld[x][y];      /* should always be non-moving! */
14379   int i;
14380
14381   for (i = 0; i < NUM_DIRECTIONS; i++)
14382   {
14383     int xx = x + xy[i][0];
14384     int yy = y + xy[i][1];
14385     int center_side = trigger_sides[i][0];
14386     int border_side = trigger_sides[i][1];
14387     int border_element;
14388
14389     if (!IN_LEV_FIELD(xx, yy))
14390       continue;
14391
14392     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14393       border_element = Feld[xx][yy];    /* may be moving! */
14394     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14395       border_element = Feld[xx][yy];
14396     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14397       border_element = MovingOrBlocked2Element(xx, yy);
14398     else
14399       continue;                 /* center and border element do not touch */
14400
14401     /* check for change of center element (but change it only once) */
14402     if (!change_center_element)
14403       change_center_element =
14404         CheckElementChangeBySide(x, y, center_element, border_element,
14405                                  CE_TOUCHING_X, border_side);
14406
14407     /* check for change of border element */
14408     CheckElementChangeBySide(xx, yy, border_element, center_element,
14409                              CE_TOUCHING_X, center_side);
14410   }
14411 }
14412
14413 #endif
14414
14415 void TestIfElementHitsCustomElement(int x, int y, int direction)
14416 {
14417   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14418   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14419   int hitx = x + dx, hity = y + dy;
14420   int hitting_element = Feld[x][y];
14421   int touched_element;
14422
14423   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14424     return;
14425
14426   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14427                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14428
14429   if (IN_LEV_FIELD(hitx, hity))
14430   {
14431     int opposite_direction = MV_DIR_OPPOSITE(direction);
14432     int hitting_side = direction;
14433     int touched_side = opposite_direction;
14434     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14435                           MovDir[hitx][hity] != direction ||
14436                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14437
14438     object_hit = TRUE;
14439
14440     if (object_hit)
14441     {
14442       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14443                                CE_HITTING_X, touched_side);
14444
14445       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14446                                CE_HIT_BY_X, hitting_side);
14447
14448       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14449                                CE_HIT_BY_SOMETHING, opposite_direction);
14450
14451 #if USE_FIX_CE_ACTION_WITH_PLAYER
14452       if (IS_PLAYER(hitx, hity))
14453       {
14454         /* use player element that is initially defined in the level playfield,
14455            not the player element that corresponds to the runtime player number
14456            (example: a level that contains EL_PLAYER_3 as the only player would
14457            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14458         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14459
14460         CheckElementChangeBySide(x, y, hitting_element, player_element,
14461                                  CE_HITTING_X, touched_side);
14462       }
14463 #endif
14464     }
14465   }
14466
14467   /* "hitting something" is also true when hitting the playfield border */
14468   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14469                            CE_HITTING_SOMETHING, direction);
14470 }
14471
14472 #if 0
14473 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14474 {
14475   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14476   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14477   int hitx = x + dx, hity = y + dy;
14478   int hitting_element = Feld[x][y];
14479   int touched_element;
14480 #if 0
14481   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14482                         !IS_FREE(hitx, hity) &&
14483                         (!IS_MOVING(hitx, hity) ||
14484                          MovDir[hitx][hity] != direction ||
14485                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14486 #endif
14487
14488   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14489     return;
14490
14491 #if 0
14492   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14493     return;
14494 #endif
14495
14496   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14497                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14498
14499   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14500                            EP_CAN_SMASH_EVERYTHING, direction);
14501
14502   if (IN_LEV_FIELD(hitx, hity))
14503   {
14504     int opposite_direction = MV_DIR_OPPOSITE(direction);
14505     int hitting_side = direction;
14506     int touched_side = opposite_direction;
14507 #if 0
14508     int touched_element = MovingOrBlocked2Element(hitx, hity);
14509 #endif
14510 #if 1
14511     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14512                           MovDir[hitx][hity] != direction ||
14513                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14514
14515     object_hit = TRUE;
14516 #endif
14517
14518     if (object_hit)
14519     {
14520       int i;
14521
14522       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14523                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14524
14525       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14526                                CE_OTHER_IS_SMASHING, touched_side);
14527
14528       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529                                CE_OTHER_GETS_SMASHED, hitting_side);
14530     }
14531   }
14532 }
14533 #endif
14534
14535 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14536 {
14537   int i, kill_x = -1, kill_y = -1;
14538
14539   int bad_element = -1;
14540   static int test_xy[4][2] =
14541   {
14542     { 0, -1 },
14543     { -1, 0 },
14544     { +1, 0 },
14545     { 0, +1 }
14546   };
14547   static int test_dir[4] =
14548   {
14549     MV_UP,
14550     MV_LEFT,
14551     MV_RIGHT,
14552     MV_DOWN
14553   };
14554
14555   for (i = 0; i < NUM_DIRECTIONS; i++)
14556   {
14557     int test_x, test_y, test_move_dir, test_element;
14558
14559     test_x = good_x + test_xy[i][0];
14560     test_y = good_y + test_xy[i][1];
14561
14562     if (!IN_LEV_FIELD(test_x, test_y))
14563       continue;
14564
14565     test_move_dir =
14566       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14567
14568     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14569
14570     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14571        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14572     */
14573     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14574         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14575     {
14576       kill_x = test_x;
14577       kill_y = test_y;
14578       bad_element = test_element;
14579
14580       break;
14581     }
14582   }
14583
14584   if (kill_x != -1 || kill_y != -1)
14585   {
14586     if (IS_PLAYER(good_x, good_y))
14587     {
14588       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14589
14590       if (player->shield_deadly_time_left > 0 &&
14591           !IS_INDESTRUCTIBLE(bad_element))
14592         Bang(kill_x, kill_y);
14593       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14594         KillPlayer(player);
14595     }
14596     else
14597       Bang(good_x, good_y);
14598   }
14599 }
14600
14601 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14602 {
14603   int i, kill_x = -1, kill_y = -1;
14604   int bad_element = Feld[bad_x][bad_y];
14605   static int test_xy[4][2] =
14606   {
14607     { 0, -1 },
14608     { -1, 0 },
14609     { +1, 0 },
14610     { 0, +1 }
14611   };
14612   static int touch_dir[4] =
14613   {
14614     MV_LEFT | MV_RIGHT,
14615     MV_UP   | MV_DOWN,
14616     MV_UP   | MV_DOWN,
14617     MV_LEFT | MV_RIGHT
14618   };
14619   static int test_dir[4] =
14620   {
14621     MV_UP,
14622     MV_LEFT,
14623     MV_RIGHT,
14624     MV_DOWN
14625   };
14626
14627   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14628     return;
14629
14630   for (i = 0; i < NUM_DIRECTIONS; i++)
14631   {
14632     int test_x, test_y, test_move_dir, test_element;
14633
14634     test_x = bad_x + test_xy[i][0];
14635     test_y = bad_y + test_xy[i][1];
14636
14637     if (!IN_LEV_FIELD(test_x, test_y))
14638       continue;
14639
14640     test_move_dir =
14641       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14642
14643     test_element = Feld[test_x][test_y];
14644
14645     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14646        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14647     */
14648     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14649         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14650     {
14651       /* good thing is player or penguin that does not move away */
14652       if (IS_PLAYER(test_x, test_y))
14653       {
14654         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14655
14656         if (bad_element == EL_ROBOT && player->is_moving)
14657           continue;     /* robot does not kill player if he is moving */
14658
14659         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14660         {
14661           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14662             continue;           /* center and border element do not touch */
14663         }
14664
14665         kill_x = test_x;
14666         kill_y = test_y;
14667
14668         break;
14669       }
14670       else if (test_element == EL_PENGUIN)
14671       {
14672         kill_x = test_x;
14673         kill_y = test_y;
14674
14675         break;
14676       }
14677     }
14678   }
14679
14680   if (kill_x != -1 || kill_y != -1)
14681   {
14682     if (IS_PLAYER(kill_x, kill_y))
14683     {
14684       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14685
14686       if (player->shield_deadly_time_left > 0 &&
14687           !IS_INDESTRUCTIBLE(bad_element))
14688         Bang(bad_x, bad_y);
14689       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14690         KillPlayer(player);
14691     }
14692     else
14693       Bang(kill_x, kill_y);
14694   }
14695 }
14696
14697 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14698 {
14699   int bad_element = Feld[bad_x][bad_y];
14700   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14701   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14702   int test_x = bad_x + dx, test_y = bad_y + dy;
14703   int test_move_dir, test_element;
14704   int kill_x = -1, kill_y = -1;
14705
14706   if (!IN_LEV_FIELD(test_x, test_y))
14707     return;
14708
14709   test_move_dir =
14710     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14711
14712   test_element = Feld[test_x][test_y];
14713
14714   if (test_move_dir != bad_move_dir)
14715   {
14716     /* good thing can be player or penguin that does not move away */
14717     if (IS_PLAYER(test_x, test_y))
14718     {
14719       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14720
14721       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14722          player as being hit when he is moving towards the bad thing, because
14723          the "get hit by" condition would be lost after the player stops) */
14724       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14725         return;         /* player moves away from bad thing */
14726
14727       kill_x = test_x;
14728       kill_y = test_y;
14729     }
14730     else if (test_element == EL_PENGUIN)
14731     {
14732       kill_x = test_x;
14733       kill_y = test_y;
14734     }
14735   }
14736
14737   if (kill_x != -1 || kill_y != -1)
14738   {
14739     if (IS_PLAYER(kill_x, kill_y))
14740     {
14741       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14742
14743       if (player->shield_deadly_time_left > 0 &&
14744           !IS_INDESTRUCTIBLE(bad_element))
14745         Bang(bad_x, bad_y);
14746       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14747         KillPlayer(player);
14748     }
14749     else
14750       Bang(kill_x, kill_y);
14751   }
14752 }
14753
14754 void TestIfPlayerTouchesBadThing(int x, int y)
14755 {
14756   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14757 }
14758
14759 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14760 {
14761   TestIfGoodThingHitsBadThing(x, y, move_dir);
14762 }
14763
14764 void TestIfBadThingTouchesPlayer(int x, int y)
14765 {
14766   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14767 }
14768
14769 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14770 {
14771   TestIfBadThingHitsGoodThing(x, y, move_dir);
14772 }
14773
14774 void TestIfFriendTouchesBadThing(int x, int y)
14775 {
14776   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14777 }
14778
14779 void TestIfBadThingTouchesFriend(int x, int y)
14780 {
14781   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14782 }
14783
14784 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14785 {
14786   int i, kill_x = bad_x, kill_y = bad_y;
14787   static int xy[4][2] =
14788   {
14789     { 0, -1 },
14790     { -1, 0 },
14791     { +1, 0 },
14792     { 0, +1 }
14793   };
14794
14795   for (i = 0; i < NUM_DIRECTIONS; i++)
14796   {
14797     int x, y, element;
14798
14799     x = bad_x + xy[i][0];
14800     y = bad_y + xy[i][1];
14801     if (!IN_LEV_FIELD(x, y))
14802       continue;
14803
14804     element = Feld[x][y];
14805     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14806         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14807     {
14808       kill_x = x;
14809       kill_y = y;
14810       break;
14811     }
14812   }
14813
14814   if (kill_x != bad_x || kill_y != bad_y)
14815     Bang(bad_x, bad_y);
14816 }
14817
14818 void KillPlayer(struct PlayerInfo *player)
14819 {
14820   int jx = player->jx, jy = player->jy;
14821
14822   if (!player->active)
14823     return;
14824
14825 #if 0
14826   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14827          player->killed, player->active, player->reanimated);
14828 #endif
14829
14830   /* the following code was introduced to prevent an infinite loop when calling
14831      -> Bang()
14832      -> CheckTriggeredElementChangeExt()
14833      -> ExecuteCustomElementAction()
14834      -> KillPlayer()
14835      -> (infinitely repeating the above sequence of function calls)
14836      which occurs when killing the player while having a CE with the setting
14837      "kill player X when explosion of <player X>"; the solution using a new
14838      field "player->killed" was chosen for backwards compatibility, although
14839      clever use of the fields "player->active" etc. would probably also work */
14840 #if 1
14841   if (player->killed)
14842     return;
14843 #endif
14844
14845   player->killed = TRUE;
14846
14847   /* remove accessible field at the player's position */
14848   Feld[jx][jy] = EL_EMPTY;
14849
14850   /* deactivate shield (else Bang()/Explode() would not work right) */
14851   player->shield_normal_time_left = 0;
14852   player->shield_deadly_time_left = 0;
14853
14854 #if 0
14855   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14856          player->killed, player->active, player->reanimated);
14857 #endif
14858
14859   Bang(jx, jy);
14860
14861 #if 0
14862   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14863          player->killed, player->active, player->reanimated);
14864 #endif
14865
14866 #if USE_PLAYER_REANIMATION
14867 #if 1
14868   if (player->reanimated)       /* killed player may have been reanimated */
14869     player->killed = player->reanimated = FALSE;
14870   else
14871     BuryPlayer(player);
14872 #else
14873   if (player->killed)           /* player may have been reanimated */
14874     BuryPlayer(player);
14875 #endif
14876 #else
14877   BuryPlayer(player);
14878 #endif
14879 }
14880
14881 static void KillPlayerUnlessEnemyProtected(int x, int y)
14882 {
14883   if (!PLAYER_ENEMY_PROTECTED(x, y))
14884     KillPlayer(PLAYERINFO(x, y));
14885 }
14886
14887 static void KillPlayerUnlessExplosionProtected(int x, int y)
14888 {
14889   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14890     KillPlayer(PLAYERINFO(x, y));
14891 }
14892
14893 void BuryPlayer(struct PlayerInfo *player)
14894 {
14895   int jx = player->jx, jy = player->jy;
14896
14897   if (!player->active)
14898     return;
14899
14900   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14901   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14902
14903   player->GameOver = TRUE;
14904   RemovePlayer(player);
14905 }
14906
14907 void RemovePlayer(struct PlayerInfo *player)
14908 {
14909   int jx = player->jx, jy = player->jy;
14910   int i, found = FALSE;
14911
14912   player->present = FALSE;
14913   player->active = FALSE;
14914
14915   if (!ExplodeField[jx][jy])
14916     StorePlayer[jx][jy] = 0;
14917
14918   if (player->is_moving)
14919     TEST_DrawLevelField(player->last_jx, player->last_jy);
14920
14921   for (i = 0; i < MAX_PLAYERS; i++)
14922     if (stored_player[i].active)
14923       found = TRUE;
14924
14925   if (!found)
14926     AllPlayersGone = TRUE;
14927
14928   ExitX = ZX = jx;
14929   ExitY = ZY = jy;
14930 }
14931
14932 #if USE_NEW_SNAP_DELAY
14933 static void setFieldForSnapping(int x, int y, int element, int direction)
14934 {
14935   struct ElementInfo *ei = &element_info[element];
14936   int direction_bit = MV_DIR_TO_BIT(direction);
14937   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14938   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14939                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14940
14941   Feld[x][y] = EL_ELEMENT_SNAPPING;
14942   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14943
14944   ResetGfxAnimation(x, y);
14945
14946   GfxElement[x][y] = element;
14947   GfxAction[x][y] = action;
14948   GfxDir[x][y] = direction;
14949   GfxFrame[x][y] = -1;
14950 }
14951 #endif
14952
14953 /*
14954   =============================================================================
14955   checkDiagonalPushing()
14956   -----------------------------------------------------------------------------
14957   check if diagonal input device direction results in pushing of object
14958   (by checking if the alternative direction is walkable, diggable, ...)
14959   =============================================================================
14960 */
14961
14962 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14963                                     int x, int y, int real_dx, int real_dy)
14964 {
14965   int jx, jy, dx, dy, xx, yy;
14966
14967   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14968     return TRUE;
14969
14970   /* diagonal direction: check alternative direction */
14971   jx = player->jx;
14972   jy = player->jy;
14973   dx = x - jx;
14974   dy = y - jy;
14975   xx = jx + (dx == 0 ? real_dx : 0);
14976   yy = jy + (dy == 0 ? real_dy : 0);
14977
14978   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14979 }
14980
14981 /*
14982   =============================================================================
14983   DigField()
14984   -----------------------------------------------------------------------------
14985   x, y:                 field next to player (non-diagonal) to try to dig to
14986   real_dx, real_dy:     direction as read from input device (can be diagonal)
14987   =============================================================================
14988 */
14989
14990 static int DigField(struct PlayerInfo *player,
14991                     int oldx, int oldy, int x, int y,
14992                     int real_dx, int real_dy, int mode)
14993 {
14994   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14995   boolean player_was_pushing = player->is_pushing;
14996   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14997   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14998   int jx = oldx, jy = oldy;
14999   int dx = x - jx, dy = y - jy;
15000   int nextx = x + dx, nexty = y + dy;
15001   int move_direction = (dx == -1 ? MV_LEFT  :
15002                         dx == +1 ? MV_RIGHT :
15003                         dy == -1 ? MV_UP    :
15004                         dy == +1 ? MV_DOWN  : MV_NONE);
15005   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15006   int dig_side = MV_DIR_OPPOSITE(move_direction);
15007   int old_element = Feld[jx][jy];
15008 #if USE_FIXED_DONT_RUN_INTO
15009   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15010 #else
15011   int element;
15012 #endif
15013   int collect_count;
15014
15015   if (is_player)                /* function can also be called by EL_PENGUIN */
15016   {
15017     if (player->MovPos == 0)
15018     {
15019       player->is_digging = FALSE;
15020       player->is_collecting = FALSE;
15021     }
15022
15023     if (player->MovPos == 0)    /* last pushing move finished */
15024       player->is_pushing = FALSE;
15025
15026     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15027     {
15028       player->is_switching = FALSE;
15029       player->push_delay = -1;
15030
15031       return MP_NO_ACTION;
15032     }
15033   }
15034
15035 #if !USE_FIXED_DONT_RUN_INTO
15036   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15037     return MP_NO_ACTION;
15038 #endif
15039
15040   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15041     old_element = Back[jx][jy];
15042
15043   /* in case of element dropped at player position, check background */
15044   else if (Back[jx][jy] != EL_EMPTY &&
15045            game.engine_version >= VERSION_IDENT(2,2,0,0))
15046     old_element = Back[jx][jy];
15047
15048   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15049     return MP_NO_ACTION;        /* field has no opening in this direction */
15050
15051   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15052     return MP_NO_ACTION;        /* field has no opening in this direction */
15053
15054 #if USE_FIXED_DONT_RUN_INTO
15055   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15056   {
15057     SplashAcid(x, y);
15058
15059     Feld[jx][jy] = player->artwork_element;
15060     InitMovingField(jx, jy, MV_DOWN);
15061     Store[jx][jy] = EL_ACID;
15062     ContinueMoving(jx, jy);
15063     BuryPlayer(player);
15064
15065     return MP_DONT_RUN_INTO;
15066   }
15067 #endif
15068
15069 #if USE_FIXED_DONT_RUN_INTO
15070   if (player_can_move && DONT_RUN_INTO(element))
15071   {
15072     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15073
15074     return MP_DONT_RUN_INTO;
15075   }
15076 #endif
15077
15078 #if USE_FIXED_DONT_RUN_INTO
15079   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15080     return MP_NO_ACTION;
15081 #endif
15082
15083 #if !USE_FIXED_DONT_RUN_INTO
15084   element = Feld[x][y];
15085 #endif
15086
15087   collect_count = element_info[element].collect_count_initial;
15088
15089   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15090     return MP_NO_ACTION;
15091
15092   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15093     player_can_move = player_can_move_or_snap;
15094
15095   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15096       game.engine_version >= VERSION_IDENT(2,2,0,0))
15097   {
15098     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15099                                player->index_bit, dig_side);
15100     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15101                                         player->index_bit, dig_side);
15102
15103     if (element == EL_DC_LANDMINE)
15104       Bang(x, y);
15105
15106     if (Feld[x][y] != element)          /* field changed by snapping */
15107       return MP_ACTION;
15108
15109     return MP_NO_ACTION;
15110   }
15111
15112 #if USE_PLAYER_GRAVITY
15113   if (player->gravity && is_player && !player->is_auto_moving &&
15114       canFallDown(player) && move_direction != MV_DOWN &&
15115       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15116     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15117 #else
15118   if (game.gravity && is_player && !player->is_auto_moving &&
15119       canFallDown(player) && move_direction != MV_DOWN &&
15120       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15121     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15122 #endif
15123
15124   if (player_can_move &&
15125       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15126   {
15127     int sound_element = SND_ELEMENT(element);
15128     int sound_action = ACTION_WALKING;
15129
15130     if (IS_RND_GATE(element))
15131     {
15132       if (!player->key[RND_GATE_NR(element)])
15133         return MP_NO_ACTION;
15134     }
15135     else if (IS_RND_GATE_GRAY(element))
15136     {
15137       if (!player->key[RND_GATE_GRAY_NR(element)])
15138         return MP_NO_ACTION;
15139     }
15140     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15141     {
15142       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15143         return MP_NO_ACTION;
15144     }
15145     else if (element == EL_EXIT_OPEN ||
15146              element == EL_EM_EXIT_OPEN ||
15147 #if 1
15148              element == EL_EM_EXIT_OPENING ||
15149 #endif
15150              element == EL_STEEL_EXIT_OPEN ||
15151              element == EL_EM_STEEL_EXIT_OPEN ||
15152 #if 1
15153              element == EL_EM_STEEL_EXIT_OPENING ||
15154 #endif
15155              element == EL_SP_EXIT_OPEN ||
15156              element == EL_SP_EXIT_OPENING)
15157     {
15158       sound_action = ACTION_PASSING;    /* player is passing exit */
15159     }
15160     else if (element == EL_EMPTY)
15161     {
15162       sound_action = ACTION_MOVING;             /* nothing to walk on */
15163     }
15164
15165     /* play sound from background or player, whatever is available */
15166     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15167       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15168     else
15169       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15170   }
15171   else if (player_can_move &&
15172            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15173   {
15174     if (!ACCESS_FROM(element, opposite_direction))
15175       return MP_NO_ACTION;      /* field not accessible from this direction */
15176
15177     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15178       return MP_NO_ACTION;
15179
15180     if (IS_EM_GATE(element))
15181     {
15182       if (!player->key[EM_GATE_NR(element)])
15183         return MP_NO_ACTION;
15184     }
15185     else if (IS_EM_GATE_GRAY(element))
15186     {
15187       if (!player->key[EM_GATE_GRAY_NR(element)])
15188         return MP_NO_ACTION;
15189     }
15190     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15191     {
15192       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15193         return MP_NO_ACTION;
15194     }
15195     else if (IS_EMC_GATE(element))
15196     {
15197       if (!player->key[EMC_GATE_NR(element)])
15198         return MP_NO_ACTION;
15199     }
15200     else if (IS_EMC_GATE_GRAY(element))
15201     {
15202       if (!player->key[EMC_GATE_GRAY_NR(element)])
15203         return MP_NO_ACTION;
15204     }
15205     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15206     {
15207       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15208         return MP_NO_ACTION;
15209     }
15210     else if (element == EL_DC_GATE_WHITE ||
15211              element == EL_DC_GATE_WHITE_GRAY ||
15212              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15213     {
15214       if (player->num_white_keys == 0)
15215         return MP_NO_ACTION;
15216
15217       player->num_white_keys--;
15218     }
15219     else if (IS_SP_PORT(element))
15220     {
15221       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15222           element == EL_SP_GRAVITY_PORT_RIGHT ||
15223           element == EL_SP_GRAVITY_PORT_UP ||
15224           element == EL_SP_GRAVITY_PORT_DOWN)
15225 #if USE_PLAYER_GRAVITY
15226         player->gravity = !player->gravity;
15227 #else
15228         game.gravity = !game.gravity;
15229 #endif
15230       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15231                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15232                element == EL_SP_GRAVITY_ON_PORT_UP ||
15233                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15234 #if USE_PLAYER_GRAVITY
15235         player->gravity = TRUE;
15236 #else
15237         game.gravity = TRUE;
15238 #endif
15239       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15240                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15241                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15242                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15243 #if USE_PLAYER_GRAVITY
15244         player->gravity = FALSE;
15245 #else
15246         game.gravity = FALSE;
15247 #endif
15248     }
15249
15250     /* automatically move to the next field with double speed */
15251     player->programmed_action = move_direction;
15252
15253     if (player->move_delay_reset_counter == 0)
15254     {
15255       player->move_delay_reset_counter = 2;     /* two double speed steps */
15256
15257       DOUBLE_PLAYER_SPEED(player);
15258     }
15259
15260     PlayLevelSoundAction(x, y, ACTION_PASSING);
15261   }
15262   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15263   {
15264     RemoveField(x, y);
15265
15266     if (mode != DF_SNAP)
15267     {
15268       GfxElement[x][y] = GFX_ELEMENT(element);
15269       player->is_digging = TRUE;
15270     }
15271
15272     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15273
15274     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15275                                         player->index_bit, dig_side);
15276
15277     if (mode == DF_SNAP)
15278     {
15279 #if USE_NEW_SNAP_DELAY
15280       if (level.block_snap_field)
15281         setFieldForSnapping(x, y, element, move_direction);
15282       else
15283         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15284 #else
15285       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15286 #endif
15287
15288       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15289                                           player->index_bit, dig_side);
15290     }
15291   }
15292   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15293   {
15294     RemoveField(x, y);
15295
15296     if (is_player && mode != DF_SNAP)
15297     {
15298       GfxElement[x][y] = element;
15299       player->is_collecting = TRUE;
15300     }
15301
15302     if (element == EL_SPEED_PILL)
15303     {
15304       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15305     }
15306     else if (element == EL_EXTRA_TIME && level.time > 0)
15307     {
15308       TimeLeft += level.extra_time;
15309
15310 #if 1
15311       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15312
15313       DisplayGameControlValues();
15314 #else
15315       DrawGameValue_Time(TimeLeft);
15316 #endif
15317     }
15318     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15319     {
15320       player->shield_normal_time_left += level.shield_normal_time;
15321       if (element == EL_SHIELD_DEADLY)
15322         player->shield_deadly_time_left += level.shield_deadly_time;
15323     }
15324     else if (element == EL_DYNAMITE ||
15325              element == EL_EM_DYNAMITE ||
15326              element == EL_SP_DISK_RED)
15327     {
15328       if (player->inventory_size < MAX_INVENTORY_SIZE)
15329         player->inventory_element[player->inventory_size++] = element;
15330
15331       DrawGameDoorValues();
15332     }
15333     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15334     {
15335       player->dynabomb_count++;
15336       player->dynabombs_left++;
15337     }
15338     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15339     {
15340       player->dynabomb_size++;
15341     }
15342     else if (element == EL_DYNABOMB_INCREASE_POWER)
15343     {
15344       player->dynabomb_xl = TRUE;
15345     }
15346     else if (IS_KEY(element))
15347     {
15348       player->key[KEY_NR(element)] = TRUE;
15349
15350       DrawGameDoorValues();
15351     }
15352     else if (element == EL_DC_KEY_WHITE)
15353     {
15354       player->num_white_keys++;
15355
15356       /* display white keys? */
15357       /* DrawGameDoorValues(); */
15358     }
15359     else if (IS_ENVELOPE(element))
15360     {
15361       player->show_envelope = element;
15362     }
15363     else if (element == EL_EMC_LENSES)
15364     {
15365       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15366
15367       RedrawAllInvisibleElementsForLenses();
15368     }
15369     else if (element == EL_EMC_MAGNIFIER)
15370     {
15371       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15372
15373       RedrawAllInvisibleElementsForMagnifier();
15374     }
15375     else if (IS_DROPPABLE(element) ||
15376              IS_THROWABLE(element))     /* can be collected and dropped */
15377     {
15378       int i;
15379
15380       if (collect_count == 0)
15381         player->inventory_infinite_element = element;
15382       else
15383         for (i = 0; i < collect_count; i++)
15384           if (player->inventory_size < MAX_INVENTORY_SIZE)
15385             player->inventory_element[player->inventory_size++] = element;
15386
15387       DrawGameDoorValues();
15388     }
15389     else if (collect_count > 0)
15390     {
15391       local_player->gems_still_needed -= collect_count;
15392       if (local_player->gems_still_needed < 0)
15393         local_player->gems_still_needed = 0;
15394
15395 #if 1
15396       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15397
15398       DisplayGameControlValues();
15399 #else
15400       DrawGameValue_Emeralds(local_player->gems_still_needed);
15401 #endif
15402     }
15403
15404     RaiseScoreElement(element);
15405     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15406
15407     if (is_player)
15408       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15409                                           player->index_bit, dig_side);
15410
15411     if (mode == DF_SNAP)
15412     {
15413 #if USE_NEW_SNAP_DELAY
15414       if (level.block_snap_field)
15415         setFieldForSnapping(x, y, element, move_direction);
15416       else
15417         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15418 #else
15419       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15420 #endif
15421
15422       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15423                                           player->index_bit, dig_side);
15424     }
15425   }
15426   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15427   {
15428     if (mode == DF_SNAP && element != EL_BD_ROCK)
15429       return MP_NO_ACTION;
15430
15431     if (CAN_FALL(element) && dy)
15432       return MP_NO_ACTION;
15433
15434     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15435         !(element == EL_SPRING && level.use_spring_bug))
15436       return MP_NO_ACTION;
15437
15438     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15439         ((move_direction & MV_VERTICAL &&
15440           ((element_info[element].move_pattern & MV_LEFT &&
15441             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15442            (element_info[element].move_pattern & MV_RIGHT &&
15443             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15444          (move_direction & MV_HORIZONTAL &&
15445           ((element_info[element].move_pattern & MV_UP &&
15446             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15447            (element_info[element].move_pattern & MV_DOWN &&
15448             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15449       return MP_NO_ACTION;
15450
15451     /* do not push elements already moving away faster than player */
15452     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15453         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15454       return MP_NO_ACTION;
15455
15456     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15457     {
15458       if (player->push_delay_value == -1 || !player_was_pushing)
15459         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15460     }
15461     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15462     {
15463       if (player->push_delay_value == -1)
15464         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15465     }
15466     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15467     {
15468       if (!player->is_pushing)
15469         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15470     }
15471
15472     player->is_pushing = TRUE;
15473     player->is_active = TRUE;
15474
15475     if (!(IN_LEV_FIELD(nextx, nexty) &&
15476           (IS_FREE(nextx, nexty) ||
15477            (IS_SB_ELEMENT(element) &&
15478             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15479            (IS_CUSTOM_ELEMENT(element) &&
15480             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15481       return MP_NO_ACTION;
15482
15483     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15484       return MP_NO_ACTION;
15485
15486     if (player->push_delay == -1)       /* new pushing; restart delay */
15487       player->push_delay = 0;
15488
15489     if (player->push_delay < player->push_delay_value &&
15490         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15491         element != EL_SPRING && element != EL_BALLOON)
15492     {
15493       /* make sure that there is no move delay before next try to push */
15494       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15495         player->move_delay = 0;
15496
15497       return MP_NO_ACTION;
15498     }
15499
15500     if (IS_CUSTOM_ELEMENT(element) &&
15501         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15502     {
15503       if (!DigFieldByCE(nextx, nexty, element))
15504         return MP_NO_ACTION;
15505     }
15506
15507     if (IS_SB_ELEMENT(element))
15508     {
15509       if (element == EL_SOKOBAN_FIELD_FULL)
15510       {
15511         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15512         local_player->sokobanfields_still_needed++;
15513       }
15514
15515       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15516       {
15517         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15518         local_player->sokobanfields_still_needed--;
15519       }
15520
15521       Feld[x][y] = EL_SOKOBAN_OBJECT;
15522
15523       if (Back[x][y] == Back[nextx][nexty])
15524         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15525       else if (Back[x][y] != 0)
15526         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15527                                     ACTION_EMPTYING);
15528       else
15529         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15530                                     ACTION_FILLING);
15531
15532 #if 1
15533       if (local_player->sokobanfields_still_needed == 0 &&
15534           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15535 #else
15536       if (local_player->sokobanfields_still_needed == 0 &&
15537           game.emulation == EMU_SOKOBAN)
15538 #endif
15539       {
15540         PlayerWins(player);
15541
15542         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15543       }
15544     }
15545     else
15546       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15547
15548     InitMovingField(x, y, move_direction);
15549     GfxAction[x][y] = ACTION_PUSHING;
15550
15551     if (mode == DF_SNAP)
15552       ContinueMoving(x, y);
15553     else
15554       MovPos[x][y] = (dx != 0 ? dx : dy);
15555
15556     Pushed[x][y] = TRUE;
15557     Pushed[nextx][nexty] = TRUE;
15558
15559     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15560       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15561     else
15562       player->push_delay_value = -1;    /* get new value later */
15563
15564     /* check for element change _after_ element has been pushed */
15565     if (game.use_change_when_pushing_bug)
15566     {
15567       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15568                                  player->index_bit, dig_side);
15569       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15570                                           player->index_bit, dig_side);
15571     }
15572   }
15573   else if (IS_SWITCHABLE(element))
15574   {
15575     if (PLAYER_SWITCHING(player, x, y))
15576     {
15577       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15578                                           player->index_bit, dig_side);
15579
15580       return MP_ACTION;
15581     }
15582
15583     player->is_switching = TRUE;
15584     player->switch_x = x;
15585     player->switch_y = y;
15586
15587     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15588
15589     if (element == EL_ROBOT_WHEEL)
15590     {
15591       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15592       ZX = x;
15593       ZY = y;
15594
15595       game.robot_wheel_active = TRUE;
15596
15597       TEST_DrawLevelField(x, y);
15598     }
15599     else if (element == EL_SP_TERMINAL)
15600     {
15601       int xx, yy;
15602
15603       SCAN_PLAYFIELD(xx, yy)
15604       {
15605         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15606           Bang(xx, yy);
15607         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15608           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15609       }
15610     }
15611     else if (IS_BELT_SWITCH(element))
15612     {
15613       ToggleBeltSwitch(x, y);
15614     }
15615     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15616              element == EL_SWITCHGATE_SWITCH_DOWN ||
15617              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15618              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15619     {
15620       ToggleSwitchgateSwitch(x, y);
15621     }
15622     else if (element == EL_LIGHT_SWITCH ||
15623              element == EL_LIGHT_SWITCH_ACTIVE)
15624     {
15625       ToggleLightSwitch(x, y);
15626     }
15627     else if (element == EL_TIMEGATE_SWITCH ||
15628              element == EL_DC_TIMEGATE_SWITCH)
15629     {
15630       ActivateTimegateSwitch(x, y);
15631     }
15632     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15633              element == EL_BALLOON_SWITCH_RIGHT ||
15634              element == EL_BALLOON_SWITCH_UP    ||
15635              element == EL_BALLOON_SWITCH_DOWN  ||
15636              element == EL_BALLOON_SWITCH_NONE  ||
15637              element == EL_BALLOON_SWITCH_ANY)
15638     {
15639       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15640                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15641                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15642                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15643                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15644                              move_direction);
15645     }
15646     else if (element == EL_LAMP)
15647     {
15648       Feld[x][y] = EL_LAMP_ACTIVE;
15649       local_player->lights_still_needed--;
15650
15651       ResetGfxAnimation(x, y);
15652       TEST_DrawLevelField(x, y);
15653     }
15654     else if (element == EL_TIME_ORB_FULL)
15655     {
15656       Feld[x][y] = EL_TIME_ORB_EMPTY;
15657
15658       if (level.time > 0 || level.use_time_orb_bug)
15659       {
15660         TimeLeft += level.time_orb_time;
15661         game.no_time_limit = FALSE;
15662
15663 #if 1
15664         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15665
15666         DisplayGameControlValues();
15667 #else
15668         DrawGameValue_Time(TimeLeft);
15669 #endif
15670       }
15671
15672       ResetGfxAnimation(x, y);
15673       TEST_DrawLevelField(x, y);
15674     }
15675     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15676              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15677     {
15678       int xx, yy;
15679
15680       game.ball_state = !game.ball_state;
15681
15682       SCAN_PLAYFIELD(xx, yy)
15683       {
15684         int e = Feld[xx][yy];
15685
15686         if (game.ball_state)
15687         {
15688           if (e == EL_EMC_MAGIC_BALL)
15689             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15690           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15691             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15692         }
15693         else
15694         {
15695           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15696             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15697           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15698             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15699         }
15700       }
15701     }
15702
15703     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15704                                         player->index_bit, dig_side);
15705
15706     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15707                                         player->index_bit, dig_side);
15708
15709     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15710                                         player->index_bit, dig_side);
15711
15712     return MP_ACTION;
15713   }
15714   else
15715   {
15716     if (!PLAYER_SWITCHING(player, x, y))
15717     {
15718       player->is_switching = TRUE;
15719       player->switch_x = x;
15720       player->switch_y = y;
15721
15722       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15723                                  player->index_bit, dig_side);
15724       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15725                                           player->index_bit, dig_side);
15726
15727       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15728                                  player->index_bit, dig_side);
15729       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15730                                           player->index_bit, dig_side);
15731     }
15732
15733     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15734                                player->index_bit, dig_side);
15735     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15736                                         player->index_bit, dig_side);
15737
15738     return MP_NO_ACTION;
15739   }
15740
15741   player->push_delay = -1;
15742
15743   if (is_player)                /* function can also be called by EL_PENGUIN */
15744   {
15745     if (Feld[x][y] != element)          /* really digged/collected something */
15746     {
15747       player->is_collecting = !player->is_digging;
15748       player->is_active = TRUE;
15749     }
15750   }
15751
15752   return MP_MOVING;
15753 }
15754
15755 static boolean DigFieldByCE(int x, int y, int digging_element)
15756 {
15757   int element = Feld[x][y];
15758
15759   if (!IS_FREE(x, y))
15760   {
15761     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15762                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15763                   ACTION_BREAKING);
15764
15765     /* no element can dig solid indestructible elements */
15766     if (IS_INDESTRUCTIBLE(element) &&
15767         !IS_DIGGABLE(element) &&
15768         !IS_COLLECTIBLE(element))
15769       return FALSE;
15770
15771     if (AmoebaNr[x][y] &&
15772         (element == EL_AMOEBA_FULL ||
15773          element == EL_BD_AMOEBA ||
15774          element == EL_AMOEBA_GROWING))
15775     {
15776       AmoebaCnt[AmoebaNr[x][y]]--;
15777       AmoebaCnt2[AmoebaNr[x][y]]--;
15778     }
15779
15780     if (IS_MOVING(x, y))
15781       RemoveMovingField(x, y);
15782     else
15783     {
15784       RemoveField(x, y);
15785       TEST_DrawLevelField(x, y);
15786     }
15787
15788     /* if digged element was about to explode, prevent the explosion */
15789     ExplodeField[x][y] = EX_TYPE_NONE;
15790
15791     PlayLevelSoundAction(x, y, action);
15792   }
15793
15794   Store[x][y] = EL_EMPTY;
15795
15796 #if 1
15797   /* this makes it possible to leave the removed element again */
15798   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15799     Store[x][y] = element;
15800 #else
15801   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15802   {
15803     int move_leave_element = element_info[digging_element].move_leave_element;
15804
15805     /* this makes it possible to leave the removed element again */
15806     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15807                    element : move_leave_element);
15808   }
15809 #endif
15810
15811   return TRUE;
15812 }
15813
15814 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15815 {
15816   int jx = player->jx, jy = player->jy;
15817   int x = jx + dx, y = jy + dy;
15818   int snap_direction = (dx == -1 ? MV_LEFT  :
15819                         dx == +1 ? MV_RIGHT :
15820                         dy == -1 ? MV_UP    :
15821                         dy == +1 ? MV_DOWN  : MV_NONE);
15822   boolean can_continue_snapping = (level.continuous_snapping &&
15823                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15824
15825   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15826     return FALSE;
15827
15828   if (!player->active || !IN_LEV_FIELD(x, y))
15829     return FALSE;
15830
15831   if (dx && dy)
15832     return FALSE;
15833
15834   if (!dx && !dy)
15835   {
15836     if (player->MovPos == 0)
15837       player->is_pushing = FALSE;
15838
15839     player->is_snapping = FALSE;
15840
15841     if (player->MovPos == 0)
15842     {
15843       player->is_moving = FALSE;
15844       player->is_digging = FALSE;
15845       player->is_collecting = FALSE;
15846     }
15847
15848     return FALSE;
15849   }
15850
15851 #if USE_NEW_CONTINUOUS_SNAPPING
15852   /* prevent snapping with already pressed snap key when not allowed */
15853   if (player->is_snapping && !can_continue_snapping)
15854     return FALSE;
15855 #else
15856   if (player->is_snapping)
15857     return FALSE;
15858 #endif
15859
15860   player->MovDir = snap_direction;
15861
15862   if (player->MovPos == 0)
15863   {
15864     player->is_moving = FALSE;
15865     player->is_digging = FALSE;
15866     player->is_collecting = FALSE;
15867   }
15868
15869   player->is_dropping = FALSE;
15870   player->is_dropping_pressed = FALSE;
15871   player->drop_pressed_delay = 0;
15872
15873   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15874     return FALSE;
15875
15876   player->is_snapping = TRUE;
15877   player->is_active = TRUE;
15878
15879   if (player->MovPos == 0)
15880   {
15881     player->is_moving = FALSE;
15882     player->is_digging = FALSE;
15883     player->is_collecting = FALSE;
15884   }
15885
15886   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15887     TEST_DrawLevelField(player->last_jx, player->last_jy);
15888
15889   TEST_DrawLevelField(x, y);
15890
15891   return TRUE;
15892 }
15893
15894 static boolean DropElement(struct PlayerInfo *player)
15895 {
15896   int old_element, new_element;
15897   int dropx = player->jx, dropy = player->jy;
15898   int drop_direction = player->MovDir;
15899   int drop_side = drop_direction;
15900 #if 1
15901   int drop_element = get_next_dropped_element(player);
15902 #else
15903   int drop_element = (player->inventory_size > 0 ?
15904                       player->inventory_element[player->inventory_size - 1] :
15905                       player->inventory_infinite_element != EL_UNDEFINED ?
15906                       player->inventory_infinite_element :
15907                       player->dynabombs_left > 0 ?
15908                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15909                       EL_UNDEFINED);
15910 #endif
15911
15912   player->is_dropping_pressed = TRUE;
15913
15914   /* do not drop an element on top of another element; when holding drop key
15915      pressed without moving, dropped element must move away before the next
15916      element can be dropped (this is especially important if the next element
15917      is dynamite, which can be placed on background for historical reasons) */
15918   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15919     return MP_ACTION;
15920
15921   if (IS_THROWABLE(drop_element))
15922   {
15923     dropx += GET_DX_FROM_DIR(drop_direction);
15924     dropy += GET_DY_FROM_DIR(drop_direction);
15925
15926     if (!IN_LEV_FIELD(dropx, dropy))
15927       return FALSE;
15928   }
15929
15930   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15931   new_element = drop_element;           /* default: no change when dropping */
15932
15933   /* check if player is active, not moving and ready to drop */
15934   if (!player->active || player->MovPos || player->drop_delay > 0)
15935     return FALSE;
15936
15937   /* check if player has anything that can be dropped */
15938   if (new_element == EL_UNDEFINED)
15939     return FALSE;
15940
15941   /* check if drop key was pressed long enough for EM style dynamite */
15942   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15943     return FALSE;
15944
15945   /* check if anything can be dropped at the current position */
15946   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15947     return FALSE;
15948
15949   /* collected custom elements can only be dropped on empty fields */
15950   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15951     return FALSE;
15952
15953   if (old_element != EL_EMPTY)
15954     Back[dropx][dropy] = old_element;   /* store old element on this field */
15955
15956   ResetGfxAnimation(dropx, dropy);
15957   ResetRandomAnimationValue(dropx, dropy);
15958
15959   if (player->inventory_size > 0 ||
15960       player->inventory_infinite_element != EL_UNDEFINED)
15961   {
15962     if (player->inventory_size > 0)
15963     {
15964       player->inventory_size--;
15965
15966       DrawGameDoorValues();
15967
15968       if (new_element == EL_DYNAMITE)
15969         new_element = EL_DYNAMITE_ACTIVE;
15970       else if (new_element == EL_EM_DYNAMITE)
15971         new_element = EL_EM_DYNAMITE_ACTIVE;
15972       else if (new_element == EL_SP_DISK_RED)
15973         new_element = EL_SP_DISK_RED_ACTIVE;
15974     }
15975
15976     Feld[dropx][dropy] = new_element;
15977
15978     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15979       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15980                           el2img(Feld[dropx][dropy]), 0);
15981
15982     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15983
15984     /* needed if previous element just changed to "empty" in the last frame */
15985     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15986
15987     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15988                                player->index_bit, drop_side);
15989     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15990                                         CE_PLAYER_DROPS_X,
15991                                         player->index_bit, drop_side);
15992
15993     TestIfElementTouchesCustomElement(dropx, dropy);
15994   }
15995   else          /* player is dropping a dyna bomb */
15996   {
15997     player->dynabombs_left--;
15998
15999     Feld[dropx][dropy] = new_element;
16000
16001     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16002       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16003                           el2img(Feld[dropx][dropy]), 0);
16004
16005     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16006   }
16007
16008   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16009     InitField_WithBug1(dropx, dropy, FALSE);
16010
16011   new_element = Feld[dropx][dropy];     /* element might have changed */
16012
16013   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16014       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16015   {
16016 #if 0
16017     int move_direction;
16018     int nextx, nexty;
16019 #endif
16020
16021     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16022       MovDir[dropx][dropy] = drop_direction;
16023
16024 #if 0
16025     move_direction = MovDir[dropx][dropy];
16026     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16027     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16028 #endif
16029
16030     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16031
16032 #if USE_FIX_IMPACT_COLLISION
16033     /* do not cause impact style collision by dropping elements that can fall */
16034     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16035 #else
16036     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16037 #endif
16038   }
16039
16040   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16041   player->is_dropping = TRUE;
16042
16043   player->drop_pressed_delay = 0;
16044   player->is_dropping_pressed = FALSE;
16045
16046   player->drop_x = dropx;
16047   player->drop_y = dropy;
16048
16049   return TRUE;
16050 }
16051
16052 /* ------------------------------------------------------------------------- */
16053 /* game sound playing functions                                              */
16054 /* ------------------------------------------------------------------------- */
16055
16056 static int *loop_sound_frame = NULL;
16057 static int *loop_sound_volume = NULL;
16058
16059 void InitPlayLevelSound()
16060 {
16061   int num_sounds = getSoundListSize();
16062
16063   checked_free(loop_sound_frame);
16064   checked_free(loop_sound_volume);
16065
16066   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16067   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16068 }
16069
16070 static void PlayLevelSound(int x, int y, int nr)
16071 {
16072   int sx = SCREENX(x), sy = SCREENY(y);
16073   int volume, stereo_position;
16074   int max_distance = 8;
16075   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16076
16077   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16078       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16079     return;
16080
16081   if (!IN_LEV_FIELD(x, y) ||
16082       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16083       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16084     return;
16085
16086   volume = SOUND_MAX_VOLUME;
16087
16088   if (!IN_SCR_FIELD(sx, sy))
16089   {
16090     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16091     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16092
16093     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16094   }
16095
16096   stereo_position = (SOUND_MAX_LEFT +
16097                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16098                      (SCR_FIELDX + 2 * max_distance));
16099
16100   if (IS_LOOP_SOUND(nr))
16101   {
16102     /* This assures that quieter loop sounds do not overwrite louder ones,
16103        while restarting sound volume comparison with each new game frame. */
16104
16105     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16106       return;
16107
16108     loop_sound_volume[nr] = volume;
16109     loop_sound_frame[nr] = FrameCounter;
16110   }
16111
16112   PlaySoundExt(nr, volume, stereo_position, type);
16113 }
16114
16115 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16116 {
16117   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16118                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16119                  y < LEVELY(BY1) ? LEVELY(BY1) :
16120                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16121                  sound_action);
16122 }
16123
16124 static void PlayLevelSoundAction(int x, int y, int action)
16125 {
16126   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16127 }
16128
16129 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16130 {
16131   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16132
16133   if (sound_effect != SND_UNDEFINED)
16134     PlayLevelSound(x, y, sound_effect);
16135 }
16136
16137 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16138                                               int action)
16139 {
16140   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16141
16142   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16143     PlayLevelSound(x, y, sound_effect);
16144 }
16145
16146 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16147 {
16148   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16149
16150   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16151     PlayLevelSound(x, y, sound_effect);
16152 }
16153
16154 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16155 {
16156   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16157
16158   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16159     StopSound(sound_effect);
16160 }
16161
16162 static void PlayLevelMusic()
16163 {
16164   if (levelset.music[level_nr] != MUS_UNDEFINED)
16165     PlayMusic(levelset.music[level_nr]);        /* from config file */
16166   else
16167     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16168 }
16169
16170 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16171 {
16172   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16173   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16174   int x = xx - 1 - offset;
16175   int y = yy - 1 - offset;
16176
16177   switch (sample)
16178   {
16179     case SAMPLE_blank:
16180       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16181       break;
16182
16183     case SAMPLE_roll:
16184       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16185       break;
16186
16187     case SAMPLE_stone:
16188       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16189       break;
16190
16191     case SAMPLE_nut:
16192       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16193       break;
16194
16195     case SAMPLE_crack:
16196       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16197       break;
16198
16199     case SAMPLE_bug:
16200       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16201       break;
16202
16203     case SAMPLE_tank:
16204       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16205       break;
16206
16207     case SAMPLE_android_clone:
16208       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16209       break;
16210
16211     case SAMPLE_android_move:
16212       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16213       break;
16214
16215     case SAMPLE_spring:
16216       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16217       break;
16218
16219     case SAMPLE_slurp:
16220       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16221       break;
16222
16223     case SAMPLE_eater:
16224       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16225       break;
16226
16227     case SAMPLE_eater_eat:
16228       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16229       break;
16230
16231     case SAMPLE_alien:
16232       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16233       break;
16234
16235     case SAMPLE_collect:
16236       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16237       break;
16238
16239     case SAMPLE_diamond:
16240       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16241       break;
16242
16243     case SAMPLE_squash:
16244       /* !!! CHECK THIS !!! */
16245 #if 1
16246       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16247 #else
16248       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16249 #endif
16250       break;
16251
16252     case SAMPLE_wonderfall:
16253       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16254       break;
16255
16256     case SAMPLE_drip:
16257       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16258       break;
16259
16260     case SAMPLE_push:
16261       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16262       break;
16263
16264     case SAMPLE_dirt:
16265       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16266       break;
16267
16268     case SAMPLE_acid:
16269       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16270       break;
16271
16272     case SAMPLE_ball:
16273       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16274       break;
16275
16276     case SAMPLE_grow:
16277       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16278       break;
16279
16280     case SAMPLE_wonder:
16281       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16282       break;
16283
16284     case SAMPLE_door:
16285       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16286       break;
16287
16288     case SAMPLE_exit_open:
16289       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16290       break;
16291
16292     case SAMPLE_exit_leave:
16293       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16294       break;
16295
16296     case SAMPLE_dynamite:
16297       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16298       break;
16299
16300     case SAMPLE_tick:
16301       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16302       break;
16303
16304     case SAMPLE_press:
16305       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16306       break;
16307
16308     case SAMPLE_wheel:
16309       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16310       break;
16311
16312     case SAMPLE_boom:
16313       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16314       break;
16315
16316     case SAMPLE_die:
16317       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16318       break;
16319
16320     case SAMPLE_time:
16321       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16322       break;
16323
16324     default:
16325       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16326       break;
16327   }
16328 }
16329
16330 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16331 {
16332   int element = map_element_SP_to_RND(element_sp);
16333   int action = map_action_SP_to_RND(action_sp);
16334   int offset = (setup.sp_show_border_elements ? 0 : 1);
16335   int x = xx - offset;
16336   int y = yy - offset;
16337
16338 #if 0
16339   printf("::: %d -> %d\n", element_sp, action_sp);
16340 #endif
16341
16342   PlayLevelSoundElementAction(x, y, element, action);
16343 }
16344
16345 void RaiseScore(int value)
16346 {
16347   local_player->score += value;
16348
16349 #if 1
16350   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16351
16352   DisplayGameControlValues();
16353 #else
16354   DrawGameValue_Score(local_player->score);
16355 #endif
16356 }
16357
16358 void RaiseScoreElement(int element)
16359 {
16360   switch (element)
16361   {
16362     case EL_EMERALD:
16363     case EL_BD_DIAMOND:
16364     case EL_EMERALD_YELLOW:
16365     case EL_EMERALD_RED:
16366     case EL_EMERALD_PURPLE:
16367     case EL_SP_INFOTRON:
16368       RaiseScore(level.score[SC_EMERALD]);
16369       break;
16370     case EL_DIAMOND:
16371       RaiseScore(level.score[SC_DIAMOND]);
16372       break;
16373     case EL_CRYSTAL:
16374       RaiseScore(level.score[SC_CRYSTAL]);
16375       break;
16376     case EL_PEARL:
16377       RaiseScore(level.score[SC_PEARL]);
16378       break;
16379     case EL_BUG:
16380     case EL_BD_BUTTERFLY:
16381     case EL_SP_ELECTRON:
16382       RaiseScore(level.score[SC_BUG]);
16383       break;
16384     case EL_SPACESHIP:
16385     case EL_BD_FIREFLY:
16386     case EL_SP_SNIKSNAK:
16387       RaiseScore(level.score[SC_SPACESHIP]);
16388       break;
16389     case EL_YAMYAM:
16390     case EL_DARK_YAMYAM:
16391       RaiseScore(level.score[SC_YAMYAM]);
16392       break;
16393     case EL_ROBOT:
16394       RaiseScore(level.score[SC_ROBOT]);
16395       break;
16396     case EL_PACMAN:
16397       RaiseScore(level.score[SC_PACMAN]);
16398       break;
16399     case EL_NUT:
16400       RaiseScore(level.score[SC_NUT]);
16401       break;
16402     case EL_DYNAMITE:
16403     case EL_EM_DYNAMITE:
16404     case EL_SP_DISK_RED:
16405     case EL_DYNABOMB_INCREASE_NUMBER:
16406     case EL_DYNABOMB_INCREASE_SIZE:
16407     case EL_DYNABOMB_INCREASE_POWER:
16408       RaiseScore(level.score[SC_DYNAMITE]);
16409       break;
16410     case EL_SHIELD_NORMAL:
16411     case EL_SHIELD_DEADLY:
16412       RaiseScore(level.score[SC_SHIELD]);
16413       break;
16414     case EL_EXTRA_TIME:
16415       RaiseScore(level.extra_time_score);
16416       break;
16417     case EL_KEY_1:
16418     case EL_KEY_2:
16419     case EL_KEY_3:
16420     case EL_KEY_4:
16421     case EL_EM_KEY_1:
16422     case EL_EM_KEY_2:
16423     case EL_EM_KEY_3:
16424     case EL_EM_KEY_4:
16425     case EL_EMC_KEY_5:
16426     case EL_EMC_KEY_6:
16427     case EL_EMC_KEY_7:
16428     case EL_EMC_KEY_8:
16429     case EL_DC_KEY_WHITE:
16430       RaiseScore(level.score[SC_KEY]);
16431       break;
16432     default:
16433       RaiseScore(element_info[element].collect_score);
16434       break;
16435   }
16436 }
16437
16438 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16439 {
16440   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16441   {
16442 #if defined(NETWORK_AVALIABLE)
16443     if (options.network)
16444       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16445     else
16446 #endif
16447     {
16448       if (quick_quit)
16449       {
16450 #if 1
16451
16452 #if 1
16453         FadeSkipNextFadeIn();
16454 #else
16455         fading = fading_none;
16456 #endif
16457
16458 #else
16459         OpenDoor(DOOR_CLOSE_1);
16460 #endif
16461
16462         game_status = GAME_MODE_MAIN;
16463
16464 #if 1
16465         DrawAndFadeInMainMenu(REDRAW_FIELD);
16466 #else
16467         DrawMainMenu();
16468 #endif
16469       }
16470       else
16471       {
16472 #if 0
16473         FadeOut(REDRAW_FIELD);
16474 #endif
16475
16476         game_status = GAME_MODE_MAIN;
16477
16478         DrawAndFadeInMainMenu(REDRAW_FIELD);
16479       }
16480     }
16481   }
16482   else          /* continue playing the game */
16483   {
16484     if (tape.playing && tape.deactivate_display)
16485       TapeDeactivateDisplayOff(TRUE);
16486
16487     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16488
16489     if (tape.playing && tape.deactivate_display)
16490       TapeDeactivateDisplayOn();
16491   }
16492 }
16493
16494 void RequestQuitGame(boolean ask_if_really_quit)
16495 {
16496   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16497   boolean skip_request = AllPlayersGone || quick_quit;
16498
16499   RequestQuitGameExt(skip_request, quick_quit,
16500                      "Do you really want to quit the game?");
16501 }
16502
16503
16504 /* ------------------------------------------------------------------------- */
16505 /* random generator functions                                                */
16506 /* ------------------------------------------------------------------------- */
16507
16508 unsigned int InitEngineRandom_RND(int seed)
16509 {
16510   game.num_random_calls = 0;
16511
16512 #if 0
16513   unsigned int rnd_seed = InitEngineRandom(seed);
16514
16515   printf("::: START RND: %d\n", rnd_seed);
16516
16517   return rnd_seed;
16518 #else
16519
16520   return InitEngineRandom(seed);
16521
16522 #endif
16523
16524 }
16525
16526 unsigned int RND(int max)
16527 {
16528   if (max > 0)
16529   {
16530     game.num_random_calls++;
16531
16532     return GetEngineRandom(max);
16533   }
16534
16535   return 0;
16536 }
16537
16538
16539 /* ------------------------------------------------------------------------- */
16540 /* game engine snapshot handling functions                                   */
16541 /* ------------------------------------------------------------------------- */
16542
16543 struct EngineSnapshotInfo
16544 {
16545   /* runtime values for custom element collect score */
16546   int collect_score[NUM_CUSTOM_ELEMENTS];
16547
16548   /* runtime values for group element choice position */
16549   int choice_pos[NUM_GROUP_ELEMENTS];
16550
16551   /* runtime values for belt position animations */
16552   int belt_graphic[4][NUM_BELT_PARTS];
16553   int belt_anim_mode[4][NUM_BELT_PARTS];
16554 };
16555
16556 static struct EngineSnapshotInfo engine_snapshot_rnd;
16557 static char *snapshot_level_identifier = NULL;
16558 static int snapshot_level_nr = -1;
16559
16560 static void SaveEngineSnapshotValues_RND()
16561 {
16562   static int belt_base_active_element[4] =
16563   {
16564     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16565     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16566     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16567     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16568   };
16569   int i, j;
16570
16571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16572   {
16573     int element = EL_CUSTOM_START + i;
16574
16575     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16576   }
16577
16578   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16579   {
16580     int element = EL_GROUP_START + i;
16581
16582     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16583   }
16584
16585   for (i = 0; i < 4; i++)
16586   {
16587     for (j = 0; j < NUM_BELT_PARTS; j++)
16588     {
16589       int element = belt_base_active_element[i] + j;
16590       int graphic = el2img(element);
16591       int anim_mode = graphic_info[graphic].anim_mode;
16592
16593       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16594       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16595     }
16596   }
16597 }
16598
16599 static void LoadEngineSnapshotValues_RND()
16600 {
16601   unsigned int num_random_calls = game.num_random_calls;
16602   int i, j;
16603
16604   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16605   {
16606     int element = EL_CUSTOM_START + i;
16607
16608     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16609   }
16610
16611   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16612   {
16613     int element = EL_GROUP_START + i;
16614
16615     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16616   }
16617
16618   for (i = 0; i < 4; i++)
16619   {
16620     for (j = 0; j < NUM_BELT_PARTS; j++)
16621     {
16622       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16623       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16624
16625       graphic_info[graphic].anim_mode = anim_mode;
16626     }
16627   }
16628
16629   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16630   {
16631     InitRND(tape.random_seed);
16632     for (i = 0; i < num_random_calls; i++)
16633       RND(1);
16634   }
16635
16636   if (game.num_random_calls != num_random_calls)
16637   {
16638     Error(ERR_INFO, "number of random calls out of sync");
16639     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16640     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16641     Error(ERR_EXIT, "this should not happen -- please debug");
16642   }
16643 }
16644
16645 void SaveEngineSnapshot()
16646 {
16647   /* do not save snapshots from editor */
16648   if (level_editor_test_game)
16649     return;
16650
16651   /* free previous snapshot buffers, if needed */
16652   FreeEngineSnapshotBuffers();
16653
16654   /* copy some special values to a structure better suited for the snapshot */
16655
16656   SaveEngineSnapshotValues_RND();
16657   SaveEngineSnapshotValues_EM();
16658   SaveEngineSnapshotValues_SP();
16659
16660   /* save values stored in special snapshot structure */
16661
16662   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16665
16666   /* save further RND engine values */
16667
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16671
16672   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16676
16677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16682
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16686
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16688
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16690
16691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16693
16694   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16712
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16715
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16719
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16722
16723   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16728
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16731
16732   /* save level identification information */
16733
16734   setString(&snapshot_level_identifier, leveldir_current->identifier);
16735   snapshot_level_nr = level_nr;
16736
16737 #if 0
16738   ListNode *node = engine_snapshot_list_rnd;
16739   int num_bytes = 0;
16740
16741   while (node != NULL)
16742   {
16743     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16744
16745     node = node->next;
16746   }
16747
16748   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16749 #endif
16750 }
16751
16752 void LoadEngineSnapshot()
16753 {
16754   /* restore generically stored snapshot buffers */
16755
16756   LoadEngineSnapshotBuffers();
16757
16758   /* restore special values from snapshot structure */
16759
16760   LoadEngineSnapshotValues_RND();
16761   LoadEngineSnapshotValues_EM();
16762   LoadEngineSnapshotValues_SP();
16763 }
16764
16765 boolean CheckEngineSnapshot()
16766 {
16767   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16768           snapshot_level_nr == level_nr);
16769 }
16770
16771
16772 /* ---------- new game button stuff ---------------------------------------- */
16773
16774 static struct
16775 {
16776   int graphic;
16777   struct Rect *pos;
16778   int gadget_id;
16779   char *infotext;
16780 } gamebutton_info[NUM_GAME_BUTTONS] =
16781 {
16782   {
16783     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16784     GAME_CTRL_ID_STOP,                  "stop game"
16785   },
16786   {
16787     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16788     GAME_CTRL_ID_PAUSE,                 "pause game"
16789   },
16790   {
16791     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16792     GAME_CTRL_ID_PLAY,                  "play game"
16793   },
16794   {
16795     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16796     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16797   },
16798   {
16799     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16800     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16801   },
16802   {
16803     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16804     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16805   }
16806 };
16807
16808 void CreateGameButtons()
16809 {
16810   int i;
16811
16812   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16813   {
16814     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16815     struct Rect *pos = gamebutton_info[i].pos;
16816     struct GadgetInfo *gi;
16817     int button_type;
16818     boolean checked;
16819     unsigned int event_mask;
16820     int gd_x   = gfx->src_x;
16821     int gd_y   = gfx->src_y;
16822     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16823     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16824     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16825     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16826     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16827     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16828     int id = i;
16829
16830     if (id == GAME_CTRL_ID_STOP ||
16831         id == GAME_CTRL_ID_PAUSE ||
16832         id == GAME_CTRL_ID_PLAY)
16833     {
16834       button_type = GD_TYPE_NORMAL_BUTTON;
16835       checked = FALSE;
16836       event_mask = GD_EVENT_RELEASED;
16837     }
16838     else
16839     {
16840       button_type = GD_TYPE_CHECK_BUTTON;
16841       checked =
16842         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16843          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16844          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16845       event_mask = GD_EVENT_PRESSED;
16846     }
16847
16848     gi = CreateGadget(GDI_CUSTOM_ID, id,
16849                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16850                       GDI_X, DX + pos->x,
16851                       GDI_Y, DY + pos->y,
16852                       GDI_WIDTH, gfx->width,
16853                       GDI_HEIGHT, gfx->height,
16854                       GDI_TYPE, button_type,
16855                       GDI_STATE, GD_BUTTON_UNPRESSED,
16856                       GDI_CHECKED, checked,
16857                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16858                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16859                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16860                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16861                       GDI_DIRECT_DRAW, FALSE,
16862                       GDI_EVENT_MASK, event_mask,
16863                       GDI_CALLBACK_ACTION, HandleGameButtons,
16864                       GDI_END);
16865
16866     if (gi == NULL)
16867       Error(ERR_EXIT, "cannot create gadget");
16868
16869     game_gadget[id] = gi;
16870   }
16871 }
16872
16873 void FreeGameButtons()
16874 {
16875   int i;
16876
16877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878     FreeGadget(game_gadget[i]);
16879 }
16880
16881 static void MapGameButtons()
16882 {
16883   int i;
16884
16885   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886     MapGadget(game_gadget[i]);
16887 }
16888
16889 void UnmapGameButtons()
16890 {
16891   int i;
16892
16893   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16894     UnmapGadget(game_gadget[i]);
16895 }
16896
16897 void RedrawGameButtons()
16898 {
16899   int i;
16900
16901   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16902     RedrawGadget(game_gadget[i]);
16903 }
16904
16905 static void HandleGameButtonsExt(int id)
16906 {
16907   if (game_status != GAME_MODE_PLAYING)
16908     return;
16909
16910   switch (id)
16911   {
16912     case GAME_CTRL_ID_STOP:
16913       if (tape.playing)
16914         TapeStop();
16915       else
16916         RequestQuitGame(TRUE);
16917       break;
16918
16919     case GAME_CTRL_ID_PAUSE:
16920       if (options.network)
16921       {
16922 #if defined(NETWORK_AVALIABLE)
16923         if (tape.pausing)
16924           SendToServer_ContinuePlaying();
16925         else
16926           SendToServer_PausePlaying();
16927 #endif
16928       }
16929       else
16930         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16931       break;
16932
16933     case GAME_CTRL_ID_PLAY:
16934       if (tape.pausing)
16935       {
16936 #if defined(NETWORK_AVALIABLE)
16937         if (options.network)
16938           SendToServer_ContinuePlaying();
16939         else
16940 #endif
16941         {
16942           tape.pausing = FALSE;
16943           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16944         }
16945       }
16946       break;
16947
16948     case SOUND_CTRL_ID_MUSIC:
16949       if (setup.sound_music)
16950       { 
16951         setup.sound_music = FALSE;
16952
16953         FadeMusic();
16954       }
16955       else if (audio.music_available)
16956       { 
16957         setup.sound = setup.sound_music = TRUE;
16958
16959         SetAudioMode(setup.sound);
16960
16961         PlayLevelMusic();
16962       }
16963       break;
16964
16965     case SOUND_CTRL_ID_LOOPS:
16966       if (setup.sound_loops)
16967         setup.sound_loops = FALSE;
16968       else if (audio.loops_available)
16969       {
16970         setup.sound = setup.sound_loops = TRUE;
16971
16972         SetAudioMode(setup.sound);
16973       }
16974       break;
16975
16976     case SOUND_CTRL_ID_SIMPLE:
16977       if (setup.sound_simple)
16978         setup.sound_simple = FALSE;
16979       else if (audio.sound_available)
16980       {
16981         setup.sound = setup.sound_simple = TRUE;
16982
16983         SetAudioMode(setup.sound);
16984       }
16985       break;
16986
16987     default:
16988       break;
16989   }
16990 }
16991
16992 static void HandleGameButtons(struct GadgetInfo *gi)
16993 {
16994   HandleGameButtonsExt(gi->custom_id);
16995 }
16996
16997 void HandleSoundButtonKeys(Key key)
16998 {
16999 #if 1
17000   if (key == setup.shortcut.sound_simple)
17001     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17002   else if (key == setup.shortcut.sound_loops)
17003     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17004   else if (key == setup.shortcut.sound_music)
17005     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17006 #else
17007   if (key == setup.shortcut.sound_simple)
17008     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17009   else if (key == setup.shortcut.sound_loops)
17010     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17011   else if (key == setup.shortcut.sound_music)
17012     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17013 #endif
17014 }