rnd-20070403-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL_NUMBER               0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY_COUNT            2
137 #define GAME_CONTROL_INVENTORY_FIRST_1          3
138 #define GAME_CONTROL_INVENTORY_FIRST_2          4
139 #define GAME_CONTROL_INVENTORY_FIRST_3          5
140 #define GAME_CONTROL_INVENTORY_FIRST_4          6
141 #define GAME_CONTROL_INVENTORY_FIRST_5          7
142 #define GAME_CONTROL_INVENTORY_FIRST_6          8
143 #define GAME_CONTROL_INVENTORY_FIRST_7          9
144 #define GAME_CONTROL_INVENTORY_FIRST_8          10
145 #define GAME_CONTROL_INVENTORY_LAST_1           11
146 #define GAME_CONTROL_INVENTORY_LAST_2           12
147 #define GAME_CONTROL_INVENTORY_LAST_3           13
148 #define GAME_CONTROL_INVENTORY_LAST_4           14
149 #define GAME_CONTROL_INVENTORY_LAST_5           15
150 #define GAME_CONTROL_INVENTORY_LAST_6           16
151 #define GAME_CONTROL_INVENTORY_LAST_7           17
152 #define GAME_CONTROL_INVENTORY_LAST_8           18
153 #define GAME_CONTROL_KEY_1                      19
154 #define GAME_CONTROL_KEY_2                      20
155 #define GAME_CONTROL_KEY_3                      21
156 #define GAME_CONTROL_KEY_4                      22
157 #define GAME_CONTROL_KEY_5                      23
158 #define GAME_CONTROL_KEY_6                      24
159 #define GAME_CONTROL_KEY_7                      25
160 #define GAME_CONTROL_KEY_8                      26
161 #define GAME_CONTROL_KEY_WHITE                  27
162 #define GAME_CONTROL_KEY_WHITE_COUNT            28
163 #define GAME_CONTROL_SCORE                      29
164 #define GAME_CONTROL_TIME                       30
165 #define GAME_CONTROL_TIME_HH                    31
166 #define GAME_CONTROL_TIME_MM                    32
167 #define GAME_CONTROL_TIME_SS                    33
168 #define GAME_CONTROL_SHIELD_NORMAL              34
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME         35
170 #define GAME_CONTROL_SHIELD_DEADLY              36
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME         37
172 #define GAME_CONTROL_EXIT                       38
173 #define GAME_CONTROL_EM_EXIT                    39
174 #define GAME_CONTROL_SP_EXIT                    40
175 #define GAME_CONTROL_STEEL_EXIT                 41
176 #define GAME_CONTROL_EM_STEEL_EXIT              42
177 #define GAME_CONTROL_EMC_MAGIC_BALL             43
178 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      44
179 #define GAME_CONTROL_LIGHT_SWITCH               45
180 #define GAME_CONTROL_LIGHT_SWITCH_TIME          46
181 #define GAME_CONTROL_TIMEGATE_SWITCH            47
182 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       48
183 #define GAME_CONTROL_SWITCHGATE_SWITCH          49
184 #define GAME_CONTROL_EMC_LENSES                 50
185 #define GAME_CONTROL_EMC_LENSES_TIME            51
186 #define GAME_CONTROL_EMC_MAGNIFIER              52
187 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         53
188 #define GAME_CONTROL_BALLOON_SWITCH             54
189 #define GAME_CONTROL_DYNABOMB_NUMBER            55
190 #define GAME_CONTROL_DYNABOMB_SIZE              56
191 #define GAME_CONTROL_DYNABOMB_POWER             57
192 #define GAME_CONTROL_PENGUINS                   58
193 #define GAME_CONTROL_SOKOBAN_OBJECTS            59
194 #define GAME_CONTROL_SOKOBAN_FIELDS             60
195 #define GAME_CONTROL_ROBOT_WHEEL                61
196 #define GAME_CONTROL_CONVEYOR_BELT_1            62
197 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     63
198 #define GAME_CONTROL_CONVEYOR_BELT_2            64
199 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     65
200 #define GAME_CONTROL_CONVEYOR_BELT_3            66
201 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     67
202 #define GAME_CONTROL_CONVEYOR_BELT_4            68
203 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     69
204 #define GAME_CONTROL_MAGIC_WALL                 70
205 #define GAME_CONTROL_MAGIC_WALL_TIME            71
206 #define GAME_CONTROL_BD_MAGIC_WALL              72
207 #define GAME_CONTROL_DC_MAGIC_WALL              73
208 #define GAME_CONTROL_PLAYER_NAME                74
209 #define GAME_CONTROL_LEVEL_NAME                 75
210 #define GAME_CONTROL_LEVEL_AUTHOR               76
211
212 #define NUM_GAME_CONTROLS                       77
213
214 int game_control_value[NUM_GAME_CONTROLS];
215 int last_game_control_value[NUM_GAME_CONTROLS];
216
217 struct GameControlInfo
218 {
219   int nr;
220
221   struct TextPosInfo *pos;
222   int type;
223 };
224
225 static struct GameControlInfo game_controls[] =
226 {
227   {
228     GAME_CONTROL_LEVEL_NUMBER,
229     &game.panel.level_number,
230     TYPE_INTEGER,
231   },
232   {
233     GAME_CONTROL_GEMS,
234     &game.panel.gems,
235     TYPE_INTEGER,
236   },
237   {
238     GAME_CONTROL_INVENTORY_COUNT,
239     &game.panel.inventory_count,
240     TYPE_INTEGER,
241   },
242   {
243     GAME_CONTROL_INVENTORY_FIRST_1,
244     &game.panel.inventory_first_1,
245     TYPE_ELEMENT,
246   },
247   {
248     GAME_CONTROL_INVENTORY_FIRST_2,
249     &game.panel.inventory_first_2,
250     TYPE_ELEMENT,
251   },
252   {
253     GAME_CONTROL_INVENTORY_FIRST_3,
254     &game.panel.inventory_first_3,
255     TYPE_ELEMENT,
256   },
257   {
258     GAME_CONTROL_INVENTORY_FIRST_4,
259     &game.panel.inventory_first_4,
260     TYPE_ELEMENT,
261   },
262   {
263     GAME_CONTROL_INVENTORY_FIRST_5,
264     &game.panel.inventory_first_5,
265     TYPE_ELEMENT,
266   },
267   {
268     GAME_CONTROL_INVENTORY_FIRST_6,
269     &game.panel.inventory_first_6,
270     TYPE_ELEMENT,
271   },
272   {
273     GAME_CONTROL_INVENTORY_FIRST_7,
274     &game.panel.inventory_first_7,
275     TYPE_ELEMENT,
276   },
277   {
278     GAME_CONTROL_INVENTORY_FIRST_8,
279     &game.panel.inventory_first_8,
280     TYPE_ELEMENT,
281   },
282   {
283     GAME_CONTROL_INVENTORY_LAST_1,
284     &game.panel.inventory_last_1,
285     TYPE_ELEMENT,
286   },
287   {
288     GAME_CONTROL_INVENTORY_LAST_2,
289     &game.panel.inventory_last_2,
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_CONTROL_INVENTORY_LAST_3,
294     &game.panel.inventory_last_3,
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_CONTROL_INVENTORY_LAST_4,
299     &game.panel.inventory_last_4,
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_CONTROL_INVENTORY_LAST_5,
304     &game.panel.inventory_last_5,
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_CONTROL_INVENTORY_LAST_6,
309     &game.panel.inventory_last_6,
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_CONTROL_INVENTORY_LAST_7,
314     &game.panel.inventory_last_7,
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_CONTROL_INVENTORY_LAST_8,
319     &game.panel.inventory_last_8,
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_CONTROL_KEY_1,
324     &game.panel.key[0],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_CONTROL_KEY_2,
329     &game.panel.key[1],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_CONTROL_KEY_3,
334     &game.panel.key[2],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_CONTROL_KEY_4,
339     &game.panel.key[3],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_CONTROL_KEY_5,
344     &game.panel.key[4],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_CONTROL_KEY_6,
349     &game.panel.key[5],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_CONTROL_KEY_7,
354     &game.panel.key[6],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_CONTROL_KEY_8,
359     &game.panel.key[7],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_CONTROL_KEY_WHITE,
364     &game.panel.key_white,
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_CONTROL_KEY_WHITE_COUNT,
369     &game.panel.key_white_count,
370     TYPE_INTEGER,
371   },
372   {
373     GAME_CONTROL_SCORE,
374     &game.panel.score,
375     TYPE_INTEGER,
376   },
377   {
378     GAME_CONTROL_TIME,
379     &game.panel.time,
380     TYPE_INTEGER,
381   },
382   {
383     GAME_CONTROL_TIME_HH,
384     &game.panel.time_hh,
385     TYPE_INTEGER,
386   },
387   {
388     GAME_CONTROL_TIME_MM,
389     &game.panel.time_mm,
390     TYPE_INTEGER,
391   },
392   {
393     GAME_CONTROL_TIME_SS,
394     &game.panel.time_ss,
395     TYPE_INTEGER,
396   },
397   {
398     GAME_CONTROL_SHIELD_NORMAL,
399     &game.panel.shield_normal,
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_CONTROL_SHIELD_NORMAL_TIME,
404     &game.panel.shield_normal_time,
405     TYPE_INTEGER,
406   },
407   {
408     GAME_CONTROL_SHIELD_DEADLY,
409     &game.panel.shield_deadly,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_CONTROL_SHIELD_DEADLY_TIME,
414     &game.panel.shield_deadly_time,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_CONTROL_EXIT,
419     &game.panel.exit,
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_CONTROL_EM_EXIT,
424     &game.panel.em_exit,
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_CONTROL_SP_EXIT,
429     &game.panel.sp_exit,
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_CONTROL_STEEL_EXIT,
434     &game.panel.steel_exit,
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_CONTROL_EM_STEEL_EXIT,
439     &game.panel.em_steel_exit,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_CONTROL_EMC_MAGIC_BALL,
444     &game.panel.emc_magic_ball,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
449     &game.panel.emc_magic_ball_switch,
450     TYPE_ELEMENT,
451   },
452   {
453     GAME_CONTROL_LIGHT_SWITCH,
454     &game.panel.light_switch,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_CONTROL_LIGHT_SWITCH_TIME,
459     &game.panel.light_switch_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_CONTROL_TIMEGATE_SWITCH,
464     &game.panel.timegate_switch,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
469     &game.panel.timegate_switch_time,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_CONTROL_SWITCHGATE_SWITCH,
474     &game.panel.switchgate_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_CONTROL_EMC_LENSES,
479     &game.panel.emc_lenses,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_CONTROL_EMC_LENSES_TIME,
484     &game.panel.emc_lenses_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_CONTROL_EMC_MAGNIFIER,
489     &game.panel.emc_magnifier,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_CONTROL_EMC_MAGNIFIER_TIME,
494     &game.panel.emc_magnifier_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_CONTROL_BALLOON_SWITCH,
499     &game.panel.balloon_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_CONTROL_DYNABOMB_NUMBER,
504     &game.panel.dynabomb_number,
505     TYPE_INTEGER,
506   },
507   {
508     GAME_CONTROL_DYNABOMB_SIZE,
509     &game.panel.dynabomb_size,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_CONTROL_DYNABOMB_POWER,
514     &game.panel.dynabomb_power,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_CONTROL_PENGUINS,
519     &game.panel.penguins,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_CONTROL_SOKOBAN_OBJECTS,
524     &game.panel.sokoban_objects,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_CONTROL_SOKOBAN_FIELDS,
529     &game.panel.sokoban_fields,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_CONTROL_ROBOT_WHEEL,
534     &game.panel.robot_wheel,
535     TYPE_ELEMENT,
536   },
537   {
538     GAME_CONTROL_CONVEYOR_BELT_1,
539     &game.panel.conveyor_belt_1,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
544     &game.panel.conveyor_belt_1_switch,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_CONTROL_CONVEYOR_BELT_2,
549     &game.panel.conveyor_belt_2,
550     TYPE_ELEMENT,
551   },
552   {
553     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
554     &game.panel.conveyor_belt_2_switch,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_CONTROL_CONVEYOR_BELT_3,
559     &game.panel.conveyor_belt_3,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
564     &game.panel.conveyor_belt_3_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_CONTROL_CONVEYOR_BELT_4,
569     &game.panel.conveyor_belt_4,
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
574     &game.panel.conveyor_belt_4_switch,
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_CONTROL_MAGIC_WALL,
579     &game.panel.magic_wall,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_CONTROL_MAGIC_WALL_TIME,
584     &game.panel.magic_wall_time,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_CONTROL_BD_MAGIC_WALL,
589     &game.panel.bd_magic_wall,
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_CONTROL_DC_MAGIC_WALL,
594     &game.panel.dc_magic_wall,
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_CONTROL_PLAYER_NAME,
599     &game.panel.player_name,
600     TYPE_STRING,
601   },
602   {
603     GAME_CONTROL_LEVEL_NAME,
604     &game.panel.level_name,
605     TYPE_STRING,
606   },
607   {
608     GAME_CONTROL_LEVEL_AUTHOR,
609     &game.panel.level_author,
610     TYPE_STRING,
611   },
612
613   {
614     -1,
615     NULL,
616     -1,
617   }
618 };
619 #endif
620
621
622 /* values for delayed check of falling and moving elements and for collision */
623 #define CHECK_DELAY_MOVING      3
624 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
625 #define CHECK_DELAY_COLLISION   2
626 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
627
628 /* values for initial player move delay (initial delay counter value) */
629 #define INITIAL_MOVE_DELAY_OFF  -1
630 #define INITIAL_MOVE_DELAY_ON   0
631
632 /* values for player movement speed (which is in fact a delay value) */
633 #define MOVE_DELAY_MIN_SPEED    32
634 #define MOVE_DELAY_NORMAL_SPEED 8
635 #define MOVE_DELAY_HIGH_SPEED   4
636 #define MOVE_DELAY_MAX_SPEED    1
637
638 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
639 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
640
641 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
642 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
643
644 /* values for other actions */
645 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
646 #define MOVE_STEPSIZE_MIN       (1)
647 #define MOVE_STEPSIZE_MAX       (TILEX)
648
649 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
650 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
651
652 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
653
654 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
655                                  RND(element_info[e].push_delay_random))
656 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
657                                  RND(element_info[e].drop_delay_random))
658 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
659                                  RND(element_info[e].move_delay_random))
660 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
661                                     (element_info[e].move_delay_random))
662 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
663                                  RND(element_info[e].ce_value_random_initial))
664 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
665 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
666                                  RND((c)->delay_random * (c)->delay_frames))
667 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
668                                  RND((c)->delay_random))
669
670
671 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
672          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
673
674 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
675         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
676          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
677          (be) + (e) - EL_SELF)
678
679 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
680         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
681          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
682          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
683          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
684          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
685          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
686          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
687          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
688          (e))
689
690 #define CAN_GROW_INTO(e)                                                \
691         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
692
693 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
694                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
695                                         (condition)))
696
697 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
698                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
699                                         (CAN_MOVE_INTO_ACID(e) &&       \
700                                          Feld[x][y] == EL_ACID) ||      \
701                                         (condition)))
702
703 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
704                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
705                                         (CAN_MOVE_INTO_ACID(e) &&       \
706                                          Feld[x][y] == EL_ACID) ||      \
707                                         (condition)))
708
709 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
710                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
711                                         (condition) ||                  \
712                                         (CAN_MOVE_INTO_ACID(e) &&       \
713                                          Feld[x][y] == EL_ACID) ||      \
714                                         (DONT_COLLIDE_WITH(e) &&        \
715                                          IS_PLAYER(x, y) &&             \
716                                          !PLAYER_ENEMY_PROTECTED(x, y))))
717
718 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
719         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
720
721 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
722         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
723
724 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
725         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
726
727 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
728         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
729                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
730
731 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
732         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
733
734 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
735         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
736
737 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
738         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
739
740 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
741         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
742
743 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
744         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
745
746 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
747         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
748                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
749                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
750                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
751                                                  IS_FOOD_PENGUIN(Feld[x][y])))
752 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
753         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
754
755 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
756         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
757
758 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
759         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
760
761 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
762         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
763                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
764
765 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
766
767 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
768                 (!IS_PLAYER(x, y) &&                                    \
769                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
770
771 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
772         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
773
774 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
775 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
776
777 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
778 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
779 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
780 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
781
782 /* game button identifiers */
783 #define GAME_CTRL_ID_STOP               0
784 #define GAME_CTRL_ID_PAUSE              1
785 #define GAME_CTRL_ID_PLAY               2
786 #define SOUND_CTRL_ID_MUSIC             3
787 #define SOUND_CTRL_ID_LOOPS             4
788 #define SOUND_CTRL_ID_SIMPLE            5
789
790 #define NUM_GAME_BUTTONS                6
791
792
793 /* forward declaration for internal use */
794
795 static void CreateField(int, int, int);
796
797 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
798 static void AdvanceFrameAndPlayerCounters(int);
799
800 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
801 static boolean MovePlayer(struct PlayerInfo *, int, int);
802 static void ScrollPlayer(struct PlayerInfo *, int);
803 static void ScrollScreen(struct PlayerInfo *, int);
804
805 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
806
807 static void InitBeltMovement(void);
808 static void CloseAllOpenTimegates(void);
809 static void CheckGravityMovement(struct PlayerInfo *);
810 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
811 static void KillPlayerUnlessEnemyProtected(int, int);
812 static void KillPlayerUnlessExplosionProtected(int, int);
813
814 static void TestIfPlayerTouchesCustomElement(int, int);
815 static void TestIfElementTouchesCustomElement(int, int);
816 static void TestIfElementHitsCustomElement(int, int, int);
817 #if 0
818 static void TestIfElementSmashesCustomElement(int, int, int);
819 #endif
820
821 static void HandleElementChange(int, int, int);
822 static void ExecuteCustomElementAction(int, int, int, int);
823 static boolean ChangeElement(int, int, int, int);
824
825 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
826 #define CheckTriggeredElementChange(x, y, e, ev)                        \
827         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
828 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
829         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
830 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
831         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
832 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
833         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
834
835 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
836 #define CheckElementChange(x, y, e, te, ev)                             \
837         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
838 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
839         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
840 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
841         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
842
843 static void PlayLevelSound(int, int, int);
844 static void PlayLevelSoundNearest(int, int, int);
845 static void PlayLevelSoundAction(int, int, int);
846 static void PlayLevelSoundElementAction(int, int, int, int);
847 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
848 static void PlayLevelSoundActionIfLoop(int, int, int);
849 static void StopLevelSoundActionIfLoop(int, int, int);
850 static void PlayLevelMusic();
851
852 static void MapGameButtons();
853 static void HandleGameButtons(struct GadgetInfo *);
854
855 int AmoebeNachbarNr(int, int);
856 void AmoebeUmwandeln(int, int);
857 void ContinueMoving(int, int);
858 void Bang(int, int);
859 void InitMovDir(int, int);
860 void InitAmoebaNr(int, int);
861 int NewHiScore(void);
862
863 void TestIfGoodThingHitsBadThing(int, int, int);
864 void TestIfBadThingHitsGoodThing(int, int, int);
865 void TestIfPlayerTouchesBadThing(int, int);
866 void TestIfPlayerRunsIntoBadThing(int, int, int);
867 void TestIfBadThingTouchesPlayer(int, int);
868 void TestIfBadThingRunsIntoPlayer(int, int, int);
869 void TestIfFriendTouchesBadThing(int, int);
870 void TestIfBadThingTouchesFriend(int, int);
871 void TestIfBadThingTouchesOtherBadThing(int, int);
872
873 void KillPlayer(struct PlayerInfo *);
874 void BuryPlayer(struct PlayerInfo *);
875 void RemovePlayer(struct PlayerInfo *);
876
877 boolean SnapField(struct PlayerInfo *, int, int);
878 boolean DropElement(struct PlayerInfo *);
879
880 static int getInvisibleActiveFromInvisibleElement(int);
881 static int getInvisibleFromInvisibleActiveElement(int);
882
883 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
884
885 /* for detection of endless loops, caused by custom element programming */
886 /* (using maximal playfield width x 10 is just a rough approximation) */
887 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
888
889 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
890 {                                                                       \
891   if (recursion_loop_detected)                                          \
892     return (rc);                                                        \
893                                                                         \
894   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
895   {                                                                     \
896     recursion_loop_detected = TRUE;                                     \
897     recursion_loop_element = (e);                                       \
898   }                                                                     \
899                                                                         \
900   recursion_loop_depth++;                                               \
901 }
902
903 #define RECURSION_LOOP_DETECTION_END()                                  \
904 {                                                                       \
905   recursion_loop_depth--;                                               \
906 }
907
908 static int recursion_loop_depth;
909 static boolean recursion_loop_detected;
910 static boolean recursion_loop_element;
911
912
913 /* ------------------------------------------------------------------------- */
914 /* definition of elements that automatically change to other elements after  */
915 /* a specified time, eventually calling a function when changing             */
916 /* ------------------------------------------------------------------------- */
917
918 /* forward declaration for changer functions */
919 static void InitBuggyBase(int, int);
920 static void WarnBuggyBase(int, int);
921
922 static void InitTrap(int, int);
923 static void ActivateTrap(int, int);
924 static void ChangeActiveTrap(int, int);
925
926 static void InitRobotWheel(int, int);
927 static void RunRobotWheel(int, int);
928 static void StopRobotWheel(int, int);
929
930 static void InitTimegateWheel(int, int);
931 static void RunTimegateWheel(int, int);
932
933 static void InitMagicBallDelay(int, int);
934 static void ActivateMagicBall(int, int);
935
936 struct ChangingElementInfo
937 {
938   int element;
939   int target_element;
940   int change_delay;
941   void (*pre_change_function)(int x, int y);
942   void (*change_function)(int x, int y);
943   void (*post_change_function)(int x, int y);
944 };
945
946 static struct ChangingElementInfo change_delay_list[] =
947 {
948   {
949     EL_NUT_BREAKING,
950     EL_EMERALD,
951     6,
952     NULL,
953     NULL,
954     NULL
955   },
956   {
957     EL_PEARL_BREAKING,
958     EL_EMPTY,
959     8,
960     NULL,
961     NULL,
962     NULL
963   },
964   {
965     EL_EXIT_OPENING,
966     EL_EXIT_OPEN,
967     29,
968     NULL,
969     NULL,
970     NULL
971   },
972   {
973     EL_EXIT_CLOSING,
974     EL_EXIT_CLOSED,
975     29,
976     NULL,
977     NULL,
978     NULL
979   },
980   {
981     EL_STEEL_EXIT_OPENING,
982     EL_STEEL_EXIT_OPEN,
983     29,
984     NULL,
985     NULL,
986     NULL
987   },
988   {
989     EL_STEEL_EXIT_CLOSING,
990     EL_STEEL_EXIT_CLOSED,
991     29,
992     NULL,
993     NULL,
994     NULL
995   },
996   {
997     EL_EM_EXIT_OPENING,
998     EL_EM_EXIT_OPEN,
999     29,
1000     NULL,
1001     NULL,
1002     NULL
1003   },
1004   {
1005     EL_EM_EXIT_CLOSING,
1006 #if 1
1007     EL_EMPTY,
1008 #else
1009     EL_EM_EXIT_CLOSED,
1010 #endif
1011     29,
1012     NULL,
1013     NULL,
1014     NULL
1015   },
1016   {
1017     EL_EM_STEEL_EXIT_OPENING,
1018     EL_EM_STEEL_EXIT_OPEN,
1019     29,
1020     NULL,
1021     NULL,
1022     NULL
1023   },
1024   {
1025     EL_EM_STEEL_EXIT_CLOSING,
1026 #if 1
1027     EL_STEELWALL,
1028 #else
1029     EL_EM_STEEL_EXIT_CLOSED,
1030 #endif
1031     29,
1032     NULL,
1033     NULL,
1034     NULL
1035   },
1036   {
1037     EL_SP_EXIT_OPENING,
1038     EL_SP_EXIT_OPEN,
1039     29,
1040     NULL,
1041     NULL,
1042     NULL
1043   },
1044   {
1045     EL_SP_EXIT_CLOSING,
1046     EL_SP_EXIT_CLOSED,
1047     29,
1048     NULL,
1049     NULL,
1050     NULL
1051   },
1052   {
1053     EL_SWITCHGATE_OPENING,
1054     EL_SWITCHGATE_OPEN,
1055     29,
1056     NULL,
1057     NULL,
1058     NULL
1059   },
1060   {
1061     EL_SWITCHGATE_CLOSING,
1062     EL_SWITCHGATE_CLOSED,
1063     29,
1064     NULL,
1065     NULL,
1066     NULL
1067   },
1068   {
1069     EL_TIMEGATE_OPENING,
1070     EL_TIMEGATE_OPEN,
1071     29,
1072     NULL,
1073     NULL,
1074     NULL
1075   },
1076   {
1077     EL_TIMEGATE_CLOSING,
1078     EL_TIMEGATE_CLOSED,
1079     29,
1080     NULL,
1081     NULL,
1082     NULL
1083   },
1084
1085   {
1086     EL_ACID_SPLASH_LEFT,
1087     EL_EMPTY,
1088     8,
1089     NULL,
1090     NULL,
1091     NULL
1092   },
1093   {
1094     EL_ACID_SPLASH_RIGHT,
1095     EL_EMPTY,
1096     8,
1097     NULL,
1098     NULL,
1099     NULL
1100   },
1101   {
1102     EL_SP_BUGGY_BASE,
1103     EL_SP_BUGGY_BASE_ACTIVATING,
1104     0,
1105     InitBuggyBase,
1106     NULL,
1107     NULL
1108   },
1109   {
1110     EL_SP_BUGGY_BASE_ACTIVATING,
1111     EL_SP_BUGGY_BASE_ACTIVE,
1112     0,
1113     InitBuggyBase,
1114     NULL,
1115     NULL
1116   },
1117   {
1118     EL_SP_BUGGY_BASE_ACTIVE,
1119     EL_SP_BUGGY_BASE,
1120     0,
1121     InitBuggyBase,
1122     WarnBuggyBase,
1123     NULL
1124   },
1125   {
1126     EL_TRAP,
1127     EL_TRAP_ACTIVE,
1128     0,
1129     InitTrap,
1130     NULL,
1131     ActivateTrap
1132   },
1133   {
1134     EL_TRAP_ACTIVE,
1135     EL_TRAP,
1136     31,
1137     NULL,
1138     ChangeActiveTrap,
1139     NULL
1140   },
1141   {
1142     EL_ROBOT_WHEEL_ACTIVE,
1143     EL_ROBOT_WHEEL,
1144     0,
1145     InitRobotWheel,
1146     RunRobotWheel,
1147     StopRobotWheel
1148   },
1149   {
1150     EL_TIMEGATE_SWITCH_ACTIVE,
1151     EL_TIMEGATE_SWITCH,
1152     0,
1153     InitTimegateWheel,
1154     RunTimegateWheel,
1155     NULL
1156   },
1157   {
1158     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1159     EL_DC_TIMEGATE_SWITCH,
1160     0,
1161     InitTimegateWheel,
1162     RunTimegateWheel,
1163     NULL
1164   },
1165   {
1166     EL_EMC_MAGIC_BALL_ACTIVE,
1167     EL_EMC_MAGIC_BALL_ACTIVE,
1168     0,
1169     InitMagicBallDelay,
1170     NULL,
1171     ActivateMagicBall
1172   },
1173   {
1174     EL_EMC_SPRING_BUMPER_ACTIVE,
1175     EL_EMC_SPRING_BUMPER,
1176     8,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_DIAGONAL_SHRINKING,
1183     EL_UNDEFINED,
1184     0,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_DIAGONAL_GROWING,
1191     EL_UNDEFINED,
1192     0,
1193     NULL,
1194     NULL,
1195     NULL,
1196   },
1197
1198   {
1199     EL_UNDEFINED,
1200     EL_UNDEFINED,
1201     -1,
1202     NULL,
1203     NULL,
1204     NULL
1205   }
1206 };
1207
1208 struct
1209 {
1210   int element;
1211   int push_delay_fixed, push_delay_random;
1212 }
1213 push_delay_list[] =
1214 {
1215   { EL_SPRING,                  0, 0 },
1216   { EL_BALLOON,                 0, 0 },
1217
1218   { EL_SOKOBAN_OBJECT,          2, 0 },
1219   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1220   { EL_SATELLITE,               2, 0 },
1221   { EL_SP_DISK_YELLOW,          2, 0 },
1222
1223   { EL_UNDEFINED,               0, 0 },
1224 };
1225
1226 struct
1227 {
1228   int element;
1229   int move_stepsize;
1230 }
1231 move_stepsize_list[] =
1232 {
1233   { EL_AMOEBA_DROP,             2 },
1234   { EL_AMOEBA_DROPPING,         2 },
1235   { EL_QUICKSAND_FILLING,       1 },
1236   { EL_QUICKSAND_EMPTYING,      1 },
1237   { EL_QUICKSAND_FAST_FILLING,  2 },
1238   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1239   { EL_MAGIC_WALL_FILLING,      2 },
1240   { EL_MAGIC_WALL_EMPTYING,     2 },
1241   { EL_BD_MAGIC_WALL_FILLING,   2 },
1242   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1243   { EL_DC_MAGIC_WALL_FILLING,   2 },
1244   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1245
1246   { EL_UNDEFINED,               0 },
1247 };
1248
1249 struct
1250 {
1251   int element;
1252   int count;
1253 }
1254 collect_count_list[] =
1255 {
1256   { EL_EMERALD,                 1 },
1257   { EL_BD_DIAMOND,              1 },
1258   { EL_EMERALD_YELLOW,          1 },
1259   { EL_EMERALD_RED,             1 },
1260   { EL_EMERALD_PURPLE,          1 },
1261   { EL_DIAMOND,                 3 },
1262   { EL_SP_INFOTRON,             1 },
1263   { EL_PEARL,                   5 },
1264   { EL_CRYSTAL,                 8 },
1265
1266   { EL_UNDEFINED,               0 },
1267 };
1268
1269 struct
1270 {
1271   int element;
1272   int direction;
1273 }
1274 access_direction_list[] =
1275 {
1276   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1277   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1278   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1279   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1280   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1281   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1282   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1283   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1284   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1285   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1286   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1287
1288   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1289   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1290   { EL_SP_PORT_UP,                                                   MV_DOWN },
1291   { EL_SP_PORT_DOWN,                                         MV_UP           },
1292   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1293   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1294   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1295   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1296   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1297   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1298   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1299   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1300   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1301   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1302   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1303   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1304   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1305   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1306   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1307
1308   { EL_UNDEFINED,                       MV_NONE                              }
1309 };
1310
1311 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1312
1313 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1314 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1315 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1316                                  IS_JUST_CHANGING(x, y))
1317
1318 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1319
1320 /* static variables for playfield scan mode (scanning forward or backward) */
1321 static int playfield_scan_start_x = 0;
1322 static int playfield_scan_start_y = 0;
1323 static int playfield_scan_delta_x = 1;
1324 static int playfield_scan_delta_y = 1;
1325
1326 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1327                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1328                                      (y) += playfield_scan_delta_y)     \
1329                                 for ((x) = playfield_scan_start_x;      \
1330                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1331                                      (x) += playfield_scan_delta_x)
1332
1333 #ifdef DEBUG
1334 void DEBUG_SetMaximumDynamite()
1335 {
1336   int i;
1337
1338   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1339     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1340       local_player->inventory_element[local_player->inventory_size++] =
1341         EL_DYNAMITE;
1342 }
1343 #endif
1344
1345 static void InitPlayfieldScanModeVars()
1346 {
1347   if (game.use_reverse_scan_direction)
1348   {
1349     playfield_scan_start_x = lev_fieldx - 1;
1350     playfield_scan_start_y = lev_fieldy - 1;
1351
1352     playfield_scan_delta_x = -1;
1353     playfield_scan_delta_y = -1;
1354   }
1355   else
1356   {
1357     playfield_scan_start_x = 0;
1358     playfield_scan_start_y = 0;
1359
1360     playfield_scan_delta_x = 1;
1361     playfield_scan_delta_y = 1;
1362   }
1363 }
1364
1365 static void InitPlayfieldScanMode(int mode)
1366 {
1367   game.use_reverse_scan_direction =
1368     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1369
1370   InitPlayfieldScanModeVars();
1371 }
1372
1373 static int get_move_delay_from_stepsize(int move_stepsize)
1374 {
1375   move_stepsize =
1376     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1377
1378   /* make sure that stepsize value is always a power of 2 */
1379   move_stepsize = (1 << log_2(move_stepsize));
1380
1381   return TILEX / move_stepsize;
1382 }
1383
1384 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1385                                boolean init_game)
1386 {
1387   int player_nr = player->index_nr;
1388   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1389   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1390
1391   /* do no immediately change move delay -- the player might just be moving */
1392   player->move_delay_value_next = move_delay;
1393
1394   /* information if player can move must be set separately */
1395   player->cannot_move = cannot_move;
1396
1397   if (init_game)
1398   {
1399     player->move_delay       = game.initial_move_delay[player_nr];
1400     player->move_delay_value = game.initial_move_delay_value[player_nr];
1401
1402     player->move_delay_value_next = -1;
1403
1404     player->move_delay_reset_counter = 0;
1405   }
1406 }
1407
1408 void GetPlayerConfig()
1409 {
1410   GameFrameDelay = setup.game_frame_delay;
1411
1412   if (!audio.sound_available)
1413     setup.sound_simple = FALSE;
1414
1415   if (!audio.loops_available)
1416     setup.sound_loops = FALSE;
1417
1418   if (!audio.music_available)
1419     setup.sound_music = FALSE;
1420
1421   if (!video.fullscreen_available)
1422     setup.fullscreen = FALSE;
1423
1424   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1425
1426   SetAudioMode(setup.sound);
1427   InitJoysticks();
1428 }
1429
1430 int GetElementFromGroupElement(int element)
1431 {
1432   if (IS_GROUP_ELEMENT(element))
1433   {
1434     struct ElementGroupInfo *group = element_info[element].group;
1435     int last_anim_random_frame = gfx.anim_random_frame;
1436     int element_pos;
1437
1438     if (group->choice_mode == ANIM_RANDOM)
1439       gfx.anim_random_frame = RND(group->num_elements_resolved);
1440
1441     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1442                                     group->choice_mode, 0,
1443                                     group->choice_pos);
1444
1445     if (group->choice_mode == ANIM_RANDOM)
1446       gfx.anim_random_frame = last_anim_random_frame;
1447
1448     group->choice_pos++;
1449
1450     element = group->element_resolved[element_pos];
1451   }
1452
1453   return element;
1454 }
1455
1456 static void InitPlayerField(int x, int y, int element, boolean init_game)
1457 {
1458   if (element == EL_SP_MURPHY)
1459   {
1460     if (init_game)
1461     {
1462       if (stored_player[0].present)
1463       {
1464         Feld[x][y] = EL_SP_MURPHY_CLONE;
1465
1466         return;
1467       }
1468       else
1469       {
1470         stored_player[0].use_murphy = TRUE;
1471
1472         if (!level.use_artwork_element[0])
1473           stored_player[0].artwork_element = EL_SP_MURPHY;
1474       }
1475
1476       Feld[x][y] = EL_PLAYER_1;
1477     }
1478   }
1479
1480   if (init_game)
1481   {
1482     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1483     int jx = player->jx, jy = player->jy;
1484
1485     player->present = TRUE;
1486
1487     player->block_last_field = (element == EL_SP_MURPHY ?
1488                                 level.sp_block_last_field :
1489                                 level.block_last_field);
1490
1491     /* ---------- initialize player's last field block delay --------------- */
1492
1493     /* always start with reliable default value (no adjustment needed) */
1494     player->block_delay_adjustment = 0;
1495
1496     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1497     if (player->block_last_field && element == EL_SP_MURPHY)
1498       player->block_delay_adjustment = 1;
1499
1500     /* special case 2: in game engines before 3.1.1, blocking was different */
1501     if (game.use_block_last_field_bug)
1502       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1503
1504     if (!options.network || player->connected)
1505     {
1506       player->active = TRUE;
1507
1508       /* remove potentially duplicate players */
1509       if (StorePlayer[jx][jy] == Feld[x][y])
1510         StorePlayer[jx][jy] = 0;
1511
1512       StorePlayer[x][y] = Feld[x][y];
1513
1514       if (options.debug)
1515       {
1516         printf("Player %d activated.\n", player->element_nr);
1517         printf("[Local player is %d and currently %s.]\n",
1518                local_player->element_nr,
1519                local_player->active ? "active" : "not active");
1520       }
1521     }
1522
1523     Feld[x][y] = EL_EMPTY;
1524
1525     player->jx = player->last_jx = x;
1526     player->jy = player->last_jy = y;
1527   }
1528 }
1529
1530 static void InitField(int x, int y, boolean init_game)
1531 {
1532   int element = Feld[x][y];
1533
1534   switch (element)
1535   {
1536     case EL_SP_MURPHY:
1537     case EL_PLAYER_1:
1538     case EL_PLAYER_2:
1539     case EL_PLAYER_3:
1540     case EL_PLAYER_4:
1541       InitPlayerField(x, y, element, init_game);
1542       break;
1543
1544     case EL_SOKOBAN_FIELD_PLAYER:
1545       element = Feld[x][y] = EL_PLAYER_1;
1546       InitField(x, y, init_game);
1547
1548       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1549       InitField(x, y, init_game);
1550       break;
1551
1552     case EL_SOKOBAN_FIELD_EMPTY:
1553       local_player->sokobanfields_still_needed++;
1554       break;
1555
1556     case EL_STONEBLOCK:
1557       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1558         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1559       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1560         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1561       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1562         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1563       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1564         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1565       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1566         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1567       break;
1568
1569     case EL_BUG:
1570     case EL_BUG_RIGHT:
1571     case EL_BUG_UP:
1572     case EL_BUG_LEFT:
1573     case EL_BUG_DOWN:
1574     case EL_SPACESHIP:
1575     case EL_SPACESHIP_RIGHT:
1576     case EL_SPACESHIP_UP:
1577     case EL_SPACESHIP_LEFT:
1578     case EL_SPACESHIP_DOWN:
1579     case EL_BD_BUTTERFLY:
1580     case EL_BD_BUTTERFLY_RIGHT:
1581     case EL_BD_BUTTERFLY_UP:
1582     case EL_BD_BUTTERFLY_LEFT:
1583     case EL_BD_BUTTERFLY_DOWN:
1584     case EL_BD_FIREFLY:
1585     case EL_BD_FIREFLY_RIGHT:
1586     case EL_BD_FIREFLY_UP:
1587     case EL_BD_FIREFLY_LEFT:
1588     case EL_BD_FIREFLY_DOWN:
1589     case EL_PACMAN_RIGHT:
1590     case EL_PACMAN_UP:
1591     case EL_PACMAN_LEFT:
1592     case EL_PACMAN_DOWN:
1593     case EL_YAMYAM:
1594     case EL_YAMYAM_LEFT:
1595     case EL_YAMYAM_RIGHT:
1596     case EL_YAMYAM_UP:
1597     case EL_YAMYAM_DOWN:
1598     case EL_DARK_YAMYAM:
1599     case EL_ROBOT:
1600     case EL_PACMAN:
1601     case EL_SP_SNIKSNAK:
1602     case EL_SP_ELECTRON:
1603     case EL_MOLE:
1604     case EL_MOLE_LEFT:
1605     case EL_MOLE_RIGHT:
1606     case EL_MOLE_UP:
1607     case EL_MOLE_DOWN:
1608       InitMovDir(x, y);
1609       break;
1610
1611     case EL_AMOEBA_FULL:
1612     case EL_BD_AMOEBA:
1613       InitAmoebaNr(x, y);
1614       break;
1615
1616     case EL_AMOEBA_DROP:
1617       if (y == lev_fieldy - 1)
1618       {
1619         Feld[x][y] = EL_AMOEBA_GROWING;
1620         Store[x][y] = EL_AMOEBA_WET;
1621       }
1622       break;
1623
1624     case EL_DYNAMITE_ACTIVE:
1625     case EL_SP_DISK_RED_ACTIVE:
1626     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1627     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1628     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1629     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1630       MovDelay[x][y] = 96;
1631       break;
1632
1633     case EL_EM_DYNAMITE_ACTIVE:
1634       MovDelay[x][y] = 32;
1635       break;
1636
1637     case EL_LAMP:
1638       local_player->lights_still_needed++;
1639       break;
1640
1641     case EL_PENGUIN:
1642       local_player->friends_still_needed++;
1643       break;
1644
1645     case EL_PIG:
1646     case EL_DRAGON:
1647       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1648       break;
1649
1650     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1651     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1652     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1653     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1654     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1655     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1656     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1657     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1658     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1659     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1660     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1661     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1662       if (init_game)
1663       {
1664         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1665         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1666         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1667
1668         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1669         {
1670           game.belt_dir[belt_nr] = belt_dir;
1671           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1672         }
1673         else    /* more than one switch -- set it like the first switch */
1674         {
1675           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1676         }
1677       }
1678       break;
1679
1680 #if !USE_BOTH_SWITCHGATE_SWITCHES
1681     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1682       if (init_game)
1683         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1684       break;
1685
1686     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1687       if (init_game)
1688         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1689       break;
1690 #endif
1691
1692     case EL_LIGHT_SWITCH_ACTIVE:
1693       if (init_game)
1694         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1695       break;
1696
1697     case EL_INVISIBLE_STEELWALL:
1698     case EL_INVISIBLE_WALL:
1699     case EL_INVISIBLE_SAND:
1700       if (game.light_time_left > 0 ||
1701           game.lenses_time_left > 0)
1702         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1703       break;
1704
1705     case EL_EMC_MAGIC_BALL:
1706       if (game.ball_state)
1707         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1708       break;
1709
1710     case EL_EMC_MAGIC_BALL_SWITCH:
1711       if (game.ball_state)
1712         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1713       break;
1714
1715     default:
1716       if (IS_CUSTOM_ELEMENT(element))
1717       {
1718         if (CAN_MOVE(element))
1719           InitMovDir(x, y);
1720
1721 #if USE_NEW_CUSTOM_VALUE
1722         if (!element_info[element].use_last_ce_value || init_game)
1723           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1724 #endif
1725       }
1726       else if (IS_GROUP_ELEMENT(element))
1727       {
1728         Feld[x][y] = GetElementFromGroupElement(element);
1729
1730         InitField(x, y, init_game);
1731       }
1732
1733       break;
1734   }
1735
1736   if (!init_game)
1737     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1738 }
1739
1740 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1741 {
1742   InitField(x, y, init_game);
1743
1744   /* not needed to call InitMovDir() -- already done by InitField()! */
1745   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1746       CAN_MOVE(Feld[x][y]))
1747     InitMovDir(x, y);
1748 }
1749
1750 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1751 {
1752   int old_element = Feld[x][y];
1753
1754   InitField(x, y, init_game);
1755
1756   /* not needed to call InitMovDir() -- already done by InitField()! */
1757   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1758       CAN_MOVE(old_element) &&
1759       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1760     InitMovDir(x, y);
1761
1762   /* this case is in fact a combination of not less than three bugs:
1763      first, it calls InitMovDir() for elements that can move, although this is
1764      already done by InitField(); then, it checks the element that was at this
1765      field _before_ the call to InitField() (which can change it); lastly, it
1766      was not called for "mole with direction" elements, which were treated as
1767      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1768   */
1769 }
1770
1771 #if 1
1772
1773 static int get_key_element_from_nr(int key_nr)
1774 {
1775   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1776                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1777                           EL_EM_KEY_1 : EL_KEY_1);
1778
1779   return key_base_element + key_nr;
1780 }
1781
1782 static int get_next_drop_element(struct PlayerInfo *player)
1783 {
1784   return (player->inventory_size > 0 ?
1785           player->inventory_element[player->inventory_size - 1] :
1786           player->inventory_infinite_element != EL_UNDEFINED ?
1787           player->inventory_infinite_element :
1788           player->dynabombs_left > 0 ?
1789           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1790           EL_UNDEFINED);
1791 }
1792
1793 static int get_drop_element_from_pos(struct PlayerInfo *player, int pos)
1794 {
1795   /* pos >= 0: get element from bottom of the stack;
1796      pos <  0: get element from top of the stack */
1797
1798   if (pos < 0)
1799   {
1800     int min_inventory_size = -pos;
1801     int inventory_pos = player->inventory_size - min_inventory_size;
1802     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1803
1804     return (player->inventory_size >= min_inventory_size ?
1805             player->inventory_element[inventory_pos] :
1806             player->inventory_infinite_element != EL_UNDEFINED ?
1807             player->inventory_infinite_element :
1808             player->dynabombs_left >= min_dynabombs_left ?
1809             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1810             EL_UNDEFINED);
1811   }
1812   else
1813   {
1814     int min_dynabombs_left = pos + 1;
1815     int min_inventory_size = pos + 1 - player->dynabombs_left;
1816     int inventory_pos = pos - player->dynabombs_left;
1817
1818     return (player->inventory_infinite_element != EL_UNDEFINED ?
1819             player->inventory_infinite_element :
1820             player->dynabombs_left >= min_dynabombs_left ?
1821             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1822             player->inventory_size >= min_inventory_size ?
1823             player->inventory_element[inventory_pos] :
1824             EL_UNDEFINED);
1825   }
1826 }
1827
1828 void InitGameControlValues()
1829 {
1830   int i;
1831
1832   for (i = 0; game_controls[i].nr != -1; i++)
1833   {
1834     int nr = game_controls[i].nr;
1835     int type = game_controls[i].type;
1836     struct TextPosInfo *pos = game_controls[i].pos;
1837
1838     /* force update of game controls after initialization */
1839     game_control_value[nr] = last_game_control_value[nr] = -1;
1840
1841     /* determine panel value width for later calculation of alignment */
1842     if (type == TYPE_INTEGER || type == TYPE_STRING)
1843     {
1844       pos->width = pos->size * getFontWidth(pos->font);
1845       pos->height = getFontHeight(pos->font);
1846     }
1847     else if (type == TYPE_ELEMENT)
1848     {
1849       pos->width = pos->size;
1850       pos->height = pos->size;
1851     }
1852   }
1853 }
1854
1855 void UpdateGameControlValues()
1856 {
1857   int i, k;
1858   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1859   int score = (local_player->LevelSolved ? local_player->score_final :
1860                local_player->score);
1861
1862   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1863   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1864
1865   game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0;
1866   for (i = 0; i < MAX_NUM_KEYS; i++)
1867     game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1868   game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1869   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1870
1871   if (game.centered_player_nr == -1)
1872   {
1873     for (i = 0; i < MAX_PLAYERS; i++)
1874     {
1875       for (k = 0; k < MAX_NUM_KEYS; k++)
1876         if (stored_player[i].key[k])
1877           game_control_value[GAME_CONTROL_KEY_1 + k] =
1878             get_key_element_from_nr(k);
1879
1880       game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1881         stored_player[i].inventory_size;
1882
1883       if (stored_player[i].num_white_keys > 0)
1884         game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1885
1886       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1887         stored_player[i].num_white_keys;
1888     }
1889   }
1890   else
1891   {
1892     int player_nr = game.centered_player_nr;
1893
1894     for (k = 0; k < MAX_NUM_KEYS; k++)
1895       if (stored_player[player_nr].key[k])
1896         game_control_value[GAME_CONTROL_KEY_1 + k] =
1897           get_key_element_from_nr(k);
1898
1899     game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1900       stored_player[player_nr].inventory_size;
1901
1902     if (stored_player[player_nr].num_white_keys > 0)
1903       game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1904
1905     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1906       stored_player[player_nr].num_white_keys;
1907   }
1908
1909   for (i = 0; i < 8; i++)
1910   {
1911     game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] =
1912       get_drop_element_from_pos(local_player, i);
1913     game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] =
1914       get_drop_element_from_pos(local_player, -i - 1);
1915   }
1916
1917   game_control_value[GAME_CONTROL_SCORE] = score;
1918
1919   game_control_value[GAME_CONTROL_TIME] = time;
1920
1921   game_control_value[GAME_CONTROL_TIME_HH] = time / 3600;
1922   game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60;
1923   game_control_value[GAME_CONTROL_TIME_SS] = time % 60;
1924
1925   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1926     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1927      EL_EMPTY);
1928   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1929     local_player->shield_normal_time_left;
1930   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1931     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1932      EL_EMPTY);
1933   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1934     local_player->shield_deadly_time_left;
1935
1936   if (local_player->gems_still_needed > 0 ||
1937       local_player->sokobanfields_still_needed > 0 ||
1938       local_player->lights_still_needed > 0)
1939   {
1940     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
1941     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
1942     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
1943     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
1944     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1945   }
1946   else
1947   {
1948     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
1949     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
1950     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
1951     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
1952     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1953   }
1954
1955   /* !!! TODO !!! */
1956   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1957   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1958
1959   game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1960     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1961   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1962
1963   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1964     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1965   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1966     game.timegate_time_left;
1967
1968   /* !!! TODO !!! */
1969   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1970
1971   game_control_value[GAME_CONTROL_EMC_LENSES] =
1972     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1973   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1974
1975   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1976     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1977   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1978
1979   game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1980     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1981      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1982      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1983      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1984      EL_BALLOON_SWITCH_NONE);
1985
1986   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1987     local_player->dynabomb_count;
1988   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1989     local_player->dynabomb_size;
1990   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1991     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1992
1993   game_control_value[GAME_CONTROL_PENGUINS] =
1994     local_player->friends_still_needed;
1995
1996   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1997     local_player->sokobanfields_still_needed;
1998   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1999     local_player->sokobanfields_still_needed;
2000
2001   /* !!! TODO !!! */
2002   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
2003
2004   /* !!! TODO !!! */
2005   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
2006   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
2007   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
2008   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
2009   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
2010   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
2011   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
2012   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
2013
2014   /* !!! TODO !!! */
2015   game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
2016   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
2017     game.magic_wall_time_left;
2018   game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
2019   game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
2020
2021   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
2022   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
2023   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
2024 }
2025
2026 void DisplayGameControlValues()
2027 {
2028   int i;
2029
2030   for (i = 0; game_controls[i].nr != -1; i++)
2031   {
2032     int nr = game_controls[i].nr;
2033     int type = game_controls[i].type;
2034     struct TextPosInfo *pos = game_controls[i].pos;
2035     int value = game_control_value[nr];
2036     int last_value = last_game_control_value[nr];
2037     int size = pos->size;
2038     int font = pos->font;
2039
2040     if (value == last_value)
2041       continue;
2042
2043     last_game_control_value[nr] = value;
2044
2045 #if 0
2046     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2047 #endif
2048
2049     if (PANEL_DEACTIVATED(pos))
2050       continue;
2051
2052     if (type == TYPE_INTEGER)
2053     {
2054       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2055       {
2056         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2057
2058         if (use_dynamic_size)           /* use dynamic number of digits */
2059         {
2060           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2061           int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2062           int size2 = size1 + 1;
2063           int font1 = pos->font;
2064           int font2 = pos->font_alt;
2065
2066           size = (value < value_change ? size1 : size2);
2067           font = (value < value_change ? font1 : font2);
2068
2069           /* clear background if value just changed its size (dynamic digits) */
2070           if ((last_value < value_change) != (value < value_change))
2071           {
2072             int width1 = size1 * getFontWidth(font1);
2073             int width2 = size2 * getFontWidth(font2);
2074             int max_width = MAX(width1, width2);
2075             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2076
2077             pos->width = max_width;
2078
2079             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2080                                        max_width, max_height);
2081           }
2082         }
2083
2084         pos->width = size * getFontWidth(font);
2085       }
2086
2087       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2088     }
2089     else if (type == TYPE_ELEMENT)
2090     {
2091       int dst_x = PANEL_XPOS(pos);
2092       int dst_y = PANEL_YPOS(pos);
2093
2094       if (value == EL_UNDEFINED || value == EL_EMPTY)
2095       {
2096         int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2097         int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2098
2099         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2100                    size, size, dst_x, dst_y);
2101       }
2102       else
2103       {
2104         int graphic = el2doorimg(value);
2105
2106         DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2107       }
2108     }
2109     else if (type == TYPE_STRING)
2110     {
2111       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
2112                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
2113                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2114
2115       if (s != NULL)
2116       {
2117         char *s_cut = getStringCopyN(s, size);
2118
2119         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2120
2121         free(s_cut);
2122       }
2123     }
2124
2125     redraw_mask |= REDRAW_DOOR_1;
2126   }
2127 }
2128
2129 void DrawGameValue_Emeralds(int value)
2130 {
2131   struct TextPosInfo *pos = &game.panel.gems;
2132 #if 1
2133   int font_nr = pos->font;
2134 #else
2135   int font_nr = FONT_TEXT_2;
2136 #endif
2137   int font_width = getFontWidth(font_nr);
2138   int chars = pos->size;
2139
2140 #if 1
2141   return;       /* !!! USE NEW STUFF !!! */
2142 #endif
2143
2144   if (PANEL_DEACTIVATED(pos))
2145     return;
2146
2147   pos->width = chars * font_width;
2148
2149   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2150 }
2151
2152 void DrawGameValue_Dynamite(int value)
2153 {
2154   struct TextPosInfo *pos = &game.panel.inventory_count;
2155 #if 1
2156   int font_nr = pos->font;
2157 #else
2158   int font_nr = FONT_TEXT_2;
2159 #endif
2160   int font_width = getFontWidth(font_nr);
2161   int chars = pos->size;
2162
2163 #if 1
2164   return;       /* !!! USE NEW STUFF !!! */
2165 #endif
2166
2167   if (PANEL_DEACTIVATED(pos))
2168     return;
2169
2170   pos->width = chars * font_width;
2171
2172   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2173 }
2174
2175 void DrawGameValue_Score(int value)
2176 {
2177   struct TextPosInfo *pos = &game.panel.score;
2178 #if 1
2179   int font_nr = pos->font;
2180 #else
2181   int font_nr = FONT_TEXT_2;
2182 #endif
2183   int font_width = getFontWidth(font_nr);
2184   int chars = pos->size;
2185
2186 #if 1
2187   return;       /* !!! USE NEW STUFF !!! */
2188 #endif
2189
2190   if (PANEL_DEACTIVATED(pos))
2191     return;
2192
2193   pos->width = chars * font_width;
2194
2195   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2196 }
2197
2198 void DrawGameValue_Time(int value)
2199 {
2200   struct TextPosInfo *pos = &game.panel.time;
2201   static int last_value = -1;
2202   int chars1 = 3;
2203   int chars2 = 4;
2204   int chars = pos->size;
2205 #if 1
2206   int font1_nr = pos->font;
2207   int font2_nr = pos->font_alt;
2208 #else
2209   int font1_nr = FONT_TEXT_2;
2210   int font2_nr = FONT_TEXT_1;
2211 #endif
2212   int font_nr = font1_nr;
2213   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2214
2215 #if 1
2216   return;       /* !!! USE NEW STUFF !!! */
2217 #endif
2218
2219   if (PANEL_DEACTIVATED(pos))
2220     return;
2221
2222   if (use_dynamic_chars)                /* use dynamic number of chars */
2223   {
2224     chars   = (value < 1000 ? chars1   : chars2);
2225     font_nr = (value < 1000 ? font1_nr : font2_nr);
2226   }
2227
2228   /* clear background if value just changed its size (dynamic chars only) */
2229   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2230   {
2231     int width1 = chars1 * getFontWidth(font1_nr);
2232     int width2 = chars2 * getFontWidth(font2_nr);
2233     int max_width = MAX(width1, width2);
2234     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2235
2236     pos->width = max_width;
2237
2238     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2239                                max_width, max_height);
2240   }
2241
2242   pos->width = chars * getFontWidth(font_nr);
2243
2244   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2245
2246   last_value = value;
2247 }
2248
2249 void DrawGameValue_Level(int value)
2250 {
2251   struct TextPosInfo *pos = &game.panel.level_number;
2252   int chars1 = 2;
2253   int chars2 = 3;
2254   int chars = pos->size;
2255 #if 1
2256   int font1_nr = pos->font;
2257   int font2_nr = pos->font_alt;
2258 #else
2259   int font1_nr = FONT_TEXT_2;
2260   int font2_nr = FONT_TEXT_1;
2261 #endif
2262   int font_nr = font1_nr;
2263   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2264
2265 #if 1
2266   return;       /* !!! USE NEW STUFF !!! */
2267 #endif
2268
2269   if (PANEL_DEACTIVATED(pos))
2270     return;
2271
2272   if (use_dynamic_chars)                /* use dynamic number of chars */
2273   {
2274     chars   = (level_nr < 100 ? chars1   : chars2);
2275     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2276   }
2277
2278   pos->width = chars * getFontWidth(font_nr);
2279
2280   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2281 }
2282
2283 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2284 {
2285 #if 0
2286   struct TextPosInfo *pos = &game.panel.keys;
2287 #endif
2288 #if 0
2289   int base_key_graphic = EL_KEY_1;
2290 #endif
2291   int i;
2292
2293 #if 1
2294   return;       /* !!! USE NEW STUFF !!! */
2295 #endif
2296
2297 #if 0
2298   if (PANEL_DEACTIVATED(pos))
2299     return;
2300 #endif
2301
2302 #if 0
2303   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2304     base_key_graphic = EL_EM_KEY_1;
2305 #endif
2306
2307 #if 0
2308   pos->width = 4 * MINI_TILEX;
2309 #endif
2310
2311 #if 1
2312   for (i = 0; i < MAX_NUM_KEYS; i++)
2313 #else
2314   /* currently only 4 of 8 possible keys are displayed */
2315   for (i = 0; i < STD_NUM_KEYS; i++)
2316 #endif
2317   {
2318 #if 1
2319     struct TextPosInfo *pos = &game.panel.key[i];
2320 #endif
2321     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2322     int src_y = DOOR_GFX_PAGEY1 + 123;
2323 #if 1
2324     int dst_x = PANEL_XPOS(pos);
2325     int dst_y = PANEL_YPOS(pos);
2326 #else
2327     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2328     int dst_y = PANEL_YPOS(pos);
2329 #endif
2330
2331 #if 1
2332     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2333                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2334                    EL_KEY_1) + i;
2335     int graphic = el2edimg(element);
2336 #endif
2337
2338 #if 1
2339     if (PANEL_DEACTIVATED(pos))
2340       continue;
2341 #endif
2342
2343 #if 0
2344     /* masked blit with tiles from half-size scaled bitmap does not work yet
2345        (no mask bitmap created for these sizes after loading and scaling) --
2346        solution: load without creating mask, scale, then create final mask */
2347
2348     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2349                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2350
2351     if (key[i])
2352     {
2353 #if 0
2354       int graphic = el2edimg(base_key_graphic + i);
2355 #endif
2356       Bitmap *src_bitmap;
2357       int src_x, src_y;
2358
2359       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2360
2361       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2362                     dst_x - src_x, dst_y - src_y);
2363       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2364                        dst_x, dst_y);
2365     }
2366 #else
2367 #if 1
2368     if (key[i])
2369       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2370     else
2371       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2372                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2373 #else
2374     if (key[i])
2375       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2376     else
2377       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2378                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2379 #endif
2380 #endif
2381   }
2382 }
2383
2384 #else
2385
2386 void DrawGameValue_Emeralds(int value)
2387 {
2388   int font_nr = FONT_TEXT_2;
2389   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2390
2391   if (PANEL_DEACTIVATED(game.panel.gems))
2392     return;
2393
2394   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2395 }
2396
2397 void DrawGameValue_Dynamite(int value)
2398 {
2399   int font_nr = FONT_TEXT_2;
2400   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2401
2402   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2403     return;
2404
2405   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2406 }
2407
2408 void DrawGameValue_Score(int value)
2409 {
2410   int font_nr = FONT_TEXT_2;
2411   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2412
2413   if (PANEL_DEACTIVATED(game.panel.score))
2414     return;
2415
2416   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2417 }
2418
2419 void DrawGameValue_Time(int value)
2420 {
2421   int font1_nr = FONT_TEXT_2;
2422 #if 1
2423   int font2_nr = FONT_TEXT_1;
2424 #else
2425   int font2_nr = FONT_LEVEL_NUMBER;
2426 #endif
2427   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2428   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2429
2430   if (PANEL_DEACTIVATED(game.panel.time))
2431     return;
2432
2433   /* clear background if value just changed its size */
2434   if (value == 999 || value == 1000)
2435     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2436
2437   if (value < 1000)
2438     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2439   else
2440     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2441 }
2442
2443 void DrawGameValue_Level(int value)
2444 {
2445   int font1_nr = FONT_TEXT_2;
2446 #if 1
2447   int font2_nr = FONT_TEXT_1;
2448 #else
2449   int font2_nr = FONT_LEVEL_NUMBER;
2450 #endif
2451
2452   if (PANEL_DEACTIVATED(game.panel.level))
2453     return;
2454
2455   if (level_nr < 100)
2456     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2457   else
2458     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2459 }
2460
2461 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2462 {
2463   int base_key_graphic = EL_KEY_1;
2464   int i;
2465
2466   if (PANEL_DEACTIVATED(game.panel.keys))
2467     return;
2468
2469   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2470     base_key_graphic = EL_EM_KEY_1;
2471
2472   /* currently only 4 of 8 possible keys are displayed */
2473   for (i = 0; i < STD_NUM_KEYS; i++)
2474   {
2475     int x = XX_KEYS + i * MINI_TILEX;
2476     int y = YY_KEYS;
2477
2478     if (key[i])
2479       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2480     else
2481       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2482                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2483   }
2484 }
2485
2486 #endif
2487
2488 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2489                        int key_bits)
2490 {
2491   int key[MAX_NUM_KEYS];
2492   int i;
2493
2494   /* prevent EM engine from updating time/score values parallel to GameWon() */
2495   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2496       local_player->LevelSolved)
2497     return;
2498
2499   for (i = 0; i < MAX_NUM_KEYS; i++)
2500     key[i] = key_bits & (1 << i);
2501
2502   DrawGameValue_Level(level_nr);
2503
2504   DrawGameValue_Emeralds(emeralds);
2505   DrawGameValue_Dynamite(dynamite);
2506   DrawGameValue_Score(score);
2507   DrawGameValue_Time(time);
2508
2509   DrawGameValue_Keys(key);
2510 }
2511
2512 void DrawGameDoorValues()
2513 {
2514   UpdateGameControlValues();
2515   DisplayGameControlValues();
2516 }
2517
2518 void DrawGameDoorValues_OLD()
2519 {
2520   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2521   int dynamite_value = 0;
2522   int score_value = (local_player->LevelSolved ? local_player->score_final :
2523                      local_player->score);
2524   int gems_value = local_player->gems_still_needed;
2525   int key_bits = 0;
2526   int i, j;
2527
2528   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2529   {
2530     DrawGameDoorValues_EM();
2531
2532     return;
2533   }
2534
2535   if (game.centered_player_nr == -1)
2536   {
2537     for (i = 0; i < MAX_PLAYERS; i++)
2538     {
2539       for (j = 0; j < MAX_NUM_KEYS; j++)
2540         if (stored_player[i].key[j])
2541           key_bits |= (1 << j);
2542
2543       dynamite_value += stored_player[i].inventory_size;
2544     }
2545   }
2546   else
2547   {
2548     int player_nr = game.centered_player_nr;
2549
2550     for (i = 0; i < MAX_NUM_KEYS; i++)
2551       if (stored_player[player_nr].key[i])
2552         key_bits |= (1 << i);
2553
2554     dynamite_value = stored_player[player_nr].inventory_size;
2555   }
2556
2557   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2558                     key_bits);
2559 }
2560
2561
2562 /*
2563   =============================================================================
2564   InitGameEngine()
2565   -----------------------------------------------------------------------------
2566   initialize game engine due to level / tape version number
2567   =============================================================================
2568 */
2569
2570 static void InitGameEngine()
2571 {
2572   int i, j, k, l, x, y;
2573
2574   /* set game engine from tape file when re-playing, else from level file */
2575   game.engine_version = (tape.playing ? tape.engine_version :
2576                          level.game_version);
2577
2578   /* ---------------------------------------------------------------------- */
2579   /* set flags for bugs and changes according to active game engine version */
2580   /* ---------------------------------------------------------------------- */
2581
2582   /*
2583     Summary of bugfix/change:
2584     Fixed handling for custom elements that change when pushed by the player.
2585
2586     Fixed/changed in version:
2587     3.1.0
2588
2589     Description:
2590     Before 3.1.0, custom elements that "change when pushing" changed directly
2591     after the player started pushing them (until then handled in "DigField()").
2592     Since 3.1.0, these custom elements are not changed until the "pushing"
2593     move of the element is finished (now handled in "ContinueMoving()").
2594
2595     Affected levels/tapes:
2596     The first condition is generally needed for all levels/tapes before version
2597     3.1.0, which might use the old behaviour before it was changed; known tapes
2598     that are affected are some tapes from the level set "Walpurgis Gardens" by
2599     Jamie Cullen.
2600     The second condition is an exception from the above case and is needed for
2601     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2602     above (including some development versions of 3.1.0), but before it was
2603     known that this change would break tapes like the above and was fixed in
2604     3.1.1, so that the changed behaviour was active although the engine version
2605     while recording maybe was before 3.1.0. There is at least one tape that is
2606     affected by this exception, which is the tape for the one-level set "Bug
2607     Machine" by Juergen Bonhagen.
2608   */
2609
2610   game.use_change_when_pushing_bug =
2611     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2612      !(tape.playing &&
2613        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2614        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2615
2616   /*
2617     Summary of bugfix/change:
2618     Fixed handling for blocking the field the player leaves when moving.
2619
2620     Fixed/changed in version:
2621     3.1.1
2622
2623     Description:
2624     Before 3.1.1, when "block last field when moving" was enabled, the field
2625     the player is leaving when moving was blocked for the time of the move,
2626     and was directly unblocked afterwards. This resulted in the last field
2627     being blocked for exactly one less than the number of frames of one player
2628     move. Additionally, even when blocking was disabled, the last field was
2629     blocked for exactly one frame.
2630     Since 3.1.1, due to changes in player movement handling, the last field
2631     is not blocked at all when blocking is disabled. When blocking is enabled,
2632     the last field is blocked for exactly the number of frames of one player
2633     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2634     last field is blocked for exactly one more than the number of frames of
2635     one player move.
2636
2637     Affected levels/tapes:
2638     (!!! yet to be determined -- probably many !!!)
2639   */
2640
2641   game.use_block_last_field_bug =
2642     (game.engine_version < VERSION_IDENT(3,1,1,0));
2643
2644   /*
2645     Summary of bugfix/change:
2646     Changed behaviour of CE changes with multiple changes per single frame.
2647
2648     Fixed/changed in version:
2649     3.2.0-6
2650
2651     Description:
2652     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2653     This resulted in race conditions where CEs seem to behave strange in some
2654     situations (where triggered CE changes were just skipped because there was
2655     already a CE change on that tile in the playfield in that engine frame).
2656     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2657     (The number of changes per frame must be limited in any case, because else
2658     it is easily possible to define CE changes that would result in an infinite
2659     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2660     should be set large enough so that it would only be reached in cases where
2661     the corresponding CE change conditions run into a loop. Therefore, it seems
2662     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2663     maximal number of change pages for custom elements.)
2664
2665     Affected levels/tapes:
2666     Probably many.
2667   */
2668
2669 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2670   game.max_num_changes_per_frame = 1;
2671 #else
2672   game.max_num_changes_per_frame =
2673     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2674 #endif
2675
2676   /* ---------------------------------------------------------------------- */
2677
2678   /* default scan direction: scan playfield from top/left to bottom/right */
2679   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2680
2681   /* dynamically adjust element properties according to game engine version */
2682   InitElementPropertiesEngine(game.engine_version);
2683
2684 #if 0
2685   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2686   printf("          tape version == %06d [%s] [file: %06d]\n",
2687          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2688          tape.file_version);
2689   printf("       => game.engine_version == %06d\n", game.engine_version);
2690 #endif
2691
2692   /* ---------- initialize player's initial move delay --------------------- */
2693
2694   /* dynamically adjust player properties according to level information */
2695   for (i = 0; i < MAX_PLAYERS; i++)
2696     game.initial_move_delay_value[i] =
2697       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2698
2699   /* dynamically adjust player properties according to game engine version */
2700   for (i = 0; i < MAX_PLAYERS; i++)
2701     game.initial_move_delay[i] =
2702       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2703        game.initial_move_delay_value[i] : 0);
2704
2705   /* ---------- initialize player's initial push delay --------------------- */
2706
2707   /* dynamically adjust player properties according to game engine version */
2708   game.initial_push_delay_value =
2709     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2710
2711   /* ---------- initialize changing elements ------------------------------- */
2712
2713   /* initialize changing elements information */
2714   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2715   {
2716     struct ElementInfo *ei = &element_info[i];
2717
2718     /* this pointer might have been changed in the level editor */
2719     ei->change = &ei->change_page[0];
2720
2721     if (!IS_CUSTOM_ELEMENT(i))
2722     {
2723       ei->change->target_element = EL_EMPTY_SPACE;
2724       ei->change->delay_fixed = 0;
2725       ei->change->delay_random = 0;
2726       ei->change->delay_frames = 1;
2727     }
2728
2729     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2730     {
2731       ei->has_change_event[j] = FALSE;
2732
2733       ei->event_page_nr[j] = 0;
2734       ei->event_page[j] = &ei->change_page[0];
2735     }
2736   }
2737
2738   /* add changing elements from pre-defined list */
2739   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2740   {
2741     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2742     struct ElementInfo *ei = &element_info[ch_delay->element];
2743
2744     ei->change->target_element       = ch_delay->target_element;
2745     ei->change->delay_fixed          = ch_delay->change_delay;
2746
2747     ei->change->pre_change_function  = ch_delay->pre_change_function;
2748     ei->change->change_function      = ch_delay->change_function;
2749     ei->change->post_change_function = ch_delay->post_change_function;
2750
2751     ei->change->can_change = TRUE;
2752     ei->change->can_change_or_has_action = TRUE;
2753
2754     ei->has_change_event[CE_DELAY] = TRUE;
2755
2756     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2757     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2758   }
2759
2760   /* ---------- initialize internal run-time variables ------------- */
2761
2762   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2763   {
2764     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2765
2766     for (j = 0; j < ei->num_change_pages; j++)
2767     {
2768       ei->change_page[j].can_change_or_has_action =
2769         (ei->change_page[j].can_change |
2770          ei->change_page[j].has_action);
2771     }
2772   }
2773
2774   /* add change events from custom element configuration */
2775   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2776   {
2777     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2778
2779     for (j = 0; j < ei->num_change_pages; j++)
2780     {
2781       if (!ei->change_page[j].can_change_or_has_action)
2782         continue;
2783
2784       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2785       {
2786         /* only add event page for the first page found with this event */
2787         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2788         {
2789           ei->has_change_event[k] = TRUE;
2790
2791           ei->event_page_nr[k] = j;
2792           ei->event_page[k] = &ei->change_page[j];
2793         }
2794       }
2795     }
2796   }
2797
2798   /* ---------- initialize run-time trigger player and element ------------- */
2799
2800   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2801   {
2802     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2803
2804     for (j = 0; j < ei->num_change_pages; j++)
2805     {
2806       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2807       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2808       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2809       ei->change_page[j].actual_trigger_ce_value = 0;
2810       ei->change_page[j].actual_trigger_ce_score = 0;
2811     }
2812   }
2813
2814   /* ---------- initialize trigger events ---------------------------------- */
2815
2816   /* initialize trigger events information */
2817   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2818     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2819       trigger_events[i][j] = FALSE;
2820
2821   /* add trigger events from element change event properties */
2822   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2823   {
2824     struct ElementInfo *ei = &element_info[i];
2825
2826     for (j = 0; j < ei->num_change_pages; j++)
2827     {
2828       if (!ei->change_page[j].can_change_or_has_action)
2829         continue;
2830
2831       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2832       {
2833         int trigger_element = ei->change_page[j].trigger_element;
2834
2835         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2836         {
2837           if (ei->change_page[j].has_event[k])
2838           {
2839             if (IS_GROUP_ELEMENT(trigger_element))
2840             {
2841               struct ElementGroupInfo *group =
2842                 element_info[trigger_element].group;
2843
2844               for (l = 0; l < group->num_elements_resolved; l++)
2845                 trigger_events[group->element_resolved[l]][k] = TRUE;
2846             }
2847             else if (trigger_element == EL_ANY_ELEMENT)
2848               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2849                 trigger_events[l][k] = TRUE;
2850             else
2851               trigger_events[trigger_element][k] = TRUE;
2852           }
2853         }
2854       }
2855     }
2856   }
2857
2858   /* ---------- initialize push delay -------------------------------------- */
2859
2860   /* initialize push delay values to default */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862   {
2863     if (!IS_CUSTOM_ELEMENT(i))
2864     {
2865       /* set default push delay values (corrected since version 3.0.7-1) */
2866       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2867       {
2868         element_info[i].push_delay_fixed = 2;
2869         element_info[i].push_delay_random = 8;
2870       }
2871       else
2872       {
2873         element_info[i].push_delay_fixed = 8;
2874         element_info[i].push_delay_random = 8;
2875       }
2876     }
2877   }
2878
2879   /* set push delay value for certain elements from pre-defined list */
2880   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2881   {
2882     int e = push_delay_list[i].element;
2883
2884     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2885     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2886   }
2887
2888   /* set push delay value for Supaplex elements for newer engine versions */
2889   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2890   {
2891     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2892     {
2893       if (IS_SP_ELEMENT(i))
2894       {
2895         /* set SP push delay to just enough to push under a falling zonk */
2896         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2897
2898         element_info[i].push_delay_fixed  = delay;
2899         element_info[i].push_delay_random = 0;
2900       }
2901     }
2902   }
2903
2904   /* ---------- initialize move stepsize ----------------------------------- */
2905
2906   /* initialize move stepsize values to default */
2907   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2908     if (!IS_CUSTOM_ELEMENT(i))
2909       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2910
2911   /* set move stepsize value for certain elements from pre-defined list */
2912   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2913   {
2914     int e = move_stepsize_list[i].element;
2915
2916     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2917   }
2918
2919   /* ---------- initialize collect score ----------------------------------- */
2920
2921   /* initialize collect score values for custom elements from initial value */
2922   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2923     if (IS_CUSTOM_ELEMENT(i))
2924       element_info[i].collect_score = element_info[i].collect_score_initial;
2925
2926   /* ---------- initialize collect count ----------------------------------- */
2927
2928   /* initialize collect count values for non-custom elements */
2929   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2930     if (!IS_CUSTOM_ELEMENT(i))
2931       element_info[i].collect_count_initial = 0;
2932
2933   /* add collect count values for all elements from pre-defined list */
2934   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2935     element_info[collect_count_list[i].element].collect_count_initial =
2936       collect_count_list[i].count;
2937
2938   /* ---------- initialize access direction -------------------------------- */
2939
2940   /* initialize access direction values to default (access from every side) */
2941   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2942     if (!IS_CUSTOM_ELEMENT(i))
2943       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2944
2945   /* set access direction value for certain elements from pre-defined list */
2946   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2947     element_info[access_direction_list[i].element].access_direction =
2948       access_direction_list[i].direction;
2949
2950   /* ---------- initialize explosion content ------------------------------- */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952   {
2953     if (IS_CUSTOM_ELEMENT(i))
2954       continue;
2955
2956     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2957     {
2958       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2959
2960       element_info[i].content.e[x][y] =
2961         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2962          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2963          i == EL_PLAYER_3 ? EL_EMERALD :
2964          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2965          i == EL_MOLE ? EL_EMERALD_RED :
2966          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2967          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2968          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2969          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2970          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2971          i == EL_WALL_EMERALD ? EL_EMERALD :
2972          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2973          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2974          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2975          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2976          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2977          i == EL_WALL_PEARL ? EL_PEARL :
2978          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2979          EL_EMPTY);
2980     }
2981   }
2982
2983   /* ---------- initialize recursion detection ------------------------------ */
2984   recursion_loop_depth = 0;
2985   recursion_loop_detected = FALSE;
2986   recursion_loop_element = EL_UNDEFINED;
2987
2988   /* ---------- initialize graphics engine ---------------------------------- */
2989   game.scroll_delay_value =
2990     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2991      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
2992   game.scroll_delay_value =
2993     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2994 }
2995
2996 int get_num_special_action(int element, int action_first, int action_last)
2997 {
2998   int num_special_action = 0;
2999   int i, j;
3000
3001   for (i = action_first; i <= action_last; i++)
3002   {
3003     boolean found = FALSE;
3004
3005     for (j = 0; j < NUM_DIRECTIONS; j++)
3006       if (el_act_dir2img(element, i, j) !=
3007           el_act_dir2img(element, ACTION_DEFAULT, j))
3008         found = TRUE;
3009
3010     if (found)
3011       num_special_action++;
3012     else
3013       break;
3014   }
3015
3016   return num_special_action;
3017 }
3018
3019
3020 /*
3021   =============================================================================
3022   InitGame()
3023   -----------------------------------------------------------------------------
3024   initialize and start new game
3025   =============================================================================
3026 */
3027
3028 void InitGame()
3029 {
3030   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3031   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3032   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3033 #if 0
3034   boolean do_fading = (game_status == GAME_MODE_MAIN);
3035 #endif
3036   int i, j, x, y;
3037
3038   game_status = GAME_MODE_PLAYING;
3039
3040   InitGameEngine();
3041   InitGameControlValues();
3042
3043   /* don't play tapes over network */
3044   network_playing = (options.network && !tape.playing);
3045
3046   for (i = 0; i < MAX_PLAYERS; i++)
3047   {
3048     struct PlayerInfo *player = &stored_player[i];
3049
3050     player->index_nr = i;
3051     player->index_bit = (1 << i);
3052     player->element_nr = EL_PLAYER_1 + i;
3053
3054     player->present = FALSE;
3055     player->active = FALSE;
3056     player->killed = FALSE;
3057
3058     player->action = 0;
3059     player->effective_action = 0;
3060     player->programmed_action = 0;
3061
3062     player->score = 0;
3063     player->score_final = 0;
3064
3065     player->gems_still_needed = level.gems_needed;
3066     player->sokobanfields_still_needed = 0;
3067     player->lights_still_needed = 0;
3068     player->friends_still_needed = 0;
3069
3070     for (j = 0; j < MAX_NUM_KEYS; j++)
3071       player->key[j] = FALSE;
3072
3073     player->num_white_keys = 0;
3074
3075     player->dynabomb_count = 0;
3076     player->dynabomb_size = 1;
3077     player->dynabombs_left = 0;
3078     player->dynabomb_xl = FALSE;
3079
3080     player->MovDir = MV_NONE;
3081     player->MovPos = 0;
3082     player->GfxPos = 0;
3083     player->GfxDir = MV_NONE;
3084     player->GfxAction = ACTION_DEFAULT;
3085     player->Frame = 0;
3086     player->StepFrame = 0;
3087
3088     player->use_murphy = FALSE;
3089     player->artwork_element =
3090       (level.use_artwork_element[i] ? level.artwork_element[i] :
3091        player->element_nr);
3092
3093     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3094     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3095
3096     player->gravity = level.initial_player_gravity[i];
3097
3098     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3099
3100     player->actual_frame_counter = 0;
3101
3102     player->step_counter = 0;
3103
3104     player->last_move_dir = MV_NONE;
3105
3106     player->is_active = FALSE;
3107
3108     player->is_waiting = FALSE;
3109     player->is_moving = FALSE;
3110     player->is_auto_moving = FALSE;
3111     player->is_digging = FALSE;
3112     player->is_snapping = FALSE;
3113     player->is_collecting = FALSE;
3114     player->is_pushing = FALSE;
3115     player->is_switching = FALSE;
3116     player->is_dropping = FALSE;
3117     player->is_dropping_pressed = FALSE;
3118
3119     player->is_bored = FALSE;
3120     player->is_sleeping = FALSE;
3121
3122     player->frame_counter_bored = -1;
3123     player->frame_counter_sleeping = -1;
3124
3125     player->anim_delay_counter = 0;
3126     player->post_delay_counter = 0;
3127
3128     player->dir_waiting = MV_NONE;
3129     player->action_waiting = ACTION_DEFAULT;
3130     player->last_action_waiting = ACTION_DEFAULT;
3131     player->special_action_bored = ACTION_DEFAULT;
3132     player->special_action_sleeping = ACTION_DEFAULT;
3133
3134     player->switch_x = -1;
3135     player->switch_y = -1;
3136
3137     player->drop_x = -1;
3138     player->drop_y = -1;
3139
3140     player->show_envelope = 0;
3141
3142     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3143
3144     player->push_delay       = -1;      /* initialized when pushing starts */
3145     player->push_delay_value = game.initial_push_delay_value;
3146
3147     player->drop_delay = 0;
3148     player->drop_pressed_delay = 0;
3149
3150     player->last_jx = -1;
3151     player->last_jy = -1;
3152     player->jx = -1;
3153     player->jy = -1;
3154
3155     player->shield_normal_time_left = 0;
3156     player->shield_deadly_time_left = 0;
3157
3158     player->inventory_infinite_element = EL_UNDEFINED;
3159     player->inventory_size = 0;
3160
3161     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3162     SnapField(player, 0, 0);
3163
3164     player->LevelSolved = FALSE;
3165     player->GameOver = FALSE;
3166
3167     player->LevelSolved_GameWon = FALSE;
3168     player->LevelSolved_GameEnd = FALSE;
3169     player->LevelSolved_PanelOff = FALSE;
3170     player->LevelSolved_SaveTape = FALSE;
3171     player->LevelSolved_SaveScore = FALSE;
3172   }
3173
3174   network_player_action_received = FALSE;
3175
3176 #if defined(NETWORK_AVALIABLE)
3177   /* initial null action */
3178   if (network_playing)
3179     SendToServer_MovePlayer(MV_NONE);
3180 #endif
3181
3182   ZX = ZY = -1;
3183   ExitX = ExitY = -1;
3184
3185   FrameCounter = 0;
3186   TimeFrames = 0;
3187   TimePlayed = 0;
3188   TimeLeft = level.time;
3189   TapeTime = 0;
3190
3191   ScreenMovDir = MV_NONE;
3192   ScreenMovPos = 0;
3193   ScreenGfxPos = 0;
3194
3195   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3196
3197   AllPlayersGone = FALSE;
3198
3199   game.yamyam_content_nr = 0;
3200   game.magic_wall_active = FALSE;
3201   game.magic_wall_time_left = 0;
3202   game.light_time_left = 0;
3203   game.timegate_time_left = 0;
3204   game.switchgate_pos = 0;
3205   game.wind_direction = level.wind_direction_initial;
3206
3207 #if !USE_PLAYER_GRAVITY
3208   game.gravity = FALSE;
3209   game.explosions_delayed = TRUE;
3210 #endif
3211
3212   game.lenses_time_left = 0;
3213   game.magnify_time_left = 0;
3214
3215   game.ball_state = level.ball_state_initial;
3216   game.ball_content_nr = 0;
3217
3218   game.envelope_active = FALSE;
3219
3220   /* set focus to local player for network games, else to all players */
3221   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3222   game.centered_player_nr_next = game.centered_player_nr;
3223   game.set_centered_player = FALSE;
3224
3225   if (network_playing && tape.recording)
3226   {
3227     /* store client dependent player focus when recording network games */
3228     tape.centered_player_nr_next = game.centered_player_nr_next;
3229     tape.set_centered_player = TRUE;
3230   }
3231
3232   for (i = 0; i < NUM_BELTS; i++)
3233   {
3234     game.belt_dir[i] = MV_NONE;
3235     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3236   }
3237
3238   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3239     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3240
3241   SCAN_PLAYFIELD(x, y)
3242   {
3243     Feld[x][y] = level.field[x][y];
3244     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3245     ChangeDelay[x][y] = 0;
3246     ChangePage[x][y] = -1;
3247 #if USE_NEW_CUSTOM_VALUE
3248     CustomValue[x][y] = 0;              /* initialized in InitField() */
3249 #endif
3250     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3251     AmoebaNr[x][y] = 0;
3252     WasJustMoving[x][y] = 0;
3253     WasJustFalling[x][y] = 0;
3254     CheckCollision[x][y] = 0;
3255     CheckImpact[x][y] = 0;
3256     Stop[x][y] = FALSE;
3257     Pushed[x][y] = FALSE;
3258
3259     ChangeCount[x][y] = 0;
3260     ChangeEvent[x][y] = -1;
3261
3262     ExplodePhase[x][y] = 0;
3263     ExplodeDelay[x][y] = 0;
3264     ExplodeField[x][y] = EX_TYPE_NONE;
3265
3266     RunnerVisit[x][y] = 0;
3267     PlayerVisit[x][y] = 0;
3268
3269     GfxFrame[x][y] = 0;
3270     GfxRandom[x][y] = INIT_GFX_RANDOM();
3271     GfxElement[x][y] = EL_UNDEFINED;
3272     GfxAction[x][y] = ACTION_DEFAULT;
3273     GfxDir[x][y] = MV_NONE;
3274   }
3275
3276   SCAN_PLAYFIELD(x, y)
3277   {
3278     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3279       emulate_bd = FALSE;
3280     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3281       emulate_sb = FALSE;
3282     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3283       emulate_sp = FALSE;
3284
3285     InitField(x, y, TRUE);
3286   }
3287
3288   InitBeltMovement();
3289
3290   for (i = 0; i < MAX_PLAYERS; i++)
3291   {
3292     struct PlayerInfo *player = &stored_player[i];
3293
3294     /* set number of special actions for bored and sleeping animation */
3295     player->num_special_action_bored =
3296       get_num_special_action(player->artwork_element,
3297                              ACTION_BORING_1, ACTION_BORING_LAST);
3298     player->num_special_action_sleeping =
3299       get_num_special_action(player->artwork_element,
3300                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3301   }
3302
3303   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3304                     emulate_sb ? EMU_SOKOBAN :
3305                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3306
3307 #if USE_NEW_ALL_SLIPPERY
3308   /* initialize type of slippery elements */
3309   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3310   {
3311     if (!IS_CUSTOM_ELEMENT(i))
3312     {
3313       /* default: elements slip down either to the left or right randomly */
3314       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3315
3316       /* SP style elements prefer to slip down on the left side */
3317       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3318         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3319
3320       /* BD style elements prefer to slip down on the left side */
3321       if (game.emulation == EMU_BOULDERDASH)
3322         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3323     }
3324   }
3325 #endif
3326
3327   /* initialize explosion and ignition delay */
3328   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3329   {
3330     if (!IS_CUSTOM_ELEMENT(i))
3331     {
3332       int num_phase = 8;
3333       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3334                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3335                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3336       int last_phase = (num_phase + 1) * delay;
3337       int half_phase = (num_phase / 2) * delay;
3338
3339       element_info[i].explosion_delay = last_phase - 1;
3340       element_info[i].ignition_delay = half_phase;
3341
3342       if (i == EL_BLACK_ORB)
3343         element_info[i].ignition_delay = 1;
3344     }
3345
3346 #if 0
3347     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3348       element_info[i].explosion_delay = 1;
3349
3350     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3351       element_info[i].ignition_delay = 1;
3352 #endif
3353   }
3354
3355   /* correct non-moving belts to start moving left */
3356   for (i = 0; i < NUM_BELTS; i++)
3357     if (game.belt_dir[i] == MV_NONE)
3358       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3359
3360   /* check if any connected player was not found in playfield */
3361   for (i = 0; i < MAX_PLAYERS; i++)
3362   {
3363     struct PlayerInfo *player = &stored_player[i];
3364
3365     if (player->connected && !player->present)
3366     {
3367       for (j = 0; j < MAX_PLAYERS; j++)
3368       {
3369         struct PlayerInfo *some_player = &stored_player[j];
3370         int jx = some_player->jx, jy = some_player->jy;
3371
3372         /* assign first free player found that is present in the playfield */
3373         if (some_player->present && !some_player->connected)
3374         {
3375           player->present = TRUE;
3376           player->active = TRUE;
3377
3378           some_player->present = FALSE;
3379           some_player->active = FALSE;
3380
3381           player->artwork_element = some_player->artwork_element;
3382
3383           player->block_last_field       = some_player->block_last_field;
3384           player->block_delay_adjustment = some_player->block_delay_adjustment;
3385
3386           StorePlayer[jx][jy] = player->element_nr;
3387           player->jx = player->last_jx = jx;
3388           player->jy = player->last_jy = jy;
3389
3390           break;
3391         }
3392       }
3393     }
3394   }
3395
3396   if (tape.playing)
3397   {
3398     /* when playing a tape, eliminate all players who do not participate */
3399
3400     for (i = 0; i < MAX_PLAYERS; i++)
3401     {
3402       if (stored_player[i].active && !tape.player_participates[i])
3403       {
3404         struct PlayerInfo *player = &stored_player[i];
3405         int jx = player->jx, jy = player->jy;
3406
3407         player->active = FALSE;
3408         StorePlayer[jx][jy] = 0;
3409         Feld[jx][jy] = EL_EMPTY;
3410       }
3411     }
3412   }
3413   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3414   {
3415     /* when in single player mode, eliminate all but the first active player */
3416
3417     for (i = 0; i < MAX_PLAYERS; i++)
3418     {
3419       if (stored_player[i].active)
3420       {
3421         for (j = i + 1; j < MAX_PLAYERS; j++)
3422         {
3423           if (stored_player[j].active)
3424           {
3425             struct PlayerInfo *player = &stored_player[j];
3426             int jx = player->jx, jy = player->jy;
3427
3428             player->active = FALSE;
3429             player->present = FALSE;
3430
3431             StorePlayer[jx][jy] = 0;
3432             Feld[jx][jy] = EL_EMPTY;
3433           }
3434         }
3435       }
3436     }
3437   }
3438
3439   /* when recording the game, store which players take part in the game */
3440   if (tape.recording)
3441   {
3442     for (i = 0; i < MAX_PLAYERS; i++)
3443       if (stored_player[i].active)
3444         tape.player_participates[i] = TRUE;
3445   }
3446
3447   if (options.debug)
3448   {
3449     for (i = 0; i < MAX_PLAYERS; i++)
3450     {
3451       struct PlayerInfo *player = &stored_player[i];
3452
3453       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3454              i+1,
3455              player->present,
3456              player->connected,
3457              player->active);
3458       if (local_player == player)
3459         printf("Player  %d is local player.\n", i+1);
3460     }
3461   }
3462
3463   if (BorderElement == EL_EMPTY)
3464   {
3465     SBX_Left = 0;
3466     SBX_Right = lev_fieldx - SCR_FIELDX;
3467     SBY_Upper = 0;
3468     SBY_Lower = lev_fieldy - SCR_FIELDY;
3469   }
3470   else
3471   {
3472     SBX_Left = -1;
3473     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3474     SBY_Upper = -1;
3475     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3476   }
3477
3478   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3479     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3480
3481   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3482     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3483
3484   /* if local player not found, look for custom element that might create
3485      the player (make some assumptions about the right custom element) */
3486   if (!local_player->present)
3487   {
3488     int start_x = 0, start_y = 0;
3489     int found_rating = 0;
3490     int found_element = EL_UNDEFINED;
3491     int player_nr = local_player->index_nr;
3492
3493     SCAN_PLAYFIELD(x, y)
3494     {
3495       int element = Feld[x][y];
3496       int content;
3497       int xx, yy;
3498       boolean is_player;
3499
3500       if (level.use_start_element[player_nr] &&
3501           level.start_element[player_nr] == element &&
3502           found_rating < 4)
3503       {
3504         start_x = x;
3505         start_y = y;
3506
3507         found_rating = 4;
3508         found_element = element;
3509       }
3510
3511       if (!IS_CUSTOM_ELEMENT(element))
3512         continue;
3513
3514       if (CAN_CHANGE(element))
3515       {
3516         for (i = 0; i < element_info[element].num_change_pages; i++)
3517         {
3518           /* check for player created from custom element as single target */
3519           content = element_info[element].change_page[i].target_element;
3520           is_player = ELEM_IS_PLAYER(content);
3521
3522           if (is_player && (found_rating < 3 ||
3523                             (found_rating == 3 && element < found_element)))
3524           {
3525             start_x = x;
3526             start_y = y;
3527
3528             found_rating = 3;
3529             found_element = element;
3530           }
3531         }
3532       }
3533
3534       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3535       {
3536         /* check for player created from custom element as explosion content */
3537         content = element_info[element].content.e[xx][yy];
3538         is_player = ELEM_IS_PLAYER(content);
3539
3540         if (is_player && (found_rating < 2 ||
3541                           (found_rating == 2 && element < found_element)))
3542         {
3543           start_x = x + xx - 1;
3544           start_y = y + yy - 1;
3545
3546           found_rating = 2;
3547           found_element = element;
3548         }
3549
3550         if (!CAN_CHANGE(element))
3551           continue;
3552
3553         for (i = 0; i < element_info[element].num_change_pages; i++)
3554         {
3555           /* check for player created from custom element as extended target */
3556           content =
3557             element_info[element].change_page[i].target_content.e[xx][yy];
3558
3559           is_player = ELEM_IS_PLAYER(content);
3560
3561           if (is_player && (found_rating < 1 ||
3562                             (found_rating == 1 && element < found_element)))
3563           {
3564             start_x = x + xx - 1;
3565             start_y = y + yy - 1;
3566
3567             found_rating = 1;
3568             found_element = element;
3569           }
3570         }
3571       }
3572     }
3573
3574     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3575                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3576                 start_x - MIDPOSX);
3577
3578     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3579                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3580                 start_y - MIDPOSY);
3581   }
3582   else
3583   {
3584     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3585                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3586                 local_player->jx - MIDPOSX);
3587
3588     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3589                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3590                 local_player->jy - MIDPOSY);
3591   }
3592
3593   StopAnimation();
3594
3595   if (!game.restart_level)
3596     CloseDoor(DOOR_CLOSE_1);
3597
3598 #if 1
3599   if (level_editor_test_game)
3600     FadeSkipNextFadeIn();
3601   else
3602     FadeSetEnterScreen();
3603 #else
3604   if (level_editor_test_game)
3605     fading = fading_none;
3606   else
3607     fading = menu.destination;
3608 #endif
3609
3610 #if 1
3611   FadeOut(REDRAW_FIELD);
3612 #else
3613   if (do_fading)
3614     FadeOut(REDRAW_FIELD);
3615 #endif
3616
3617   /* !!! FIX THIS (START) !!! */
3618   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3619   {
3620     InitGameEngine_EM();
3621
3622     /* blit playfield from scroll buffer to normal back buffer for fading in */
3623     BlitScreenToBitmap_EM(backbuffer);
3624   }
3625   else
3626   {
3627     DrawLevel();
3628     DrawAllPlayers();
3629
3630     /* after drawing the level, correct some elements */
3631     if (game.timegate_time_left == 0)
3632       CloseAllOpenTimegates();
3633
3634     /* blit playfield from scroll buffer to normal back buffer for fading in */
3635     if (setup.soft_scrolling)
3636       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3637
3638     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3639   }
3640   /* !!! FIX THIS (END) !!! */
3641
3642 #if 1
3643   FadeIn(REDRAW_FIELD);
3644 #else
3645   if (do_fading)
3646     FadeIn(REDRAW_FIELD);
3647
3648   BackToFront();
3649 #endif
3650
3651   if (!game.restart_level)
3652   {
3653     /* copy default game door content to main double buffer */
3654     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3655                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3656   }
3657
3658   SetPanelBackground();
3659   SetDrawBackgroundMask(REDRAW_DOOR_1);
3660
3661   DrawGameDoorValues();
3662
3663   if (!game.restart_level)
3664   {
3665     UnmapGameButtons();
3666     UnmapTapeButtons();
3667     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3668     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3669     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3670     MapGameButtons();
3671     MapTapeButtons();
3672
3673     /* copy actual game door content to door double buffer for OpenDoor() */
3674     BlitBitmap(drawto, bitmap_db_door,
3675                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3676
3677     OpenDoor(DOOR_OPEN_ALL);
3678
3679     PlaySound(SND_GAME_STARTING);
3680
3681     if (setup.sound_music)
3682       PlayLevelMusic();
3683
3684     KeyboardAutoRepeatOffUnlessAutoplay();
3685
3686     if (options.debug)
3687     {
3688       for (i = 0; i < MAX_PLAYERS; i++)
3689         printf("Player %d %sactive.\n",
3690                i + 1, (stored_player[i].active ? "" : "not "));
3691     }
3692   }
3693
3694 #if 1
3695   UnmapAllGadgets();
3696
3697   MapGameButtons();
3698   MapTapeButtons();
3699 #endif
3700
3701   game.restart_level = FALSE;
3702 }
3703
3704 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3705 {
3706   /* this is used for non-R'n'D game engines to update certain engine values */
3707
3708   /* needed to determine if sounds are played within the visible screen area */
3709   scroll_x = actual_scroll_x;
3710   scroll_y = actual_scroll_y;
3711 }
3712
3713 void InitMovDir(int x, int y)
3714 {
3715   int i, element = Feld[x][y];
3716   static int xy[4][2] =
3717   {
3718     {  0, +1 },
3719     { +1,  0 },
3720     {  0, -1 },
3721     { -1,  0 }
3722   };
3723   static int direction[3][4] =
3724   {
3725     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3726     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3727     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3728   };
3729
3730   switch (element)
3731   {
3732     case EL_BUG_RIGHT:
3733     case EL_BUG_UP:
3734     case EL_BUG_LEFT:
3735     case EL_BUG_DOWN:
3736       Feld[x][y] = EL_BUG;
3737       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3738       break;
3739
3740     case EL_SPACESHIP_RIGHT:
3741     case EL_SPACESHIP_UP:
3742     case EL_SPACESHIP_LEFT:
3743     case EL_SPACESHIP_DOWN:
3744       Feld[x][y] = EL_SPACESHIP;
3745       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3746       break;
3747
3748     case EL_BD_BUTTERFLY_RIGHT:
3749     case EL_BD_BUTTERFLY_UP:
3750     case EL_BD_BUTTERFLY_LEFT:
3751     case EL_BD_BUTTERFLY_DOWN:
3752       Feld[x][y] = EL_BD_BUTTERFLY;
3753       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3754       break;
3755
3756     case EL_BD_FIREFLY_RIGHT:
3757     case EL_BD_FIREFLY_UP:
3758     case EL_BD_FIREFLY_LEFT:
3759     case EL_BD_FIREFLY_DOWN:
3760       Feld[x][y] = EL_BD_FIREFLY;
3761       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3762       break;
3763
3764     case EL_PACMAN_RIGHT:
3765     case EL_PACMAN_UP:
3766     case EL_PACMAN_LEFT:
3767     case EL_PACMAN_DOWN:
3768       Feld[x][y] = EL_PACMAN;
3769       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3770       break;
3771
3772     case EL_YAMYAM_LEFT:
3773     case EL_YAMYAM_RIGHT:
3774     case EL_YAMYAM_UP:
3775     case EL_YAMYAM_DOWN:
3776       Feld[x][y] = EL_YAMYAM;
3777       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3778       break;
3779
3780     case EL_SP_SNIKSNAK:
3781       MovDir[x][y] = MV_UP;
3782       break;
3783
3784     case EL_SP_ELECTRON:
3785       MovDir[x][y] = MV_LEFT;
3786       break;
3787
3788     case EL_MOLE_LEFT:
3789     case EL_MOLE_RIGHT:
3790     case EL_MOLE_UP:
3791     case EL_MOLE_DOWN:
3792       Feld[x][y] = EL_MOLE;
3793       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3794       break;
3795
3796     default:
3797       if (IS_CUSTOM_ELEMENT(element))
3798       {
3799         struct ElementInfo *ei = &element_info[element];
3800         int move_direction_initial = ei->move_direction_initial;
3801         int move_pattern = ei->move_pattern;
3802
3803         if (move_direction_initial == MV_START_PREVIOUS)
3804         {
3805           if (MovDir[x][y] != MV_NONE)
3806             return;
3807
3808           move_direction_initial = MV_START_AUTOMATIC;
3809         }
3810
3811         if (move_direction_initial == MV_START_RANDOM)
3812           MovDir[x][y] = 1 << RND(4);
3813         else if (move_direction_initial & MV_ANY_DIRECTION)
3814           MovDir[x][y] = move_direction_initial;
3815         else if (move_pattern == MV_ALL_DIRECTIONS ||
3816                  move_pattern == MV_TURNING_LEFT ||
3817                  move_pattern == MV_TURNING_RIGHT ||
3818                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3819                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3820                  move_pattern == MV_TURNING_RANDOM)
3821           MovDir[x][y] = 1 << RND(4);
3822         else if (move_pattern == MV_HORIZONTAL)
3823           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3824         else if (move_pattern == MV_VERTICAL)
3825           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3826         else if (move_pattern & MV_ANY_DIRECTION)
3827           MovDir[x][y] = element_info[element].move_pattern;
3828         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3829                  move_pattern == MV_ALONG_RIGHT_SIDE)
3830         {
3831           /* use random direction as default start direction */
3832           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3833             MovDir[x][y] = 1 << RND(4);
3834
3835           for (i = 0; i < NUM_DIRECTIONS; i++)
3836           {
3837             int x1 = x + xy[i][0];
3838             int y1 = y + xy[i][1];
3839
3840             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3841             {
3842               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3843                 MovDir[x][y] = direction[0][i];
3844               else
3845                 MovDir[x][y] = direction[1][i];
3846
3847               break;
3848             }
3849           }
3850         }                
3851       }
3852       else
3853       {
3854         MovDir[x][y] = 1 << RND(4);
3855
3856         if (element != EL_BUG &&
3857             element != EL_SPACESHIP &&
3858             element != EL_BD_BUTTERFLY &&
3859             element != EL_BD_FIREFLY)
3860           break;
3861
3862         for (i = 0; i < NUM_DIRECTIONS; i++)
3863         {
3864           int x1 = x + xy[i][0];
3865           int y1 = y + xy[i][1];
3866
3867           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3868           {
3869             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3870             {
3871               MovDir[x][y] = direction[0][i];
3872               break;
3873             }
3874             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3875                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3876             {
3877               MovDir[x][y] = direction[1][i];
3878               break;
3879             }
3880           }
3881         }
3882       }
3883       break;
3884   }
3885
3886   GfxDir[x][y] = MovDir[x][y];
3887 }
3888
3889 void InitAmoebaNr(int x, int y)
3890 {
3891   int i;
3892   int group_nr = AmoebeNachbarNr(x, y);
3893
3894   if (group_nr == 0)
3895   {
3896     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3897     {
3898       if (AmoebaCnt[i] == 0)
3899       {
3900         group_nr = i;
3901         break;
3902       }
3903     }
3904   }
3905
3906   AmoebaNr[x][y] = group_nr;
3907   AmoebaCnt[group_nr]++;
3908   AmoebaCnt2[group_nr]++;
3909 }
3910
3911 static void PlayerWins(struct PlayerInfo *player)
3912 {
3913   player->LevelSolved = TRUE;
3914   player->GameOver = TRUE;
3915
3916   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3917                          level.native_em_level->lev->score : player->score);
3918 }
3919
3920 void GameWon()
3921 {
3922   static int time, time_final;
3923   static int score, score_final;
3924   static int game_over_delay_1 = 0;
3925   static int game_over_delay_2 = 0;
3926   int game_over_delay_value_1 = 50;
3927   int game_over_delay_value_2 = 50;
3928
3929   if (!local_player->LevelSolved_GameWon)
3930   {
3931     int i;
3932
3933     /* do not start end game actions before the player stops moving (to exit) */
3934     if (local_player->MovPos)
3935       return;
3936
3937     local_player->LevelSolved_GameWon = TRUE;
3938     local_player->LevelSolved_SaveTape = tape.recording;
3939     local_player->LevelSolved_SaveScore = !tape.playing;
3940
3941     if (tape.auto_play)         /* tape might already be stopped here */
3942       tape.auto_play_level_solved = TRUE;
3943
3944 #if 1
3945     TapeStop();
3946 #endif
3947
3948     game_over_delay_1 = game_over_delay_value_1;
3949     game_over_delay_2 = game_over_delay_value_2;
3950
3951     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3952     score = score_final = local_player->score_final;
3953
3954     if (TimeLeft > 0)
3955     {
3956       time_final = 0;
3957       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3958     }
3959     else if (level.time == 0 && TimePlayed < 999)
3960     {
3961       time_final = 999;
3962       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3963     }
3964
3965     local_player->score_final = score_final;
3966
3967     if (level_editor_test_game)
3968     {
3969       time = time_final;
3970       score = score_final;
3971
3972 #if 1
3973       game_control_value[GAME_CONTROL_TIME] = time;
3974       game_control_value[GAME_CONTROL_SCORE] = score;
3975
3976       DisplayGameControlValues();
3977 #else
3978       DrawGameValue_Time(time);
3979       DrawGameValue_Score(score);
3980 #endif
3981     }
3982
3983     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3984     {
3985       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3986       {
3987         /* close exit door after last player */
3988         if ((AllPlayersGone &&
3989              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3990               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3991               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3992             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3993             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3994         {
3995           int element = Feld[ExitX][ExitY];
3996
3997 #if 0
3998           if (element == EL_EM_EXIT_OPEN ||
3999               element == EL_EM_STEEL_EXIT_OPEN)
4000           {
4001             Bang(ExitX, ExitY);
4002           }
4003           else
4004 #endif
4005           {
4006             Feld[ExitX][ExitY] =
4007               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4008                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4009                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4010                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4011                EL_EM_STEEL_EXIT_CLOSING);
4012
4013             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4014           }
4015         }
4016
4017         /* player disappears */
4018         DrawLevelField(ExitX, ExitY);
4019       }
4020
4021       for (i = 0; i < MAX_PLAYERS; i++)
4022       {
4023         struct PlayerInfo *player = &stored_player[i];
4024
4025         if (player->present)
4026         {
4027           RemovePlayer(player);
4028
4029           /* player disappears */
4030           DrawLevelField(player->jx, player->jy);
4031         }
4032       }
4033     }
4034
4035     PlaySound(SND_GAME_WINNING);
4036   }
4037
4038   if (game_over_delay_1 > 0)
4039   {
4040     game_over_delay_1--;
4041
4042     return;
4043   }
4044
4045   if (time != time_final)
4046   {
4047     int time_to_go = ABS(time_final - time);
4048     int time_count_dir = (time < time_final ? +1 : -1);
4049     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4050
4051     time  += time_count_steps * time_count_dir;
4052     score += time_count_steps * level.score[SC_TIME_BONUS];
4053
4054 #if 1
4055     game_control_value[GAME_CONTROL_TIME] = time;
4056     game_control_value[GAME_CONTROL_SCORE] = score;
4057
4058     DisplayGameControlValues();
4059 #else
4060     DrawGameValue_Time(time);
4061     DrawGameValue_Score(score);
4062 #endif
4063
4064     if (time == time_final)
4065       StopSound(SND_GAME_LEVELTIME_BONUS);
4066     else if (setup.sound_loops)
4067       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4068     else
4069       PlaySound(SND_GAME_LEVELTIME_BONUS);
4070
4071     return;
4072   }
4073
4074   local_player->LevelSolved_PanelOff = TRUE;
4075
4076   if (game_over_delay_2 > 0)
4077   {
4078     game_over_delay_2--;
4079
4080     return;
4081   }
4082
4083 #if 1
4084   GameEnd();
4085 #endif
4086 }
4087
4088 void GameEnd()
4089 {
4090   int hi_pos;
4091   boolean raise_level = FALSE;
4092
4093   local_player->LevelSolved_GameEnd = TRUE;
4094
4095   CloseDoor(DOOR_CLOSE_1);
4096
4097   if (local_player->LevelSolved_SaveTape)
4098   {
4099 #if 0
4100     TapeStop();
4101 #endif
4102
4103 #if 1
4104     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4105 #else
4106     SaveTape(tape.level_nr);            /* ask to save tape */
4107 #endif
4108   }
4109
4110   if (level_editor_test_game)
4111   {
4112     game_status = GAME_MODE_MAIN;
4113
4114 #if 1
4115     DrawAndFadeInMainMenu(REDRAW_FIELD);
4116 #else
4117     DrawMainMenu();
4118 #endif
4119
4120     return;
4121   }
4122
4123   if (!local_player->LevelSolved_SaveScore)
4124   {
4125 #if 1
4126     FadeOut(REDRAW_FIELD);
4127 #endif
4128
4129     game_status = GAME_MODE_MAIN;
4130
4131     DrawAndFadeInMainMenu(REDRAW_FIELD);
4132
4133     return;
4134   }
4135
4136   if (level_nr == leveldir_current->handicap_level)
4137   {
4138     leveldir_current->handicap_level++;
4139     SaveLevelSetup_SeriesInfo();
4140   }
4141
4142   if (level_nr < leveldir_current->last_level)
4143     raise_level = TRUE;                 /* advance to next level */
4144
4145   if ((hi_pos = NewHiScore()) >= 0) 
4146   {
4147     game_status = GAME_MODE_SCORES;
4148
4149     DrawHallOfFame(hi_pos);
4150
4151     if (raise_level)
4152     {
4153       level_nr++;
4154       TapeErase();
4155     }
4156   }
4157   else
4158   {
4159 #if 1
4160     FadeOut(REDRAW_FIELD);
4161 #endif
4162
4163     game_status = GAME_MODE_MAIN;
4164
4165     if (raise_level)
4166     {
4167       level_nr++;
4168       TapeErase();
4169     }
4170
4171     DrawAndFadeInMainMenu(REDRAW_FIELD);
4172   }
4173 }
4174
4175 int NewHiScore()
4176 {
4177   int k, l;
4178   int position = -1;
4179
4180   LoadScore(level_nr);
4181
4182   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4183       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4184     return -1;
4185
4186   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4187   {
4188     if (local_player->score_final > highscore[k].Score)
4189     {
4190       /* player has made it to the hall of fame */
4191
4192       if (k < MAX_SCORE_ENTRIES - 1)
4193       {
4194         int m = MAX_SCORE_ENTRIES - 1;
4195
4196 #ifdef ONE_PER_NAME
4197         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4198           if (strEqual(setup.player_name, highscore[l].Name))
4199             m = l;
4200         if (m == k)     /* player's new highscore overwrites his old one */
4201           goto put_into_list;
4202 #endif
4203
4204         for (l = m; l > k; l--)
4205         {
4206           strcpy(highscore[l].Name, highscore[l - 1].Name);
4207           highscore[l].Score = highscore[l - 1].Score;
4208         }
4209       }
4210
4211 #ifdef ONE_PER_NAME
4212       put_into_list:
4213 #endif
4214       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4215       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4216       highscore[k].Score = local_player->score_final; 
4217       position = k;
4218       break;
4219     }
4220
4221 #ifdef ONE_PER_NAME
4222     else if (!strncmp(setup.player_name, highscore[k].Name,
4223                       MAX_PLAYER_NAME_LEN))
4224       break;    /* player already there with a higher score */
4225 #endif
4226
4227   }
4228
4229   if (position >= 0) 
4230     SaveScore(level_nr);
4231
4232   return position;
4233 }
4234
4235 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4236 {
4237   int element = Feld[x][y];
4238   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4239   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4240   int horiz_move = (dx != 0);
4241   int sign = (horiz_move ? dx : dy);
4242   int step = sign * element_info[element].move_stepsize;
4243
4244   /* special values for move stepsize for spring and things on conveyor belt */
4245   if (horiz_move)
4246   {
4247     if (CAN_FALL(element) &&
4248         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4249       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4250     else if (element == EL_SPRING)
4251       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4252   }
4253
4254   return step;
4255 }
4256
4257 inline static int getElementMoveStepsize(int x, int y)
4258 {
4259   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4260 }
4261
4262 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4263 {
4264   if (player->GfxAction != action || player->GfxDir != dir)
4265   {
4266 #if 0
4267     printf("Player frame reset! (%d => %d, %d => %d)\n",
4268            player->GfxAction, action, player->GfxDir, dir);
4269 #endif
4270
4271     player->GfxAction = action;
4272     player->GfxDir = dir;
4273     player->Frame = 0;
4274     player->StepFrame = 0;
4275   }
4276 }
4277
4278 #if USE_GFX_RESET_GFX_ANIMATION
4279 static void ResetGfxFrame(int x, int y, boolean redraw)
4280 {
4281   int element = Feld[x][y];
4282   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4283   int last_gfx_frame = GfxFrame[x][y];
4284
4285   if (graphic_info[graphic].anim_global_sync)
4286     GfxFrame[x][y] = FrameCounter;
4287   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4288     GfxFrame[x][y] = CustomValue[x][y];
4289   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4290     GfxFrame[x][y] = element_info[element].collect_score;
4291   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4292     GfxFrame[x][y] = ChangeDelay[x][y];
4293
4294   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4295     DrawLevelGraphicAnimation(x, y, graphic);
4296 }
4297 #endif
4298
4299 static void ResetGfxAnimation(int x, int y)
4300 {
4301   GfxAction[x][y] = ACTION_DEFAULT;
4302   GfxDir[x][y] = MovDir[x][y];
4303   GfxFrame[x][y] = 0;
4304
4305 #if USE_GFX_RESET_GFX_ANIMATION
4306   ResetGfxFrame(x, y, FALSE);
4307 #endif
4308 }
4309
4310 static void ResetRandomAnimationValue(int x, int y)
4311 {
4312   GfxRandom[x][y] = INIT_GFX_RANDOM();
4313 }
4314
4315 void InitMovingField(int x, int y, int direction)
4316 {
4317   int element = Feld[x][y];
4318   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4319   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4320   int newx = x + dx;
4321   int newy = y + dy;
4322   boolean is_moving_before, is_moving_after;
4323 #if 0
4324   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4325 #endif
4326
4327   /* check if element was/is moving or being moved before/after mode change */
4328 #if 1
4329 #if 1
4330   is_moving_before = (WasJustMoving[x][y] != 0);
4331 #else
4332   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4333   is_moving_before = WasJustMoving[x][y];
4334 #endif
4335 #else
4336   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4337 #endif
4338   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4339
4340   /* reset animation only for moving elements which change direction of moving
4341      or which just started or stopped moving
4342      (else CEs with property "can move" / "not moving" are reset each frame) */
4343 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4344 #if 1
4345   if (is_moving_before != is_moving_after ||
4346       direction != MovDir[x][y])
4347     ResetGfxAnimation(x, y);
4348 #else
4349   if ((is_moving_before || is_moving_after) && !continues_moving)
4350     ResetGfxAnimation(x, y);
4351 #endif
4352 #else
4353   if (!continues_moving)
4354     ResetGfxAnimation(x, y);
4355 #endif
4356
4357   MovDir[x][y] = direction;
4358   GfxDir[x][y] = direction;
4359
4360 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4361   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4362                      direction == MV_DOWN && CAN_FALL(element) ?
4363                      ACTION_FALLING : ACTION_MOVING);
4364 #else
4365   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4366                      ACTION_FALLING : ACTION_MOVING);
4367 #endif
4368
4369   /* this is needed for CEs with property "can move" / "not moving" */
4370
4371   if (is_moving_after)
4372   {
4373     if (Feld[newx][newy] == EL_EMPTY)
4374       Feld[newx][newy] = EL_BLOCKED;
4375
4376     MovDir[newx][newy] = MovDir[x][y];
4377
4378 #if USE_NEW_CUSTOM_VALUE
4379     CustomValue[newx][newy] = CustomValue[x][y];
4380 #endif
4381
4382     GfxFrame[newx][newy] = GfxFrame[x][y];
4383     GfxRandom[newx][newy] = GfxRandom[x][y];
4384     GfxAction[newx][newy] = GfxAction[x][y];
4385     GfxDir[newx][newy] = GfxDir[x][y];
4386   }
4387 }
4388
4389 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4390 {
4391   int direction = MovDir[x][y];
4392   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4393   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4394
4395   *goes_to_x = newx;
4396   *goes_to_y = newy;
4397 }
4398
4399 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4400 {
4401   int oldx = x, oldy = y;
4402   int direction = MovDir[x][y];
4403
4404   if (direction == MV_LEFT)
4405     oldx++;
4406   else if (direction == MV_RIGHT)
4407     oldx--;
4408   else if (direction == MV_UP)
4409     oldy++;
4410   else if (direction == MV_DOWN)
4411     oldy--;
4412
4413   *comes_from_x = oldx;
4414   *comes_from_y = oldy;
4415 }
4416
4417 int MovingOrBlocked2Element(int x, int y)
4418 {
4419   int element = Feld[x][y];
4420
4421   if (element == EL_BLOCKED)
4422   {
4423     int oldx, oldy;
4424
4425     Blocked2Moving(x, y, &oldx, &oldy);
4426     return Feld[oldx][oldy];
4427   }
4428   else
4429     return element;
4430 }
4431
4432 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4433 {
4434   /* like MovingOrBlocked2Element(), but if element is moving
4435      and (x,y) is the field the moving element is just leaving,
4436      return EL_BLOCKED instead of the element value */
4437   int element = Feld[x][y];
4438
4439   if (IS_MOVING(x, y))
4440   {
4441     if (element == EL_BLOCKED)
4442     {
4443       int oldx, oldy;
4444
4445       Blocked2Moving(x, y, &oldx, &oldy);
4446       return Feld[oldx][oldy];
4447     }
4448     else
4449       return EL_BLOCKED;
4450   }
4451   else
4452     return element;
4453 }
4454
4455 static void RemoveField(int x, int y)
4456 {
4457   Feld[x][y] = EL_EMPTY;
4458
4459   MovPos[x][y] = 0;
4460   MovDir[x][y] = 0;
4461   MovDelay[x][y] = 0;
4462
4463 #if USE_NEW_CUSTOM_VALUE
4464   CustomValue[x][y] = 0;
4465 #endif
4466
4467   AmoebaNr[x][y] = 0;
4468   ChangeDelay[x][y] = 0;
4469   ChangePage[x][y] = -1;
4470   Pushed[x][y] = FALSE;
4471
4472 #if 0
4473   ExplodeField[x][y] = EX_TYPE_NONE;
4474 #endif
4475
4476   GfxElement[x][y] = EL_UNDEFINED;
4477   GfxAction[x][y] = ACTION_DEFAULT;
4478   GfxDir[x][y] = MV_NONE;
4479 }
4480
4481 void RemoveMovingField(int x, int y)
4482 {
4483   int oldx = x, oldy = y, newx = x, newy = y;
4484   int element = Feld[x][y];
4485   int next_element = EL_UNDEFINED;
4486
4487   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4488     return;
4489
4490   if (IS_MOVING(x, y))
4491   {
4492     Moving2Blocked(x, y, &newx, &newy);
4493
4494     if (Feld[newx][newy] != EL_BLOCKED)
4495     {
4496       /* element is moving, but target field is not free (blocked), but
4497          already occupied by something different (example: acid pool);
4498          in this case, only remove the moving field, but not the target */
4499
4500       RemoveField(oldx, oldy);
4501
4502       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4503
4504       DrawLevelField(oldx, oldy);
4505
4506       return;
4507     }
4508   }
4509   else if (element == EL_BLOCKED)
4510   {
4511     Blocked2Moving(x, y, &oldx, &oldy);
4512     if (!IS_MOVING(oldx, oldy))
4513       return;
4514   }
4515
4516   if (element == EL_BLOCKED &&
4517       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4518        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4519        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4520        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4521        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4522        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4523     next_element = get_next_element(Feld[oldx][oldy]);
4524
4525   RemoveField(oldx, oldy);
4526   RemoveField(newx, newy);
4527
4528   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4529
4530   if (next_element != EL_UNDEFINED)
4531     Feld[oldx][oldy] = next_element;
4532
4533   DrawLevelField(oldx, oldy);
4534   DrawLevelField(newx, newy);
4535 }
4536
4537 void DrawDynamite(int x, int y)
4538 {
4539   int sx = SCREENX(x), sy = SCREENY(y);
4540   int graphic = el2img(Feld[x][y]);
4541   int frame;
4542
4543   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4544     return;
4545
4546   if (IS_WALKABLE_INSIDE(Back[x][y]))
4547     return;
4548
4549   if (Back[x][y])
4550     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4551   else if (Store[x][y])
4552     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4553
4554   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4555
4556   if (Back[x][y] || Store[x][y])
4557     DrawGraphicThruMask(sx, sy, graphic, frame);
4558   else
4559     DrawGraphic(sx, sy, graphic, frame);
4560 }
4561
4562 void CheckDynamite(int x, int y)
4563 {
4564   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4565   {
4566     MovDelay[x][y]--;
4567
4568     if (MovDelay[x][y] != 0)
4569     {
4570       DrawDynamite(x, y);
4571       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4572
4573       return;
4574     }
4575   }
4576
4577   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4578
4579   Bang(x, y);
4580 }
4581
4582 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4583 {
4584   boolean num_checked_players = 0;
4585   int i;
4586
4587   for (i = 0; i < MAX_PLAYERS; i++)
4588   {
4589     if (stored_player[i].active)
4590     {
4591       int sx = stored_player[i].jx;
4592       int sy = stored_player[i].jy;
4593
4594       if (num_checked_players == 0)
4595       {
4596         *sx1 = *sx2 = sx;
4597         *sy1 = *sy2 = sy;
4598       }
4599       else
4600       {
4601         *sx1 = MIN(*sx1, sx);
4602         *sy1 = MIN(*sy1, sy);
4603         *sx2 = MAX(*sx2, sx);
4604         *sy2 = MAX(*sy2, sy);
4605       }
4606
4607       num_checked_players++;
4608     }
4609   }
4610 }
4611
4612 static boolean checkIfAllPlayersFitToScreen_RND()
4613 {
4614   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4615
4616   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4617
4618   return (sx2 - sx1 < SCR_FIELDX &&
4619           sy2 - sy1 < SCR_FIELDY);
4620 }
4621
4622 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4623 {
4624   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4625
4626   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4627
4628   *sx = (sx1 + sx2) / 2;
4629   *sy = (sy1 + sy2) / 2;
4630 }
4631
4632 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4633                         boolean center_screen, boolean quick_relocation)
4634 {
4635   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4636   boolean no_delay = (tape.warp_forward);
4637   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4638   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4639
4640   if (quick_relocation)
4641   {
4642     int offset = game.scroll_delay_value;
4643
4644     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4645     {
4646       if (!level.shifted_relocation || center_screen)
4647       {
4648         /* quick relocation (without scrolling), with centering of screen */
4649
4650         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4651                     x > SBX_Right + MIDPOSX ? SBX_Right :
4652                     x - MIDPOSX);
4653
4654         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4655                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4656                     y - MIDPOSY);
4657       }
4658       else
4659       {
4660         /* quick relocation (without scrolling), but do not center screen */
4661
4662         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4663                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4664                                old_x - MIDPOSX);
4665
4666         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4667                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4668                                old_y - MIDPOSY);
4669
4670         int offset_x = x + (scroll_x - center_scroll_x);
4671         int offset_y = y + (scroll_y - center_scroll_y);
4672
4673         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4674                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4675                     offset_x - MIDPOSX);
4676
4677         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4678                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4679                     offset_y - MIDPOSY);
4680       }
4681     }
4682     else
4683     {
4684       /* quick relocation (without scrolling), inside visible screen area */
4685
4686       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4687           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4688         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4689
4690       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4691           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4692         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4693
4694       /* don't scroll over playfield boundaries */
4695       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4696         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4697
4698       /* don't scroll over playfield boundaries */
4699       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4700         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4701     }
4702
4703     RedrawPlayfield(TRUE, 0,0,0,0);
4704   }
4705   else
4706   {
4707 #if 1
4708     int scroll_xx, scroll_yy;
4709
4710     if (!level.shifted_relocation || center_screen)
4711     {
4712       /* visible relocation (with scrolling), with centering of screen */
4713
4714       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4715                    x > SBX_Right + MIDPOSX ? SBX_Right :
4716                    x - MIDPOSX);
4717
4718       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4719                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4720                    y - MIDPOSY);
4721     }
4722     else
4723     {
4724       /* visible relocation (with scrolling), but do not center screen */
4725
4726       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4727                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4728                              old_x - MIDPOSX);
4729
4730       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4731                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4732                              old_y - MIDPOSY);
4733
4734       int offset_x = x + (scroll_x - center_scroll_x);
4735       int offset_y = y + (scroll_y - center_scroll_y);
4736
4737       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4738                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4739                    offset_x - MIDPOSX);
4740
4741       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4742                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4743                    offset_y - MIDPOSY);
4744     }
4745
4746 #else
4747
4748     /* visible relocation (with scrolling), with centering of screen */
4749
4750     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4751                      x > SBX_Right + MIDPOSX ? SBX_Right :
4752                      x - MIDPOSX);
4753
4754     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4755                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4756                      y - MIDPOSY);
4757 #endif
4758
4759     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4760
4761     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4762     {
4763       int dx = 0, dy = 0;
4764       int fx = FX, fy = FY;
4765
4766       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4767       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4768
4769       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4770         break;
4771
4772       scroll_x -= dx;
4773       scroll_y -= dy;
4774
4775       fx += dx * TILEX / 2;
4776       fy += dy * TILEY / 2;
4777
4778       ScrollLevel(dx, dy);
4779       DrawAllPlayers();
4780
4781       /* scroll in two steps of half tile size to make things smoother */
4782       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4783       FlushDisplay();
4784       Delay(wait_delay_value);
4785
4786       /* scroll second step to align at full tile size */
4787       BackToFront();
4788       Delay(wait_delay_value);
4789     }
4790
4791     DrawAllPlayers();
4792     BackToFront();
4793     Delay(wait_delay_value);
4794   }
4795 }
4796
4797 void RelocatePlayer(int jx, int jy, int el_player_raw)
4798 {
4799   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4800   int player_nr = GET_PLAYER_NR(el_player);
4801   struct PlayerInfo *player = &stored_player[player_nr];
4802   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4803   boolean no_delay = (tape.warp_forward);
4804   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4805   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4806   int old_jx = player->jx;
4807   int old_jy = player->jy;
4808   int old_element = Feld[old_jx][old_jy];
4809   int element = Feld[jx][jy];
4810   boolean player_relocated = (old_jx != jx || old_jy != jy);
4811
4812   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4813   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4814   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4815   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4816   int leave_side_horiz = move_dir_horiz;
4817   int leave_side_vert  = move_dir_vert;
4818   int enter_side = enter_side_horiz | enter_side_vert;
4819   int leave_side = leave_side_horiz | leave_side_vert;
4820
4821   if (player->GameOver)         /* do not reanimate dead player */
4822     return;
4823
4824   if (!player_relocated)        /* no need to relocate the player */
4825     return;
4826
4827   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4828   {
4829     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4830     DrawLevelField(jx, jy);
4831   }
4832
4833   if (player->present)
4834   {
4835     while (player->MovPos)
4836     {
4837       ScrollPlayer(player, SCROLL_GO_ON);
4838       ScrollScreen(NULL, SCROLL_GO_ON);
4839
4840       AdvanceFrameAndPlayerCounters(player->index_nr);
4841
4842       DrawPlayer(player);
4843
4844       BackToFront();
4845       Delay(wait_delay_value);
4846     }
4847
4848     DrawPlayer(player);         /* needed here only to cleanup last field */
4849     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4850
4851     player->is_moving = FALSE;
4852   }
4853
4854   if (IS_CUSTOM_ELEMENT(old_element))
4855     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4856                                CE_LEFT_BY_PLAYER,
4857                                player->index_bit, leave_side);
4858
4859   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4860                                       CE_PLAYER_LEAVES_X,
4861                                       player->index_bit, leave_side);
4862
4863   Feld[jx][jy] = el_player;
4864   InitPlayerField(jx, jy, el_player, TRUE);
4865
4866   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4867   {
4868     Feld[jx][jy] = element;
4869     InitField(jx, jy, FALSE);
4870   }
4871
4872   /* only visually relocate centered player */
4873   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4874                      FALSE, level.instant_relocation);
4875
4876   TestIfPlayerTouchesBadThing(jx, jy);
4877   TestIfPlayerTouchesCustomElement(jx, jy);
4878
4879   if (IS_CUSTOM_ELEMENT(element))
4880     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4881                                player->index_bit, enter_side);
4882
4883   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4884                                       player->index_bit, enter_side);
4885 }
4886
4887 void Explode(int ex, int ey, int phase, int mode)
4888 {
4889   int x, y;
4890   int last_phase;
4891   int border_element;
4892
4893   /* !!! eliminate this variable !!! */
4894   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4895
4896   if (game.explosions_delayed)
4897   {
4898     ExplodeField[ex][ey] = mode;
4899     return;
4900   }
4901
4902   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4903   {
4904     int center_element = Feld[ex][ey];
4905     int artwork_element, explosion_element;     /* set these values later */
4906
4907 #if 0
4908     /* --- This is only really needed (and now handled) in "Impact()". --- */
4909     /* do not explode moving elements that left the explode field in time */
4910     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4911         center_element == EL_EMPTY &&
4912         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4913       return;
4914 #endif
4915
4916 #if 0
4917     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4918     if (mode == EX_TYPE_NORMAL ||
4919         mode == EX_TYPE_CENTER ||
4920         mode == EX_TYPE_CROSS)
4921       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4922 #endif
4923
4924     /* remove things displayed in background while burning dynamite */
4925     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4926       Back[ex][ey] = 0;
4927
4928     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4929     {
4930       /* put moving element to center field (and let it explode there) */
4931       center_element = MovingOrBlocked2Element(ex, ey);
4932       RemoveMovingField(ex, ey);
4933       Feld[ex][ey] = center_element;
4934     }
4935
4936     /* now "center_element" is finally determined -- set related values now */
4937     artwork_element = center_element;           /* for custom player artwork */
4938     explosion_element = center_element;         /* for custom player artwork */
4939
4940     if (IS_PLAYER(ex, ey))
4941     {
4942       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4943
4944       artwork_element = stored_player[player_nr].artwork_element;
4945
4946       if (level.use_explosion_element[player_nr])
4947       {
4948         explosion_element = level.explosion_element[player_nr];
4949         artwork_element = explosion_element;
4950       }
4951     }
4952
4953 #if 1
4954     if (mode == EX_TYPE_NORMAL ||
4955         mode == EX_TYPE_CENTER ||
4956         mode == EX_TYPE_CROSS)
4957       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4958 #endif
4959
4960     last_phase = element_info[explosion_element].explosion_delay + 1;
4961
4962     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4963     {
4964       int xx = x - ex + 1;
4965       int yy = y - ey + 1;
4966       int element;
4967
4968       if (!IN_LEV_FIELD(x, y) ||
4969           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4970           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4971         continue;
4972
4973       element = Feld[x][y];
4974
4975       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4976       {
4977         element = MovingOrBlocked2Element(x, y);
4978
4979         if (!IS_EXPLOSION_PROOF(element))
4980           RemoveMovingField(x, y);
4981       }
4982
4983       /* indestructible elements can only explode in center (but not flames) */
4984       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4985                                            mode == EX_TYPE_BORDER)) ||
4986           element == EL_FLAMES)
4987         continue;
4988
4989       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4990          behaviour, for example when touching a yamyam that explodes to rocks
4991          with active deadly shield, a rock is created under the player !!! */
4992       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4993 #if 0
4994       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4995           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4996            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4997 #else
4998       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4999 #endif
5000       {
5001         if (IS_ACTIVE_BOMB(element))
5002         {
5003           /* re-activate things under the bomb like gate or penguin */
5004           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5005           Back[x][y] = 0;
5006         }
5007
5008         continue;
5009       }
5010
5011       /* save walkable background elements while explosion on same tile */
5012       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5013           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5014         Back[x][y] = element;
5015
5016       /* ignite explodable elements reached by other explosion */
5017       if (element == EL_EXPLOSION)
5018         element = Store2[x][y];
5019
5020       if (AmoebaNr[x][y] &&
5021           (element == EL_AMOEBA_FULL ||
5022            element == EL_BD_AMOEBA ||
5023            element == EL_AMOEBA_GROWING))
5024       {
5025         AmoebaCnt[AmoebaNr[x][y]]--;
5026         AmoebaCnt2[AmoebaNr[x][y]]--;
5027       }
5028
5029       RemoveField(x, y);
5030
5031       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5032       {
5033         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5034
5035         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5036
5037         if (PLAYERINFO(ex, ey)->use_murphy)
5038           Store[x][y] = EL_EMPTY;
5039       }
5040
5041       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5042          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5043       else if (ELEM_IS_PLAYER(center_element))
5044         Store[x][y] = EL_EMPTY;
5045       else if (center_element == EL_YAMYAM)
5046         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5047       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5048         Store[x][y] = element_info[center_element].content.e[xx][yy];
5049 #if 1
5050       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5051          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5052          otherwise) -- FIX THIS !!! */
5053       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5054         Store[x][y] = element_info[element].content.e[1][1];
5055 #else
5056       else if (!CAN_EXPLODE(element))
5057         Store[x][y] = element_info[element].content.e[1][1];
5058 #endif
5059       else
5060         Store[x][y] = EL_EMPTY;
5061
5062       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5063           center_element == EL_AMOEBA_TO_DIAMOND)
5064         Store2[x][y] = element;
5065
5066       Feld[x][y] = EL_EXPLOSION;
5067       GfxElement[x][y] = artwork_element;
5068
5069       ExplodePhase[x][y] = 1;
5070       ExplodeDelay[x][y] = last_phase;
5071
5072       Stop[x][y] = TRUE;
5073     }
5074
5075     if (center_element == EL_YAMYAM)
5076       game.yamyam_content_nr =
5077         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5078
5079     return;
5080   }
5081
5082   if (Stop[ex][ey])
5083     return;
5084
5085   x = ex;
5086   y = ey;
5087
5088   if (phase == 1)
5089     GfxFrame[x][y] = 0;         /* restart explosion animation */
5090
5091   last_phase = ExplodeDelay[x][y];
5092
5093   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5094
5095 #ifdef DEBUG
5096
5097   /* activate this even in non-DEBUG version until cause for crash in
5098      getGraphicAnimationFrame() (see below) is found and eliminated */
5099
5100 #endif
5101 #if 1
5102
5103 #if 1
5104   /* this can happen if the player leaves an explosion just in time */
5105   if (GfxElement[x][y] == EL_UNDEFINED)
5106     GfxElement[x][y] = EL_EMPTY;
5107 #else
5108   if (GfxElement[x][y] == EL_UNDEFINED)
5109   {
5110     printf("\n\n");
5111     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5112     printf("Explode(): This should never happen!\n");
5113     printf("\n\n");
5114
5115     GfxElement[x][y] = EL_EMPTY;
5116   }
5117 #endif
5118
5119 #endif
5120
5121   border_element = Store2[x][y];
5122   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5123     border_element = StorePlayer[x][y];
5124
5125   if (phase == element_info[border_element].ignition_delay ||
5126       phase == last_phase)
5127   {
5128     boolean border_explosion = FALSE;
5129
5130     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5131         !PLAYER_EXPLOSION_PROTECTED(x, y))
5132     {
5133       KillPlayerUnlessExplosionProtected(x, y);
5134       border_explosion = TRUE;
5135     }
5136     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5137     {
5138       Feld[x][y] = Store2[x][y];
5139       Store2[x][y] = 0;
5140       Bang(x, y);
5141       border_explosion = TRUE;
5142     }
5143     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5144     {
5145       AmoebeUmwandeln(x, y);
5146       Store2[x][y] = 0;
5147       border_explosion = TRUE;
5148     }
5149
5150     /* if an element just explodes due to another explosion (chain-reaction),
5151        do not immediately end the new explosion when it was the last frame of
5152        the explosion (as it would be done in the following "if"-statement!) */
5153     if (border_explosion && phase == last_phase)
5154       return;
5155   }
5156
5157   if (phase == last_phase)
5158   {
5159     int element;
5160
5161     element = Feld[x][y] = Store[x][y];
5162     Store[x][y] = Store2[x][y] = 0;
5163     GfxElement[x][y] = EL_UNDEFINED;
5164
5165     /* player can escape from explosions and might therefore be still alive */
5166     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5167         element <= EL_PLAYER_IS_EXPLODING_4)
5168     {
5169       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5170       int explosion_element = EL_PLAYER_1 + player_nr;
5171       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5172       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5173
5174       if (level.use_explosion_element[player_nr])
5175         explosion_element = level.explosion_element[player_nr];
5176
5177       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5178                     element_info[explosion_element].content.e[xx][yy]);
5179     }
5180
5181     /* restore probably existing indestructible background element */
5182     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5183       element = Feld[x][y] = Back[x][y];
5184     Back[x][y] = 0;
5185
5186     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5187     GfxDir[x][y] = MV_NONE;
5188     ChangeDelay[x][y] = 0;
5189     ChangePage[x][y] = -1;
5190
5191 #if USE_NEW_CUSTOM_VALUE
5192     CustomValue[x][y] = 0;
5193 #endif
5194
5195     InitField_WithBug2(x, y, FALSE);
5196
5197     DrawLevelField(x, y);
5198
5199     TestIfElementTouchesCustomElement(x, y);
5200
5201     if (GFX_CRUMBLED(element))
5202       DrawLevelFieldCrumbledSandNeighbours(x, y);
5203
5204     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5205       StorePlayer[x][y] = 0;
5206
5207     if (ELEM_IS_PLAYER(element))
5208       RelocatePlayer(x, y, element);
5209   }
5210   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5211   {
5212     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5213     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5214
5215     if (phase == delay)
5216       DrawLevelFieldCrumbledSand(x, y);
5217
5218     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5219     {
5220       DrawLevelElement(x, y, Back[x][y]);
5221       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5222     }
5223     else if (IS_WALKABLE_UNDER(Back[x][y]))
5224     {
5225       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5226       DrawLevelElementThruMask(x, y, Back[x][y]);
5227     }
5228     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5229       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5230   }
5231 }
5232
5233 void DynaExplode(int ex, int ey)
5234 {
5235   int i, j;
5236   int dynabomb_element = Feld[ex][ey];
5237   int dynabomb_size = 1;
5238   boolean dynabomb_xl = FALSE;
5239   struct PlayerInfo *player;
5240   static int xy[4][2] =
5241   {
5242     { 0, -1 },
5243     { -1, 0 },
5244     { +1, 0 },
5245     { 0, +1 }
5246   };
5247
5248   if (IS_ACTIVE_BOMB(dynabomb_element))
5249   {
5250     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5251     dynabomb_size = player->dynabomb_size;
5252     dynabomb_xl = player->dynabomb_xl;
5253     player->dynabombs_left++;
5254   }
5255
5256   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5257
5258   for (i = 0; i < NUM_DIRECTIONS; i++)
5259   {
5260     for (j = 1; j <= dynabomb_size; j++)
5261     {
5262       int x = ex + j * xy[i][0];
5263       int y = ey + j * xy[i][1];
5264       int element;
5265
5266       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5267         break;
5268
5269       element = Feld[x][y];
5270
5271       /* do not restart explosions of fields with active bombs */
5272       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5273         continue;
5274
5275       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5276
5277       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5278           !IS_DIGGABLE(element) && !dynabomb_xl)
5279         break;
5280     }
5281   }
5282 }
5283
5284 void Bang(int x, int y)
5285 {
5286   int element = MovingOrBlocked2Element(x, y);
5287   int explosion_type = EX_TYPE_NORMAL;
5288
5289   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5290   {
5291     struct PlayerInfo *player = PLAYERINFO(x, y);
5292
5293     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5294                             player->element_nr);
5295
5296     if (level.use_explosion_element[player->index_nr])
5297     {
5298       int explosion_element = level.explosion_element[player->index_nr];
5299
5300       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5301         explosion_type = EX_TYPE_CROSS;
5302       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5303         explosion_type = EX_TYPE_CENTER;
5304     }
5305   }
5306
5307   switch (element)
5308   {
5309     case EL_BUG:
5310     case EL_SPACESHIP:
5311     case EL_BD_BUTTERFLY:
5312     case EL_BD_FIREFLY:
5313     case EL_YAMYAM:
5314     case EL_DARK_YAMYAM:
5315     case EL_ROBOT:
5316     case EL_PACMAN:
5317     case EL_MOLE:
5318       RaiseScoreElement(element);
5319       break;
5320
5321     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5322     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5323     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5324     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5325     case EL_DYNABOMB_INCREASE_NUMBER:
5326     case EL_DYNABOMB_INCREASE_SIZE:
5327     case EL_DYNABOMB_INCREASE_POWER:
5328       explosion_type = EX_TYPE_DYNA;
5329       break;
5330
5331     case EL_DC_LANDMINE:
5332 #if 0
5333     case EL_EM_EXIT_OPEN:
5334     case EL_EM_STEEL_EXIT_OPEN:
5335 #endif
5336       explosion_type = EX_TYPE_CENTER;
5337       break;
5338
5339     case EL_PENGUIN:
5340     case EL_LAMP:
5341     case EL_LAMP_ACTIVE:
5342     case EL_AMOEBA_TO_DIAMOND:
5343       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5344         explosion_type = EX_TYPE_CENTER;
5345       break;
5346
5347     default:
5348       if (element_info[element].explosion_type == EXPLODES_CROSS)
5349         explosion_type = EX_TYPE_CROSS;
5350       else if (element_info[element].explosion_type == EXPLODES_1X1)
5351         explosion_type = EX_TYPE_CENTER;
5352       break;
5353   }
5354
5355   if (explosion_type == EX_TYPE_DYNA)
5356     DynaExplode(x, y);
5357   else
5358     Explode(x, y, EX_PHASE_START, explosion_type);
5359
5360   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5361 }
5362
5363 void SplashAcid(int x, int y)
5364 {
5365   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5366       (!IN_LEV_FIELD(x - 1, y - 2) ||
5367        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5368     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5369
5370   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5371       (!IN_LEV_FIELD(x + 1, y - 2) ||
5372        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5373     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5374
5375   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5376 }
5377
5378 static void InitBeltMovement()
5379 {
5380   static int belt_base_element[4] =
5381   {
5382     EL_CONVEYOR_BELT_1_LEFT,
5383     EL_CONVEYOR_BELT_2_LEFT,
5384     EL_CONVEYOR_BELT_3_LEFT,
5385     EL_CONVEYOR_BELT_4_LEFT
5386   };
5387   static int belt_base_active_element[4] =
5388   {
5389     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5390     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5391     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5392     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5393   };
5394
5395   int x, y, i, j;
5396
5397   /* set frame order for belt animation graphic according to belt direction */
5398   for (i = 0; i < NUM_BELTS; i++)
5399   {
5400     int belt_nr = i;
5401
5402     for (j = 0; j < NUM_BELT_PARTS; j++)
5403     {
5404       int element = belt_base_active_element[belt_nr] + j;
5405       int graphic = el2img(element);
5406
5407       if (game.belt_dir[i] == MV_LEFT)
5408         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5409       else
5410         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5411     }
5412   }
5413
5414   SCAN_PLAYFIELD(x, y)
5415   {
5416     int element = Feld[x][y];
5417
5418     for (i = 0; i < NUM_BELTS; i++)
5419     {
5420       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5421       {
5422         int e_belt_nr = getBeltNrFromBeltElement(element);
5423         int belt_nr = i;
5424
5425         if (e_belt_nr == belt_nr)
5426         {
5427           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5428
5429           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5430         }
5431       }
5432     }
5433   }
5434 }
5435
5436 static void ToggleBeltSwitch(int x, int y)
5437 {
5438   static int belt_base_element[4] =
5439   {
5440     EL_CONVEYOR_BELT_1_LEFT,
5441     EL_CONVEYOR_BELT_2_LEFT,
5442     EL_CONVEYOR_BELT_3_LEFT,
5443     EL_CONVEYOR_BELT_4_LEFT
5444   };
5445   static int belt_base_active_element[4] =
5446   {
5447     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5448     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5449     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5450     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5451   };
5452   static int belt_base_switch_element[4] =
5453   {
5454     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5455     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5456     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5457     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5458   };
5459   static int belt_move_dir[4] =
5460   {
5461     MV_LEFT,
5462     MV_NONE,
5463     MV_RIGHT,
5464     MV_NONE,
5465   };
5466
5467   int element = Feld[x][y];
5468   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5469   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5470   int belt_dir = belt_move_dir[belt_dir_nr];
5471   int xx, yy, i;
5472
5473   if (!IS_BELT_SWITCH(element))
5474     return;
5475
5476   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5477   game.belt_dir[belt_nr] = belt_dir;
5478
5479   if (belt_dir_nr == 3)
5480     belt_dir_nr = 1;
5481
5482   /* set frame order for belt animation graphic according to belt direction */
5483   for (i = 0; i < NUM_BELT_PARTS; i++)
5484   {
5485     int element = belt_base_active_element[belt_nr] + i;
5486     int graphic = el2img(element);
5487
5488     if (belt_dir == MV_LEFT)
5489       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5490     else
5491       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5492   }
5493
5494   SCAN_PLAYFIELD(xx, yy)
5495   {
5496     int element = Feld[xx][yy];
5497
5498     if (IS_BELT_SWITCH(element))
5499     {
5500       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5501
5502       if (e_belt_nr == belt_nr)
5503       {
5504         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5505         DrawLevelField(xx, yy);
5506       }
5507     }
5508     else if (IS_BELT(element) && belt_dir != MV_NONE)
5509     {
5510       int e_belt_nr = getBeltNrFromBeltElement(element);
5511
5512       if (e_belt_nr == belt_nr)
5513       {
5514         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5515
5516         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5517         DrawLevelField(xx, yy);
5518       }
5519     }
5520     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5521     {
5522       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5523
5524       if (e_belt_nr == belt_nr)
5525       {
5526         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5527
5528         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5529         DrawLevelField(xx, yy);
5530       }
5531     }
5532   }
5533 }
5534
5535 static void ToggleSwitchgateSwitch(int x, int y)
5536 {
5537   int xx, yy;
5538
5539   game.switchgate_pos = !game.switchgate_pos;
5540
5541   SCAN_PLAYFIELD(xx, yy)
5542   {
5543     int element = Feld[xx][yy];
5544
5545 #if !USE_BOTH_SWITCHGATE_SWITCHES
5546     if (element == EL_SWITCHGATE_SWITCH_UP ||
5547         element == EL_SWITCHGATE_SWITCH_DOWN)
5548     {
5549       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5550       DrawLevelField(xx, yy);
5551     }
5552     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5553              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5554     {
5555       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5556       DrawLevelField(xx, yy);
5557     }
5558 #else
5559     if (element == EL_SWITCHGATE_SWITCH_UP)
5560     {
5561       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5562       DrawLevelField(xx, yy);
5563     }
5564     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5565     {
5566       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5567       DrawLevelField(xx, yy);
5568     }
5569     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5570     {
5571       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5572       DrawLevelField(xx, yy);
5573     }
5574     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5575     {
5576       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5577       DrawLevelField(xx, yy);
5578     }
5579 #endif
5580     else if (element == EL_SWITCHGATE_OPEN ||
5581              element == EL_SWITCHGATE_OPENING)
5582     {
5583       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5584
5585       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5586     }
5587     else if (element == EL_SWITCHGATE_CLOSED ||
5588              element == EL_SWITCHGATE_CLOSING)
5589     {
5590       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5591
5592       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5593     }
5594   }
5595 }
5596
5597 static int getInvisibleActiveFromInvisibleElement(int element)
5598 {
5599   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5600           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5601           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5602           element);
5603 }
5604
5605 static int getInvisibleFromInvisibleActiveElement(int element)
5606 {
5607   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5608           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5609           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5610           element);
5611 }
5612
5613 static void RedrawAllLightSwitchesAndInvisibleElements()
5614 {
5615   int x, y;
5616
5617   SCAN_PLAYFIELD(x, y)
5618   {
5619     int element = Feld[x][y];
5620
5621     if (element == EL_LIGHT_SWITCH &&
5622         game.light_time_left > 0)
5623     {
5624       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5625       DrawLevelField(x, y);
5626     }
5627     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5628              game.light_time_left == 0)
5629     {
5630       Feld[x][y] = EL_LIGHT_SWITCH;
5631       DrawLevelField(x, y);
5632     }
5633     else if (element == EL_EMC_DRIPPER &&
5634              game.light_time_left > 0)
5635     {
5636       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5637       DrawLevelField(x, y);
5638     }
5639     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5640              game.light_time_left == 0)
5641     {
5642       Feld[x][y] = EL_EMC_DRIPPER;
5643       DrawLevelField(x, y);
5644     }
5645     else if (element == EL_INVISIBLE_STEELWALL ||
5646              element == EL_INVISIBLE_WALL ||
5647              element == EL_INVISIBLE_SAND)
5648     {
5649       if (game.light_time_left > 0)
5650         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5651
5652       DrawLevelField(x, y);
5653
5654       /* uncrumble neighbour fields, if needed */
5655       if (element == EL_INVISIBLE_SAND)
5656         DrawLevelFieldCrumbledSandNeighbours(x, y);
5657     }
5658     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5659              element == EL_INVISIBLE_WALL_ACTIVE ||
5660              element == EL_INVISIBLE_SAND_ACTIVE)
5661     {
5662       if (game.light_time_left == 0)
5663         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5664
5665       DrawLevelField(x, y);
5666
5667       /* re-crumble neighbour fields, if needed */
5668       if (element == EL_INVISIBLE_SAND)
5669         DrawLevelFieldCrumbledSandNeighbours(x, y);
5670     }
5671   }
5672 }
5673
5674 static void RedrawAllInvisibleElementsForLenses()
5675 {
5676   int x, y;
5677
5678   SCAN_PLAYFIELD(x, y)
5679   {
5680     int element = Feld[x][y];
5681
5682     if (element == EL_EMC_DRIPPER &&
5683         game.lenses_time_left > 0)
5684     {
5685       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5686       DrawLevelField(x, y);
5687     }
5688     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5689              game.lenses_time_left == 0)
5690     {
5691       Feld[x][y] = EL_EMC_DRIPPER;
5692       DrawLevelField(x, y);
5693     }
5694     else if (element == EL_INVISIBLE_STEELWALL ||
5695              element == EL_INVISIBLE_WALL ||
5696              element == EL_INVISIBLE_SAND)
5697     {
5698       if (game.lenses_time_left > 0)
5699         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5700
5701       DrawLevelField(x, y);
5702
5703       /* uncrumble neighbour fields, if needed */
5704       if (element == EL_INVISIBLE_SAND)
5705         DrawLevelFieldCrumbledSandNeighbours(x, y);
5706     }
5707     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5708              element == EL_INVISIBLE_WALL_ACTIVE ||
5709              element == EL_INVISIBLE_SAND_ACTIVE)
5710     {
5711       if (game.lenses_time_left == 0)
5712         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5713
5714       DrawLevelField(x, y);
5715
5716       /* re-crumble neighbour fields, if needed */
5717       if (element == EL_INVISIBLE_SAND)
5718         DrawLevelFieldCrumbledSandNeighbours(x, y);
5719     }
5720   }
5721 }
5722
5723 static void RedrawAllInvisibleElementsForMagnifier()
5724 {
5725   int x, y;
5726
5727   SCAN_PLAYFIELD(x, y)
5728   {
5729     int element = Feld[x][y];
5730
5731     if (element == EL_EMC_FAKE_GRASS &&
5732         game.magnify_time_left > 0)
5733     {
5734       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5735       DrawLevelField(x, y);
5736     }
5737     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5738              game.magnify_time_left == 0)
5739     {
5740       Feld[x][y] = EL_EMC_FAKE_GRASS;
5741       DrawLevelField(x, y);
5742     }
5743     else if (IS_GATE_GRAY(element) &&
5744              game.magnify_time_left > 0)
5745     {
5746       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5747                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5748                     IS_EM_GATE_GRAY(element) ?
5749                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5750                     IS_EMC_GATE_GRAY(element) ?
5751                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5752                     element);
5753       DrawLevelField(x, y);
5754     }
5755     else if (IS_GATE_GRAY_ACTIVE(element) &&
5756              game.magnify_time_left == 0)
5757     {
5758       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5759                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5760                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5761                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5762                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5763                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5764                     element);
5765       DrawLevelField(x, y);
5766     }
5767   }
5768 }
5769
5770 static void ToggleLightSwitch(int x, int y)
5771 {
5772   int element = Feld[x][y];
5773
5774   game.light_time_left =
5775     (element == EL_LIGHT_SWITCH ?
5776      level.time_light * FRAMES_PER_SECOND : 0);
5777
5778   RedrawAllLightSwitchesAndInvisibleElements();
5779 }
5780
5781 static void ActivateTimegateSwitch(int x, int y)
5782 {
5783   int xx, yy;
5784
5785   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5786
5787   SCAN_PLAYFIELD(xx, yy)
5788   {
5789     int element = Feld[xx][yy];
5790
5791     if (element == EL_TIMEGATE_CLOSED ||
5792         element == EL_TIMEGATE_CLOSING)
5793     {
5794       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5795       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5796     }
5797
5798     /*
5799     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5800     {
5801       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5802       DrawLevelField(xx, yy);
5803     }
5804     */
5805
5806   }
5807
5808 #if 1
5809   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5810                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5811 #else
5812   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5813 #endif
5814 }
5815
5816 void Impact(int x, int y)
5817 {
5818   boolean last_line = (y == lev_fieldy - 1);
5819   boolean object_hit = FALSE;
5820   boolean impact = (last_line || object_hit);
5821   int element = Feld[x][y];
5822   int smashed = EL_STEELWALL;
5823
5824   if (!last_line)       /* check if element below was hit */
5825   {
5826     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5827       return;
5828
5829     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5830                                          MovDir[x][y + 1] != MV_DOWN ||
5831                                          MovPos[x][y + 1] <= TILEY / 2));
5832
5833     /* do not smash moving elements that left the smashed field in time */
5834     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5835         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5836       object_hit = FALSE;
5837
5838 #if USE_QUICKSAND_IMPACT_BUGFIX
5839     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5840     {
5841       RemoveMovingField(x, y + 1);
5842       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5843       Feld[x][y + 2] = EL_ROCK;
5844       DrawLevelField(x, y + 2);
5845
5846       object_hit = TRUE;
5847     }
5848
5849     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5850     {
5851       RemoveMovingField(x, y + 1);
5852       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5853       Feld[x][y + 2] = EL_ROCK;
5854       DrawLevelField(x, y + 2);
5855
5856       object_hit = TRUE;
5857     }
5858 #endif
5859
5860     if (object_hit)
5861       smashed = MovingOrBlocked2Element(x, y + 1);
5862
5863     impact = (last_line || object_hit);
5864   }
5865
5866   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5867   {
5868     SplashAcid(x, y + 1);
5869     return;
5870   }
5871
5872   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5873   /* only reset graphic animation if graphic really changes after impact */
5874   if (impact &&
5875       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5876   {
5877     ResetGfxAnimation(x, y);
5878     DrawLevelField(x, y);
5879   }
5880
5881   if (impact && CAN_EXPLODE_IMPACT(element))
5882   {
5883     Bang(x, y);
5884     return;
5885   }
5886   else if (impact && element == EL_PEARL &&
5887            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5888   {
5889     ResetGfxAnimation(x, y);
5890
5891     Feld[x][y] = EL_PEARL_BREAKING;
5892     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5893     return;
5894   }
5895   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5896   {
5897     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5898
5899     return;
5900   }
5901
5902   if (impact && element == EL_AMOEBA_DROP)
5903   {
5904     if (object_hit && IS_PLAYER(x, y + 1))
5905       KillPlayerUnlessEnemyProtected(x, y + 1);
5906     else if (object_hit && smashed == EL_PENGUIN)
5907       Bang(x, y + 1);
5908     else
5909     {
5910       Feld[x][y] = EL_AMOEBA_GROWING;
5911       Store[x][y] = EL_AMOEBA_WET;
5912
5913       ResetRandomAnimationValue(x, y);
5914     }
5915     return;
5916   }
5917
5918   if (object_hit)               /* check which object was hit */
5919   {
5920     if ((CAN_PASS_MAGIC_WALL(element) && 
5921          (smashed == EL_MAGIC_WALL ||
5922           smashed == EL_BD_MAGIC_WALL)) ||
5923         (CAN_PASS_DC_MAGIC_WALL(element) &&
5924          smashed == EL_DC_MAGIC_WALL))
5925     {
5926       int xx, yy;
5927       int activated_magic_wall =
5928         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5929          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5930          EL_DC_MAGIC_WALL_ACTIVE);
5931
5932       /* activate magic wall / mill */
5933       SCAN_PLAYFIELD(xx, yy)
5934       {
5935         if (Feld[xx][yy] == smashed)
5936           Feld[xx][yy] = activated_magic_wall;
5937       }
5938
5939       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5940       game.magic_wall_active = TRUE;
5941
5942       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5943                             SND_MAGIC_WALL_ACTIVATING :
5944                             smashed == EL_BD_MAGIC_WALL ?
5945                             SND_BD_MAGIC_WALL_ACTIVATING :
5946                             SND_DC_MAGIC_WALL_ACTIVATING));
5947     }
5948
5949     if (IS_PLAYER(x, y + 1))
5950     {
5951       if (CAN_SMASH_PLAYER(element))
5952       {
5953         KillPlayerUnlessEnemyProtected(x, y + 1);
5954         return;
5955       }
5956     }
5957     else if (smashed == EL_PENGUIN)
5958     {
5959       if (CAN_SMASH_PLAYER(element))
5960       {
5961         Bang(x, y + 1);
5962         return;
5963       }
5964     }
5965     else if (element == EL_BD_DIAMOND)
5966     {
5967       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5968       {
5969         Bang(x, y + 1);
5970         return;
5971       }
5972     }
5973     else if (((element == EL_SP_INFOTRON ||
5974                element == EL_SP_ZONK) &&
5975               (smashed == EL_SP_SNIKSNAK ||
5976                smashed == EL_SP_ELECTRON ||
5977                smashed == EL_SP_DISK_ORANGE)) ||
5978              (element == EL_SP_INFOTRON &&
5979               smashed == EL_SP_DISK_YELLOW))
5980     {
5981       Bang(x, y + 1);
5982       return;
5983     }
5984     else if (CAN_SMASH_EVERYTHING(element))
5985     {
5986       if (IS_CLASSIC_ENEMY(smashed) ||
5987           CAN_EXPLODE_SMASHED(smashed))
5988       {
5989         Bang(x, y + 1);
5990         return;
5991       }
5992       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5993       {
5994         if (smashed == EL_LAMP ||
5995             smashed == EL_LAMP_ACTIVE)
5996         {
5997           Bang(x, y + 1);
5998           return;
5999         }
6000         else if (smashed == EL_NUT)
6001         {
6002           Feld[x][y + 1] = EL_NUT_BREAKING;
6003           PlayLevelSound(x, y, SND_NUT_BREAKING);
6004           RaiseScoreElement(EL_NUT);
6005           return;
6006         }
6007         else if (smashed == EL_PEARL)
6008         {
6009           ResetGfxAnimation(x, y);
6010
6011           Feld[x][y + 1] = EL_PEARL_BREAKING;
6012           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6013           return;
6014         }
6015         else if (smashed == EL_DIAMOND)
6016         {
6017           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6018           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6019           return;
6020         }
6021         else if (IS_BELT_SWITCH(smashed))
6022         {
6023           ToggleBeltSwitch(x, y + 1);
6024         }
6025         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6026                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6027                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6028                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6029         {
6030           ToggleSwitchgateSwitch(x, y + 1);
6031         }
6032         else if (smashed == EL_LIGHT_SWITCH ||
6033                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6034         {
6035           ToggleLightSwitch(x, y + 1);
6036         }
6037         else
6038         {
6039 #if 0
6040           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6041 #endif
6042
6043           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6044
6045           CheckElementChangeBySide(x, y + 1, smashed, element,
6046                                    CE_SWITCHED, CH_SIDE_TOP);
6047           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6048                                             CH_SIDE_TOP);
6049         }
6050       }
6051       else
6052       {
6053         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6054       }
6055     }
6056   }
6057
6058   /* play sound of magic wall / mill */
6059   if (!last_line &&
6060       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6061        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6062        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6063   {
6064     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6065       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6066     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6067       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6068     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6069       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6070
6071     return;
6072   }
6073
6074   /* play sound of object that hits the ground */
6075   if (last_line || object_hit)
6076     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6077 }
6078
6079 inline static void TurnRoundExt(int x, int y)
6080 {
6081   static struct
6082   {
6083     int dx, dy;
6084   } move_xy[] =
6085   {
6086     {  0,  0 },
6087     { -1,  0 },
6088     { +1,  0 },
6089     {  0,  0 },
6090     {  0, -1 },
6091     {  0,  0 }, { 0, 0 }, { 0, 0 },
6092     {  0, +1 }
6093   };
6094   static struct
6095   {
6096     int left, right, back;
6097   } turn[] =
6098   {
6099     { 0,        0,              0        },
6100     { MV_DOWN,  MV_UP,          MV_RIGHT },
6101     { MV_UP,    MV_DOWN,        MV_LEFT  },
6102     { 0,        0,              0        },
6103     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6104     { 0,        0,              0        },
6105     { 0,        0,              0        },
6106     { 0,        0,              0        },
6107     { MV_RIGHT, MV_LEFT,        MV_UP    }
6108   };
6109
6110   int element = Feld[x][y];
6111   int move_pattern = element_info[element].move_pattern;
6112
6113   int old_move_dir = MovDir[x][y];
6114   int left_dir  = turn[old_move_dir].left;
6115   int right_dir = turn[old_move_dir].right;
6116   int back_dir  = turn[old_move_dir].back;
6117
6118   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6119   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6120   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6121   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6122
6123   int left_x  = x + left_dx,  left_y  = y + left_dy;
6124   int right_x = x + right_dx, right_y = y + right_dy;
6125   int move_x  = x + move_dx,  move_y  = y + move_dy;
6126
6127   int xx, yy;
6128
6129   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6130   {
6131     TestIfBadThingTouchesOtherBadThing(x, y);
6132
6133     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6134       MovDir[x][y] = right_dir;
6135     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6136       MovDir[x][y] = left_dir;
6137
6138     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6139       MovDelay[x][y] = 9;
6140     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6141       MovDelay[x][y] = 1;
6142   }
6143   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6144   {
6145     TestIfBadThingTouchesOtherBadThing(x, y);
6146
6147     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6148       MovDir[x][y] = left_dir;
6149     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6150       MovDir[x][y] = right_dir;
6151
6152     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6153       MovDelay[x][y] = 9;
6154     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6155       MovDelay[x][y] = 1;
6156   }
6157   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6158   {
6159     TestIfBadThingTouchesOtherBadThing(x, y);
6160
6161     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6162       MovDir[x][y] = left_dir;
6163     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6164       MovDir[x][y] = right_dir;
6165
6166     if (MovDir[x][y] != old_move_dir)
6167       MovDelay[x][y] = 9;
6168   }
6169   else if (element == EL_YAMYAM)
6170   {
6171     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6172     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6173
6174     if (can_turn_left && can_turn_right)
6175       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6176     else if (can_turn_left)
6177       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6178     else if (can_turn_right)
6179       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6180     else
6181       MovDir[x][y] = back_dir;
6182
6183     MovDelay[x][y] = 16 + 16 * RND(3);
6184   }
6185   else if (element == EL_DARK_YAMYAM)
6186   {
6187     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6188                                                          left_x, left_y);
6189     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6190                                                          right_x, right_y);
6191
6192     if (can_turn_left && can_turn_right)
6193       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6194     else if (can_turn_left)
6195       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6196     else if (can_turn_right)
6197       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6198     else
6199       MovDir[x][y] = back_dir;
6200
6201     MovDelay[x][y] = 16 + 16 * RND(3);
6202   }
6203   else if (element == EL_PACMAN)
6204   {
6205     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6206     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6207
6208     if (can_turn_left && can_turn_right)
6209       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6210     else if (can_turn_left)
6211       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6212     else if (can_turn_right)
6213       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6214     else
6215       MovDir[x][y] = back_dir;
6216
6217     MovDelay[x][y] = 6 + RND(40);
6218   }
6219   else if (element == EL_PIG)
6220   {
6221     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6222     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6223     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6224     boolean should_turn_left, should_turn_right, should_move_on;
6225     int rnd_value = 24;
6226     int rnd = RND(rnd_value);
6227
6228     should_turn_left = (can_turn_left &&
6229                         (!can_move_on ||
6230                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6231                                                    y + back_dy + left_dy)));
6232     should_turn_right = (can_turn_right &&
6233                          (!can_move_on ||
6234                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6235                                                     y + back_dy + right_dy)));
6236     should_move_on = (can_move_on &&
6237                       (!can_turn_left ||
6238                        !can_turn_right ||
6239                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6240                                                  y + move_dy + left_dy) ||
6241                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6242                                                  y + move_dy + right_dy)));
6243
6244     if (should_turn_left || should_turn_right || should_move_on)
6245     {
6246       if (should_turn_left && should_turn_right && should_move_on)
6247         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6248                         rnd < 2 * rnd_value / 3 ? right_dir :
6249                         old_move_dir);
6250       else if (should_turn_left && should_turn_right)
6251         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6252       else if (should_turn_left && should_move_on)
6253         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6254       else if (should_turn_right && should_move_on)
6255         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6256       else if (should_turn_left)
6257         MovDir[x][y] = left_dir;
6258       else if (should_turn_right)
6259         MovDir[x][y] = right_dir;
6260       else if (should_move_on)
6261         MovDir[x][y] = old_move_dir;
6262     }
6263     else if (can_move_on && rnd > rnd_value / 8)
6264       MovDir[x][y] = old_move_dir;
6265     else if (can_turn_left && can_turn_right)
6266       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6267     else if (can_turn_left && rnd > rnd_value / 8)
6268       MovDir[x][y] = left_dir;
6269     else if (can_turn_right && rnd > rnd_value/8)
6270       MovDir[x][y] = right_dir;
6271     else
6272       MovDir[x][y] = back_dir;
6273
6274     xx = x + move_xy[MovDir[x][y]].dx;
6275     yy = y + move_xy[MovDir[x][y]].dy;
6276
6277     if (!IN_LEV_FIELD(xx, yy) ||
6278         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6279       MovDir[x][y] = old_move_dir;
6280
6281     MovDelay[x][y] = 0;
6282   }
6283   else if (element == EL_DRAGON)
6284   {
6285     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6286     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6287     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6288     int rnd_value = 24;
6289     int rnd = RND(rnd_value);
6290
6291     if (can_move_on && rnd > rnd_value / 8)
6292       MovDir[x][y] = old_move_dir;
6293     else if (can_turn_left && can_turn_right)
6294       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6295     else if (can_turn_left && rnd > rnd_value / 8)
6296       MovDir[x][y] = left_dir;
6297     else if (can_turn_right && rnd > rnd_value / 8)
6298       MovDir[x][y] = right_dir;
6299     else
6300       MovDir[x][y] = back_dir;
6301
6302     xx = x + move_xy[MovDir[x][y]].dx;
6303     yy = y + move_xy[MovDir[x][y]].dy;
6304
6305     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6306       MovDir[x][y] = old_move_dir;
6307
6308     MovDelay[x][y] = 0;
6309   }
6310   else if (element == EL_MOLE)
6311   {
6312     boolean can_move_on =
6313       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6314                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6315                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6316     if (!can_move_on)
6317     {
6318       boolean can_turn_left =
6319         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6320                               IS_AMOEBOID(Feld[left_x][left_y])));
6321
6322       boolean can_turn_right =
6323         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6324                               IS_AMOEBOID(Feld[right_x][right_y])));
6325
6326       if (can_turn_left && can_turn_right)
6327         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6328       else if (can_turn_left)
6329         MovDir[x][y] = left_dir;
6330       else
6331         MovDir[x][y] = right_dir;
6332     }
6333
6334     if (MovDir[x][y] != old_move_dir)
6335       MovDelay[x][y] = 9;
6336   }
6337   else if (element == EL_BALLOON)
6338   {
6339     MovDir[x][y] = game.wind_direction;
6340     MovDelay[x][y] = 0;
6341   }
6342   else if (element == EL_SPRING)
6343   {
6344 #if USE_NEW_SPRING_BUMPER
6345     if (MovDir[x][y] & MV_HORIZONTAL)
6346     {
6347       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6348           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6349       {
6350         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6351         ResetGfxAnimation(move_x, move_y);
6352         DrawLevelField(move_x, move_y);
6353
6354         MovDir[x][y] = back_dir;
6355       }
6356       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6357                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6358         MovDir[x][y] = MV_NONE;
6359     }
6360 #else
6361     if (MovDir[x][y] & MV_HORIZONTAL &&
6362         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6363          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6364       MovDir[x][y] = MV_NONE;
6365 #endif
6366
6367     MovDelay[x][y] = 0;
6368   }
6369   else if (element == EL_ROBOT ||
6370            element == EL_SATELLITE ||
6371            element == EL_PENGUIN ||
6372            element == EL_EMC_ANDROID)
6373   {
6374     int attr_x = -1, attr_y = -1;
6375
6376     if (AllPlayersGone)
6377     {
6378       attr_x = ExitX;
6379       attr_y = ExitY;
6380     }
6381     else
6382     {
6383       int i;
6384
6385       for (i = 0; i < MAX_PLAYERS; i++)
6386       {
6387         struct PlayerInfo *player = &stored_player[i];
6388         int jx = player->jx, jy = player->jy;
6389
6390         if (!player->active)
6391           continue;
6392
6393         if (attr_x == -1 ||
6394             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6395         {
6396           attr_x = jx;
6397           attr_y = jy;
6398         }
6399       }
6400     }
6401
6402     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6403         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6404          game.engine_version < VERSION_IDENT(3,1,0,0)))
6405     {
6406       attr_x = ZX;
6407       attr_y = ZY;
6408     }
6409
6410     if (element == EL_PENGUIN)
6411     {
6412       int i;
6413       static int xy[4][2] =
6414       {
6415         { 0, -1 },
6416         { -1, 0 },
6417         { +1, 0 },
6418         { 0, +1 }
6419       };
6420
6421       for (i = 0; i < NUM_DIRECTIONS; i++)
6422       {
6423         int ex = x + xy[i][0];
6424         int ey = y + xy[i][1];
6425
6426         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6427                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6428                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6429                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6430         {
6431           attr_x = ex;
6432           attr_y = ey;
6433           break;
6434         }
6435       }
6436     }
6437
6438     MovDir[x][y] = MV_NONE;
6439     if (attr_x < x)
6440       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6441     else if (attr_x > x)
6442       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6443     if (attr_y < y)
6444       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6445     else if (attr_y > y)
6446       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6447
6448     if (element == EL_ROBOT)
6449     {
6450       int newx, newy;
6451
6452       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6453         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6454       Moving2Blocked(x, y, &newx, &newy);
6455
6456       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6457         MovDelay[x][y] = 8 + 8 * !RND(3);
6458       else
6459         MovDelay[x][y] = 16;
6460     }
6461     else if (element == EL_PENGUIN)
6462     {
6463       int newx, newy;
6464
6465       MovDelay[x][y] = 1;
6466
6467       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6468       {
6469         boolean first_horiz = RND(2);
6470         int new_move_dir = MovDir[x][y];
6471
6472         MovDir[x][y] =
6473           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6474         Moving2Blocked(x, y, &newx, &newy);
6475
6476         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6477           return;
6478
6479         MovDir[x][y] =
6480           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6481         Moving2Blocked(x, y, &newx, &newy);
6482
6483         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6484           return;
6485
6486         MovDir[x][y] = old_move_dir;
6487         return;
6488       }
6489     }
6490     else if (element == EL_SATELLITE)
6491     {
6492       int newx, newy;
6493
6494       MovDelay[x][y] = 1;
6495
6496       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6497       {
6498         boolean first_horiz = RND(2);
6499         int new_move_dir = MovDir[x][y];
6500
6501         MovDir[x][y] =
6502           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6503         Moving2Blocked(x, y, &newx, &newy);
6504
6505         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6506           return;
6507
6508         MovDir[x][y] =
6509           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6510         Moving2Blocked(x, y, &newx, &newy);
6511
6512         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6513           return;
6514
6515         MovDir[x][y] = old_move_dir;
6516         return;
6517       }
6518     }
6519     else if (element == EL_EMC_ANDROID)
6520     {
6521       static int check_pos[16] =
6522       {
6523         -1,             /*  0 => (invalid)          */
6524         7,              /*  1 => MV_LEFT            */
6525         3,              /*  2 => MV_RIGHT           */
6526         -1,             /*  3 => (invalid)          */
6527         1,              /*  4 =>            MV_UP   */
6528         0,              /*  5 => MV_LEFT  | MV_UP   */
6529         2,              /*  6 => MV_RIGHT | MV_UP   */
6530         -1,             /*  7 => (invalid)          */
6531         5,              /*  8 =>            MV_DOWN */
6532         6,              /*  9 => MV_LEFT  | MV_DOWN */
6533         4,              /* 10 => MV_RIGHT | MV_DOWN */
6534         -1,             /* 11 => (invalid)          */
6535         -1,             /* 12 => (invalid)          */
6536         -1,             /* 13 => (invalid)          */
6537         -1,             /* 14 => (invalid)          */
6538         -1,             /* 15 => (invalid)          */
6539       };
6540       static struct
6541       {
6542         int dx, dy;
6543         int dir;
6544       } check_xy[8] =
6545       {
6546         { -1, -1,       MV_LEFT  | MV_UP   },
6547         {  0, -1,                  MV_UP   },
6548         { +1, -1,       MV_RIGHT | MV_UP   },
6549         { +1,  0,       MV_RIGHT           },
6550         { +1, +1,       MV_RIGHT | MV_DOWN },
6551         {  0, +1,                  MV_DOWN },
6552         { -1, +1,       MV_LEFT  | MV_DOWN },
6553         { -1,  0,       MV_LEFT            },
6554       };
6555       int start_pos, check_order;
6556       boolean can_clone = FALSE;
6557       int i;
6558
6559       /* check if there is any free field around current position */
6560       for (i = 0; i < 8; i++)
6561       {
6562         int newx = x + check_xy[i].dx;
6563         int newy = y + check_xy[i].dy;
6564
6565         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6566         {
6567           can_clone = TRUE;
6568
6569           break;
6570         }
6571       }
6572
6573       if (can_clone)            /* randomly find an element to clone */
6574       {
6575         can_clone = FALSE;
6576
6577         start_pos = check_pos[RND(8)];
6578         check_order = (RND(2) ? -1 : +1);
6579
6580         for (i = 0; i < 8; i++)
6581         {
6582           int pos_raw = start_pos + i * check_order;
6583           int pos = (pos_raw + 8) % 8;
6584           int newx = x + check_xy[pos].dx;
6585           int newy = y + check_xy[pos].dy;
6586
6587           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6588           {
6589             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6590             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6591
6592             Store[x][y] = Feld[newx][newy];
6593
6594             can_clone = TRUE;
6595
6596             break;
6597           }
6598         }
6599       }
6600
6601       if (can_clone)            /* randomly find a direction to move */
6602       {
6603         can_clone = FALSE;
6604
6605         start_pos = check_pos[RND(8)];
6606         check_order = (RND(2) ? -1 : +1);
6607
6608         for (i = 0; i < 8; i++)
6609         {
6610           int pos_raw = start_pos + i * check_order;
6611           int pos = (pos_raw + 8) % 8;
6612           int newx = x + check_xy[pos].dx;
6613           int newy = y + check_xy[pos].dy;
6614           int new_move_dir = check_xy[pos].dir;
6615
6616           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6617           {
6618             MovDir[x][y] = new_move_dir;
6619             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6620
6621             can_clone = TRUE;
6622
6623             break;
6624           }
6625         }
6626       }
6627
6628       if (can_clone)            /* cloning and moving successful */
6629         return;
6630
6631       /* cannot clone -- try to move towards player */
6632
6633       start_pos = check_pos[MovDir[x][y] & 0x0f];
6634       check_order = (RND(2) ? -1 : +1);
6635
6636       for (i = 0; i < 3; i++)
6637       {
6638         /* first check start_pos, then previous/next or (next/previous) pos */
6639         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6640         int pos = (pos_raw + 8) % 8;
6641         int newx = x + check_xy[pos].dx;
6642         int newy = y + check_xy[pos].dy;
6643         int new_move_dir = check_xy[pos].dir;
6644
6645         if (IS_PLAYER(newx, newy))
6646           break;
6647
6648         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6649         {
6650           MovDir[x][y] = new_move_dir;
6651           MovDelay[x][y] = level.android_move_time * 8 + 1;
6652
6653           break;
6654         }
6655       }
6656     }
6657   }
6658   else if (move_pattern == MV_TURNING_LEFT ||
6659            move_pattern == MV_TURNING_RIGHT ||
6660            move_pattern == MV_TURNING_LEFT_RIGHT ||
6661            move_pattern == MV_TURNING_RIGHT_LEFT ||
6662            move_pattern == MV_TURNING_RANDOM ||
6663            move_pattern == MV_ALL_DIRECTIONS)
6664   {
6665     boolean can_turn_left =
6666       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6667     boolean can_turn_right =
6668       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6669
6670     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6671       return;
6672
6673     if (move_pattern == MV_TURNING_LEFT)
6674       MovDir[x][y] = left_dir;
6675     else if (move_pattern == MV_TURNING_RIGHT)
6676       MovDir[x][y] = right_dir;
6677     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6678       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6679     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6680       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6681     else if (move_pattern == MV_TURNING_RANDOM)
6682       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6683                       can_turn_right && !can_turn_left ? right_dir :
6684                       RND(2) ? left_dir : right_dir);
6685     else if (can_turn_left && can_turn_right)
6686       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6687     else if (can_turn_left)
6688       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6689     else if (can_turn_right)
6690       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6691     else
6692       MovDir[x][y] = back_dir;
6693
6694     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6695   }
6696   else if (move_pattern == MV_HORIZONTAL ||
6697            move_pattern == MV_VERTICAL)
6698   {
6699     if (move_pattern & old_move_dir)
6700       MovDir[x][y] = back_dir;
6701     else if (move_pattern == MV_HORIZONTAL)
6702       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6703     else if (move_pattern == MV_VERTICAL)
6704       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6705
6706     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6707   }
6708   else if (move_pattern & MV_ANY_DIRECTION)
6709   {
6710     MovDir[x][y] = move_pattern;
6711     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6712   }
6713   else if (move_pattern & MV_WIND_DIRECTION)
6714   {
6715     MovDir[x][y] = game.wind_direction;
6716     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6717   }
6718   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6719   {
6720     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6721       MovDir[x][y] = left_dir;
6722     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6723       MovDir[x][y] = right_dir;
6724
6725     if (MovDir[x][y] != old_move_dir)
6726       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6727   }
6728   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6729   {
6730     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6731       MovDir[x][y] = right_dir;
6732     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6733       MovDir[x][y] = left_dir;
6734
6735     if (MovDir[x][y] != old_move_dir)
6736       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6737   }
6738   else if (move_pattern == MV_TOWARDS_PLAYER ||
6739            move_pattern == MV_AWAY_FROM_PLAYER)
6740   {
6741     int attr_x = -1, attr_y = -1;
6742     int newx, newy;
6743     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6744
6745     if (AllPlayersGone)
6746     {
6747       attr_x = ExitX;
6748       attr_y = ExitY;
6749     }
6750     else
6751     {
6752       int i;
6753
6754       for (i = 0; i < MAX_PLAYERS; i++)
6755       {
6756         struct PlayerInfo *player = &stored_player[i];
6757         int jx = player->jx, jy = player->jy;
6758
6759         if (!player->active)
6760           continue;
6761
6762         if (attr_x == -1 ||
6763             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6764         {
6765           attr_x = jx;
6766           attr_y = jy;
6767         }
6768       }
6769     }
6770
6771     MovDir[x][y] = MV_NONE;
6772     if (attr_x < x)
6773       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6774     else if (attr_x > x)
6775       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6776     if (attr_y < y)
6777       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6778     else if (attr_y > y)
6779       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6780
6781     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6782
6783     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6784     {
6785       boolean first_horiz = RND(2);
6786       int new_move_dir = MovDir[x][y];
6787
6788       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6789       {
6790         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6791         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6792
6793         return;
6794       }
6795
6796       MovDir[x][y] =
6797         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6798       Moving2Blocked(x, y, &newx, &newy);
6799
6800       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6801         return;
6802
6803       MovDir[x][y] =
6804         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6805       Moving2Blocked(x, y, &newx, &newy);
6806
6807       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6808         return;
6809
6810       MovDir[x][y] = old_move_dir;
6811     }
6812   }
6813   else if (move_pattern == MV_WHEN_PUSHED ||
6814            move_pattern == MV_WHEN_DROPPED)
6815   {
6816     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6817       MovDir[x][y] = MV_NONE;
6818
6819     MovDelay[x][y] = 0;
6820   }
6821   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6822   {
6823     static int test_xy[7][2] =
6824     {
6825       { 0, -1 },
6826       { -1, 0 },
6827       { +1, 0 },
6828       { 0, +1 },
6829       { 0, -1 },
6830       { -1, 0 },
6831       { +1, 0 },
6832     };
6833     static int test_dir[7] =
6834     {
6835       MV_UP,
6836       MV_LEFT,
6837       MV_RIGHT,
6838       MV_DOWN,
6839       MV_UP,
6840       MV_LEFT,
6841       MV_RIGHT,
6842     };
6843     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6844     int move_preference = -1000000;     /* start with very low preference */
6845     int new_move_dir = MV_NONE;
6846     int start_test = RND(4);
6847     int i;
6848
6849     for (i = 0; i < NUM_DIRECTIONS; i++)
6850     {
6851       int move_dir = test_dir[start_test + i];
6852       int move_dir_preference;
6853
6854       xx = x + test_xy[start_test + i][0];
6855       yy = y + test_xy[start_test + i][1];
6856
6857       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6858           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6859       {
6860         new_move_dir = move_dir;
6861
6862         break;
6863       }
6864
6865       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6866         continue;
6867
6868       move_dir_preference = -1 * RunnerVisit[xx][yy];
6869       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6870         move_dir_preference = PlayerVisit[xx][yy];
6871
6872       if (move_dir_preference > move_preference)
6873       {
6874         /* prefer field that has not been visited for the longest time */
6875         move_preference = move_dir_preference;
6876         new_move_dir = move_dir;
6877       }
6878       else if (move_dir_preference == move_preference &&
6879                move_dir == old_move_dir)
6880       {
6881         /* prefer last direction when all directions are preferred equally */
6882         move_preference = move_dir_preference;
6883         new_move_dir = move_dir;
6884       }
6885     }
6886
6887     MovDir[x][y] = new_move_dir;
6888     if (old_move_dir != new_move_dir)
6889       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6890   }
6891 }
6892
6893 static void TurnRound(int x, int y)
6894 {
6895   int direction = MovDir[x][y];
6896
6897   TurnRoundExt(x, y);
6898
6899   GfxDir[x][y] = MovDir[x][y];
6900
6901   if (direction != MovDir[x][y])
6902     GfxFrame[x][y] = 0;
6903
6904   if (MovDelay[x][y])
6905     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6906
6907   ResetGfxFrame(x, y, FALSE);
6908 }
6909
6910 static boolean JustBeingPushed(int x, int y)
6911 {
6912   int i;
6913
6914   for (i = 0; i < MAX_PLAYERS; i++)
6915   {
6916     struct PlayerInfo *player = &stored_player[i];
6917
6918     if (player->active && player->is_pushing && player->MovPos)
6919     {
6920       int next_jx = player->jx + (player->jx - player->last_jx);
6921       int next_jy = player->jy + (player->jy - player->last_jy);
6922
6923       if (x == next_jx && y == next_jy)
6924         return TRUE;
6925     }
6926   }
6927
6928   return FALSE;
6929 }
6930
6931 void StartMoving(int x, int y)
6932 {
6933   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6934   int element = Feld[x][y];
6935
6936   if (Stop[x][y])
6937     return;
6938
6939   if (MovDelay[x][y] == 0)
6940     GfxAction[x][y] = ACTION_DEFAULT;
6941
6942   if (CAN_FALL(element) && y < lev_fieldy - 1)
6943   {
6944     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6945         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6946       if (JustBeingPushed(x, y))
6947         return;
6948
6949     if (element == EL_QUICKSAND_FULL)
6950     {
6951       if (IS_FREE(x, y + 1))
6952       {
6953         InitMovingField(x, y, MV_DOWN);
6954         started_moving = TRUE;
6955
6956         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6957 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6958         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6959           Store[x][y] = EL_ROCK;
6960 #else
6961         Store[x][y] = EL_ROCK;
6962 #endif
6963
6964         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6965       }
6966       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6967       {
6968         if (!MovDelay[x][y])
6969           MovDelay[x][y] = TILEY + 1;
6970
6971         if (MovDelay[x][y])
6972         {
6973           MovDelay[x][y]--;
6974           if (MovDelay[x][y])
6975             return;
6976         }
6977
6978         Feld[x][y] = EL_QUICKSAND_EMPTY;
6979         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6980         Store[x][y + 1] = Store[x][y];
6981         Store[x][y] = 0;
6982
6983         PlayLevelSoundAction(x, y, ACTION_FILLING);
6984       }
6985     }
6986     else if (element == EL_QUICKSAND_FAST_FULL)
6987     {
6988       if (IS_FREE(x, y + 1))
6989       {
6990         InitMovingField(x, y, MV_DOWN);
6991         started_moving = TRUE;
6992
6993         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6994 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6995         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6996           Store[x][y] = EL_ROCK;
6997 #else
6998         Store[x][y] = EL_ROCK;
6999 #endif
7000
7001         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7002       }
7003       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7004       {
7005         if (!MovDelay[x][y])
7006           MovDelay[x][y] = TILEY + 1;
7007
7008         if (MovDelay[x][y])
7009         {
7010           MovDelay[x][y]--;
7011           if (MovDelay[x][y])
7012             return;
7013         }
7014
7015         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7016         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7017         Store[x][y + 1] = Store[x][y];
7018         Store[x][y] = 0;
7019
7020         PlayLevelSoundAction(x, y, ACTION_FILLING);
7021       }
7022     }
7023     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7024              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7025     {
7026       InitMovingField(x, y, MV_DOWN);
7027       started_moving = TRUE;
7028
7029       Feld[x][y] = EL_QUICKSAND_FILLING;
7030       Store[x][y] = element;
7031
7032       PlayLevelSoundAction(x, y, ACTION_FILLING);
7033     }
7034     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7035              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7036     {
7037       InitMovingField(x, y, MV_DOWN);
7038       started_moving = TRUE;
7039
7040       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7041       Store[x][y] = element;
7042
7043       PlayLevelSoundAction(x, y, ACTION_FILLING);
7044     }
7045     else if (element == EL_MAGIC_WALL_FULL)
7046     {
7047       if (IS_FREE(x, y + 1))
7048       {
7049         InitMovingField(x, y, MV_DOWN);
7050         started_moving = TRUE;
7051
7052         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7053         Store[x][y] = EL_CHANGED(Store[x][y]);
7054       }
7055       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7056       {
7057         if (!MovDelay[x][y])
7058           MovDelay[x][y] = TILEY/4 + 1;
7059
7060         if (MovDelay[x][y])
7061         {
7062           MovDelay[x][y]--;
7063           if (MovDelay[x][y])
7064             return;
7065         }
7066
7067         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7068         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7069         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7070         Store[x][y] = 0;
7071       }
7072     }
7073     else if (element == EL_BD_MAGIC_WALL_FULL)
7074     {
7075       if (IS_FREE(x, y + 1))
7076       {
7077         InitMovingField(x, y, MV_DOWN);
7078         started_moving = TRUE;
7079
7080         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7081         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7082       }
7083       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7084       {
7085         if (!MovDelay[x][y])
7086           MovDelay[x][y] = TILEY/4 + 1;
7087
7088         if (MovDelay[x][y])
7089         {
7090           MovDelay[x][y]--;
7091           if (MovDelay[x][y])
7092             return;
7093         }
7094
7095         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7096         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7097         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7098         Store[x][y] = 0;
7099       }
7100     }
7101     else if (element == EL_DC_MAGIC_WALL_FULL)
7102     {
7103       if (IS_FREE(x, y + 1))
7104       {
7105         InitMovingField(x, y, MV_DOWN);
7106         started_moving = TRUE;
7107
7108         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7109         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7110       }
7111       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7112       {
7113         if (!MovDelay[x][y])
7114           MovDelay[x][y] = TILEY/4 + 1;
7115
7116         if (MovDelay[x][y])
7117         {
7118           MovDelay[x][y]--;
7119           if (MovDelay[x][y])
7120             return;
7121         }
7122
7123         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7124         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7125         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7126         Store[x][y] = 0;
7127       }
7128     }
7129     else if ((CAN_PASS_MAGIC_WALL(element) &&
7130               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7131                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7132              (CAN_PASS_DC_MAGIC_WALL(element) &&
7133               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7134
7135     {
7136       InitMovingField(x, y, MV_DOWN);
7137       started_moving = TRUE;
7138
7139       Feld[x][y] =
7140         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7141          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7142          EL_DC_MAGIC_WALL_FILLING);
7143       Store[x][y] = element;
7144     }
7145     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7146     {
7147       SplashAcid(x, y + 1);
7148
7149       InitMovingField(x, y, MV_DOWN);
7150       started_moving = TRUE;
7151
7152       Store[x][y] = EL_ACID;
7153     }
7154     else if (
7155 #if USE_FIX_IMPACT_COLLISION
7156              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7157               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7158 #else
7159              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7160               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7161 #endif
7162              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7163               CAN_FALL(element) && WasJustFalling[x][y] &&
7164               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7165
7166              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7167               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7168               (Feld[x][y + 1] == EL_BLOCKED)))
7169     {
7170       /* this is needed for a special case not covered by calling "Impact()"
7171          from "ContinueMoving()": if an element moves to a tile directly below
7172          another element which was just falling on that tile (which was empty
7173          in the previous frame), the falling element above would just stop
7174          instead of smashing the element below (in previous version, the above
7175          element was just checked for "moving" instead of "falling", resulting
7176          in incorrect smashes caused by horizontal movement of the above
7177          element; also, the case of the player being the element to smash was
7178          simply not covered here... :-/ ) */
7179
7180       CheckCollision[x][y] = 0;
7181       CheckImpact[x][y] = 0;
7182
7183       Impact(x, y);
7184     }
7185     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7186     {
7187       if (MovDir[x][y] == MV_NONE)
7188       {
7189         InitMovingField(x, y, MV_DOWN);
7190         started_moving = TRUE;
7191       }
7192     }
7193     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7194     {
7195       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7196         MovDir[x][y] = MV_DOWN;
7197
7198       InitMovingField(x, y, MV_DOWN);
7199       started_moving = TRUE;
7200     }
7201     else if (element == EL_AMOEBA_DROP)
7202     {
7203       Feld[x][y] = EL_AMOEBA_GROWING;
7204       Store[x][y] = EL_AMOEBA_WET;
7205     }
7206     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7207               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7208              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7209              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7210     {
7211       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7212                                 (IS_FREE(x - 1, y + 1) ||
7213                                  Feld[x - 1][y + 1] == EL_ACID));
7214       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7215                                 (IS_FREE(x + 1, y + 1) ||
7216                                  Feld[x + 1][y + 1] == EL_ACID));
7217       boolean can_fall_any  = (can_fall_left || can_fall_right);
7218       boolean can_fall_both = (can_fall_left && can_fall_right);
7219       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7220
7221 #if USE_NEW_ALL_SLIPPERY
7222       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7223       {
7224         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7225           can_fall_right = FALSE;
7226         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7227           can_fall_left = FALSE;
7228         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7229           can_fall_right = FALSE;
7230         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7231           can_fall_left = FALSE;
7232
7233         can_fall_any  = (can_fall_left || can_fall_right);
7234         can_fall_both = FALSE;
7235       }
7236 #else
7237       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7238       {
7239         if (slippery_type == SLIPPERY_ONLY_LEFT)
7240           can_fall_right = FALSE;
7241         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7242           can_fall_left = FALSE;
7243         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7244           can_fall_right = FALSE;
7245         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7246           can_fall_left = FALSE;
7247
7248         can_fall_any  = (can_fall_left || can_fall_right);
7249         can_fall_both = (can_fall_left && can_fall_right);
7250       }
7251 #endif
7252
7253 #if USE_NEW_ALL_SLIPPERY
7254 #else
7255 #if USE_NEW_SP_SLIPPERY
7256       /* !!! better use the same properties as for custom elements here !!! */
7257       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7258                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7259       {
7260         can_fall_right = FALSE;         /* slip down on left side */
7261         can_fall_both = FALSE;
7262       }
7263 #endif
7264 #endif
7265
7266 #if USE_NEW_ALL_SLIPPERY
7267       if (can_fall_both)
7268       {
7269         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7270           can_fall_right = FALSE;       /* slip down on left side */
7271         else
7272           can_fall_left = !(can_fall_right = RND(2));
7273
7274         can_fall_both = FALSE;
7275       }
7276 #else
7277       if (can_fall_both)
7278       {
7279         if (game.emulation == EMU_BOULDERDASH ||
7280             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7281           can_fall_right = FALSE;       /* slip down on left side */
7282         else
7283           can_fall_left = !(can_fall_right = RND(2));
7284
7285         can_fall_both = FALSE;
7286       }
7287 #endif
7288
7289       if (can_fall_any)
7290       {
7291         /* if not determined otherwise, prefer left side for slipping down */
7292         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7293         started_moving = TRUE;
7294       }
7295     }
7296 #if 0
7297     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7298 #else
7299     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7300 #endif
7301     {
7302       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7303       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7304       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7305       int belt_dir = game.belt_dir[belt_nr];
7306
7307       if ((belt_dir == MV_LEFT  && left_is_free) ||
7308           (belt_dir == MV_RIGHT && right_is_free))
7309       {
7310         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7311
7312         InitMovingField(x, y, belt_dir);
7313         started_moving = TRUE;
7314
7315         Pushed[x][y] = TRUE;
7316         Pushed[nextx][y] = TRUE;
7317
7318         GfxAction[x][y] = ACTION_DEFAULT;
7319       }
7320       else
7321       {
7322         MovDir[x][y] = 0;       /* if element was moving, stop it */
7323       }
7324     }
7325   }
7326
7327   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7328 #if 0
7329   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7330 #else
7331   if (CAN_MOVE(element) && !started_moving)
7332 #endif
7333   {
7334     int move_pattern = element_info[element].move_pattern;
7335     int newx, newy;
7336
7337 #if 0
7338 #if DEBUG
7339     if (MovDir[x][y] == MV_NONE)
7340     {
7341       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7342              x, y, element, element_info[element].token_name);
7343       printf("StartMoving(): This should never happen!\n");
7344     }
7345 #endif
7346 #endif
7347
7348     Moving2Blocked(x, y, &newx, &newy);
7349
7350     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7351       return;
7352
7353     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7354         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7355     {
7356       WasJustMoving[x][y] = 0;
7357       CheckCollision[x][y] = 0;
7358
7359       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7360
7361       if (Feld[x][y] != element)        /* element has changed */
7362         return;
7363     }
7364
7365     if (!MovDelay[x][y])        /* start new movement phase */
7366     {
7367       /* all objects that can change their move direction after each step
7368          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7369
7370       if (element != EL_YAMYAM &&
7371           element != EL_DARK_YAMYAM &&
7372           element != EL_PACMAN &&
7373           !(move_pattern & MV_ANY_DIRECTION) &&
7374           move_pattern != MV_TURNING_LEFT &&
7375           move_pattern != MV_TURNING_RIGHT &&
7376           move_pattern != MV_TURNING_LEFT_RIGHT &&
7377           move_pattern != MV_TURNING_RIGHT_LEFT &&
7378           move_pattern != MV_TURNING_RANDOM)
7379       {
7380         TurnRound(x, y);
7381
7382         if (MovDelay[x][y] && (element == EL_BUG ||
7383                                element == EL_SPACESHIP ||
7384                                element == EL_SP_SNIKSNAK ||
7385                                element == EL_SP_ELECTRON ||
7386                                element == EL_MOLE))
7387           DrawLevelField(x, y);
7388       }
7389     }
7390
7391     if (MovDelay[x][y])         /* wait some time before next movement */
7392     {
7393       MovDelay[x][y]--;
7394
7395       if (element == EL_ROBOT ||
7396           element == EL_YAMYAM ||
7397           element == EL_DARK_YAMYAM)
7398       {
7399         DrawLevelElementAnimationIfNeeded(x, y, element);
7400         PlayLevelSoundAction(x, y, ACTION_WAITING);
7401       }
7402       else if (element == EL_SP_ELECTRON)
7403         DrawLevelElementAnimationIfNeeded(x, y, element);
7404       else if (element == EL_DRAGON)
7405       {
7406         int i;
7407         int dir = MovDir[x][y];
7408         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7409         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7410         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7411                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7412                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7413                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7414         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7415
7416         GfxAction[x][y] = ACTION_ATTACKING;
7417
7418         if (IS_PLAYER(x, y))
7419           DrawPlayerField(x, y);
7420         else
7421           DrawLevelField(x, y);
7422
7423         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7424
7425         for (i = 1; i <= 3; i++)
7426         {
7427           int xx = x + i * dx;
7428           int yy = y + i * dy;
7429           int sx = SCREENX(xx);
7430           int sy = SCREENY(yy);
7431           int flame_graphic = graphic + (i - 1);
7432
7433           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7434             break;
7435
7436           if (MovDelay[x][y])
7437           {
7438             int flamed = MovingOrBlocked2Element(xx, yy);
7439
7440             /* !!! */
7441 #if 0
7442             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7443               Bang(xx, yy);
7444             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7445               RemoveMovingField(xx, yy);
7446             else
7447               RemoveField(xx, yy);
7448 #else
7449             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7450               Bang(xx, yy);
7451             else
7452               RemoveMovingField(xx, yy);
7453 #endif
7454
7455             ChangeDelay[xx][yy] = 0;
7456
7457             Feld[xx][yy] = EL_FLAMES;
7458
7459             if (IN_SCR_FIELD(sx, sy))
7460             {
7461               DrawLevelFieldCrumbledSand(xx, yy);
7462               DrawGraphic(sx, sy, flame_graphic, frame);
7463             }
7464           }
7465           else
7466           {
7467             if (Feld[xx][yy] == EL_FLAMES)
7468               Feld[xx][yy] = EL_EMPTY;
7469             DrawLevelField(xx, yy);
7470           }
7471         }
7472       }
7473
7474       if (MovDelay[x][y])       /* element still has to wait some time */
7475       {
7476         PlayLevelSoundAction(x, y, ACTION_WAITING);
7477
7478         return;
7479       }
7480     }
7481
7482     /* now make next step */
7483
7484     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7485
7486     if (DONT_COLLIDE_WITH(element) &&
7487         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7488         !PLAYER_ENEMY_PROTECTED(newx, newy))
7489     {
7490       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7491
7492       return;
7493     }
7494
7495     else if (CAN_MOVE_INTO_ACID(element) &&
7496              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7497              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7498              (MovDir[x][y] == MV_DOWN ||
7499               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7500     {
7501       SplashAcid(newx, newy);
7502       Store[x][y] = EL_ACID;
7503     }
7504     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7505     {
7506       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7507           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7508           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7509           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7510       {
7511         RemoveField(x, y);
7512         DrawLevelField(x, y);
7513
7514         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7515         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7516           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7517
7518         local_player->friends_still_needed--;
7519         if (!local_player->friends_still_needed &&
7520             !local_player->GameOver && AllPlayersGone)
7521           PlayerWins(local_player);
7522
7523         return;
7524       }
7525       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7526       {
7527         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7528           DrawLevelField(newx, newy);
7529         else
7530           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7531       }
7532       else if (!IS_FREE(newx, newy))
7533       {
7534         GfxAction[x][y] = ACTION_WAITING;
7535
7536         if (IS_PLAYER(x, y))
7537           DrawPlayerField(x, y);
7538         else
7539           DrawLevelField(x, y);
7540
7541         return;
7542       }
7543     }
7544     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7545     {
7546       if (IS_FOOD_PIG(Feld[newx][newy]))
7547       {
7548         if (IS_MOVING(newx, newy))
7549           RemoveMovingField(newx, newy);
7550         else
7551         {
7552           Feld[newx][newy] = EL_EMPTY;
7553           DrawLevelField(newx, newy);
7554         }
7555
7556         PlayLevelSound(x, y, SND_PIG_DIGGING);
7557       }
7558       else if (!IS_FREE(newx, newy))
7559       {
7560         if (IS_PLAYER(x, y))
7561           DrawPlayerField(x, y);
7562         else
7563           DrawLevelField(x, y);
7564
7565         return;
7566       }
7567     }
7568     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7569     {
7570       if (Store[x][y] != EL_EMPTY)
7571       {
7572         boolean can_clone = FALSE;
7573         int xx, yy;
7574
7575         /* check if element to clone is still there */
7576         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7577         {
7578           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7579           {
7580             can_clone = TRUE;
7581
7582             break;
7583           }
7584         }
7585
7586         /* cannot clone or target field not free anymore -- do not clone */
7587         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7588           Store[x][y] = EL_EMPTY;
7589       }
7590
7591       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7592       {
7593         if (IS_MV_DIAGONAL(MovDir[x][y]))
7594         {
7595           int diagonal_move_dir = MovDir[x][y];
7596           int stored = Store[x][y];
7597           int change_delay = 8;
7598           int graphic;
7599
7600           /* android is moving diagonally */
7601
7602           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7603
7604           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7605           GfxElement[x][y] = EL_EMC_ANDROID;
7606           GfxAction[x][y] = ACTION_SHRINKING;
7607           GfxDir[x][y] = diagonal_move_dir;
7608           ChangeDelay[x][y] = change_delay;
7609
7610           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7611                                    GfxDir[x][y]);
7612
7613           DrawLevelGraphicAnimation(x, y, graphic);
7614           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7615
7616           if (Feld[newx][newy] == EL_ACID)
7617           {
7618             SplashAcid(newx, newy);
7619
7620             return;
7621           }
7622
7623           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7624
7625           Store[newx][newy] = EL_EMC_ANDROID;
7626           GfxElement[newx][newy] = EL_EMC_ANDROID;
7627           GfxAction[newx][newy] = ACTION_GROWING;
7628           GfxDir[newx][newy] = diagonal_move_dir;
7629           ChangeDelay[newx][newy] = change_delay;
7630
7631           graphic = el_act_dir2img(GfxElement[newx][newy],
7632                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7633
7634           DrawLevelGraphicAnimation(newx, newy, graphic);
7635           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7636
7637           return;
7638         }
7639         else
7640         {
7641           Feld[newx][newy] = EL_EMPTY;
7642           DrawLevelField(newx, newy);
7643
7644           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7645         }
7646       }
7647       else if (!IS_FREE(newx, newy))
7648       {
7649 #if 0
7650         if (IS_PLAYER(x, y))
7651           DrawPlayerField(x, y);
7652         else
7653           DrawLevelField(x, y);
7654 #endif
7655
7656         return;
7657       }
7658     }
7659     else if (IS_CUSTOM_ELEMENT(element) &&
7660              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7661     {
7662       int new_element = Feld[newx][newy];
7663
7664       if (!IS_FREE(newx, newy))
7665       {
7666         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7667                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7668                       ACTION_BREAKING);
7669
7670         /* no element can dig solid indestructible elements */
7671         if (IS_INDESTRUCTIBLE(new_element) &&
7672             !IS_DIGGABLE(new_element) &&
7673             !IS_COLLECTIBLE(new_element))
7674           return;
7675
7676         if (AmoebaNr[newx][newy] &&
7677             (new_element == EL_AMOEBA_FULL ||
7678              new_element == EL_BD_AMOEBA ||
7679              new_element == EL_AMOEBA_GROWING))
7680         {
7681           AmoebaCnt[AmoebaNr[newx][newy]]--;
7682           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7683         }
7684
7685         if (IS_MOVING(newx, newy))
7686           RemoveMovingField(newx, newy);
7687         else
7688         {
7689           RemoveField(newx, newy);
7690           DrawLevelField(newx, newy);
7691         }
7692
7693         /* if digged element was about to explode, prevent the explosion */
7694         ExplodeField[newx][newy] = EX_TYPE_NONE;
7695
7696         PlayLevelSoundAction(x, y, action);
7697       }
7698
7699       Store[newx][newy] = EL_EMPTY;
7700 #if 1
7701       /* this makes it possible to leave the removed element again */
7702       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7703         Store[newx][newy] = new_element;
7704 #else
7705       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7706       {
7707         int move_leave_element = element_info[element].move_leave_element;
7708
7709         /* this makes it possible to leave the removed element again */
7710         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7711                              new_element : move_leave_element);
7712       }
7713 #endif
7714
7715       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7716       {
7717         RunnerVisit[x][y] = FrameCounter;
7718         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7719       }
7720     }
7721     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7722     {
7723       if (!IS_FREE(newx, newy))
7724       {
7725         if (IS_PLAYER(x, y))
7726           DrawPlayerField(x, y);
7727         else
7728           DrawLevelField(x, y);
7729
7730         return;
7731       }
7732       else
7733       {
7734         boolean wanna_flame = !RND(10);
7735         int dx = newx - x, dy = newy - y;
7736         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7737         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7738         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7739                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7740         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7741                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7742
7743         if ((wanna_flame ||
7744              IS_CLASSIC_ENEMY(element1) ||
7745              IS_CLASSIC_ENEMY(element2)) &&
7746             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7747             element1 != EL_FLAMES && element2 != EL_FLAMES)
7748         {
7749           ResetGfxAnimation(x, y);
7750           GfxAction[x][y] = ACTION_ATTACKING;
7751
7752           if (IS_PLAYER(x, y))
7753             DrawPlayerField(x, y);
7754           else
7755             DrawLevelField(x, y);
7756
7757           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7758
7759           MovDelay[x][y] = 50;
7760
7761           /* !!! */
7762 #if 0
7763           RemoveField(newx, newy);
7764 #endif
7765           Feld[newx][newy] = EL_FLAMES;
7766           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7767           {
7768 #if 0
7769             RemoveField(newx1, newy1);
7770 #endif
7771             Feld[newx1][newy1] = EL_FLAMES;
7772           }
7773           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7774           {
7775 #if 0
7776             RemoveField(newx2, newy2);
7777 #endif
7778             Feld[newx2][newy2] = EL_FLAMES;
7779           }
7780
7781           return;
7782         }
7783       }
7784     }
7785     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7786              Feld[newx][newy] == EL_DIAMOND)
7787     {
7788       if (IS_MOVING(newx, newy))
7789         RemoveMovingField(newx, newy);
7790       else
7791       {
7792         Feld[newx][newy] = EL_EMPTY;
7793         DrawLevelField(newx, newy);
7794       }
7795
7796       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7797     }
7798     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7799              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7800     {
7801       if (AmoebaNr[newx][newy])
7802       {
7803         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7804         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7805             Feld[newx][newy] == EL_BD_AMOEBA)
7806           AmoebaCnt[AmoebaNr[newx][newy]]--;
7807       }
7808
7809 #if 0
7810       /* !!! test !!! */
7811       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7812       {
7813         RemoveMovingField(newx, newy);
7814       }
7815 #else
7816       if (IS_MOVING(newx, newy))
7817       {
7818         RemoveMovingField(newx, newy);
7819       }
7820 #endif
7821       else
7822       {
7823         Feld[newx][newy] = EL_EMPTY;
7824         DrawLevelField(newx, newy);
7825       }
7826
7827       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7828     }
7829     else if ((element == EL_PACMAN || element == EL_MOLE)
7830              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7831     {
7832       if (AmoebaNr[newx][newy])
7833       {
7834         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7835         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7836             Feld[newx][newy] == EL_BD_AMOEBA)
7837           AmoebaCnt[AmoebaNr[newx][newy]]--;
7838       }
7839
7840       if (element == EL_MOLE)
7841       {
7842         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7843         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7844
7845         ResetGfxAnimation(x, y);
7846         GfxAction[x][y] = ACTION_DIGGING;
7847         DrawLevelField(x, y);
7848
7849         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7850
7851         return;                         /* wait for shrinking amoeba */
7852       }
7853       else      /* element == EL_PACMAN */
7854       {
7855         Feld[newx][newy] = EL_EMPTY;
7856         DrawLevelField(newx, newy);
7857         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7858       }
7859     }
7860     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7861              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7862               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7863     {
7864       /* wait for shrinking amoeba to completely disappear */
7865       return;
7866     }
7867     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7868     {
7869       /* object was running against a wall */
7870
7871       TurnRound(x, y);
7872
7873 #if 0
7874       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7875       if (move_pattern & MV_ANY_DIRECTION &&
7876           move_pattern == MovDir[x][y])
7877       {
7878         int blocking_element =
7879           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7880
7881         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7882                                  MovDir[x][y]);
7883
7884         element = Feld[x][y];   /* element might have changed */
7885       }
7886 #endif
7887
7888       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7889         DrawLevelElementAnimation(x, y, element);
7890
7891       if (DONT_TOUCH(element))
7892         TestIfBadThingTouchesPlayer(x, y);
7893
7894       return;
7895     }
7896
7897     InitMovingField(x, y, MovDir[x][y]);
7898
7899     PlayLevelSoundAction(x, y, ACTION_MOVING);
7900   }
7901
7902   if (MovDir[x][y])
7903     ContinueMoving(x, y);
7904 }
7905
7906 void ContinueMoving(int x, int y)
7907 {
7908   int element = Feld[x][y];
7909   struct ElementInfo *ei = &element_info[element];
7910   int direction = MovDir[x][y];
7911   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7912   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7913   int newx = x + dx, newy = y + dy;
7914   int stored = Store[x][y];
7915   int stored_new = Store[newx][newy];
7916   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7917   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7918   boolean last_line = (newy == lev_fieldy - 1);
7919
7920   MovPos[x][y] += getElementMoveStepsize(x, y);
7921
7922   if (pushed_by_player) /* special case: moving object pushed by player */
7923     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7924
7925   if (ABS(MovPos[x][y]) < TILEX)
7926   {
7927 #if 0
7928     int ee = Feld[x][y];
7929     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7930     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7931
7932     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7933            x, y, ABS(MovPos[x][y]),
7934            ee, gg, ff,
7935            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7936 #endif
7937
7938     DrawLevelField(x, y);
7939
7940     return;     /* element is still moving */
7941   }
7942
7943   /* element reached destination field */
7944
7945   Feld[x][y] = EL_EMPTY;
7946   Feld[newx][newy] = element;
7947   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7948
7949   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7950   {
7951     element = Feld[newx][newy] = EL_ACID;
7952   }
7953   else if (element == EL_MOLE)
7954   {
7955     Feld[x][y] = EL_SAND;
7956
7957     DrawLevelFieldCrumbledSandNeighbours(x, y);
7958   }
7959   else if (element == EL_QUICKSAND_FILLING)
7960   {
7961     element = Feld[newx][newy] = get_next_element(element);
7962     Store[newx][newy] = Store[x][y];
7963   }
7964   else if (element == EL_QUICKSAND_EMPTYING)
7965   {
7966     Feld[x][y] = get_next_element(element);
7967     element = Feld[newx][newy] = Store[x][y];
7968   }
7969   else if (element == EL_QUICKSAND_FAST_FILLING)
7970   {
7971     element = Feld[newx][newy] = get_next_element(element);
7972     Store[newx][newy] = Store[x][y];
7973   }
7974   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7975   {
7976     Feld[x][y] = get_next_element(element);
7977     element = Feld[newx][newy] = Store[x][y];
7978   }
7979   else if (element == EL_MAGIC_WALL_FILLING)
7980   {
7981     element = Feld[newx][newy] = get_next_element(element);
7982     if (!game.magic_wall_active)
7983       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7984     Store[newx][newy] = Store[x][y];
7985   }
7986   else if (element == EL_MAGIC_WALL_EMPTYING)
7987   {
7988     Feld[x][y] = get_next_element(element);
7989     if (!game.magic_wall_active)
7990       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7991     element = Feld[newx][newy] = Store[x][y];
7992
7993 #if USE_NEW_CUSTOM_VALUE
7994     InitField(newx, newy, FALSE);
7995 #endif
7996   }
7997   else if (element == EL_BD_MAGIC_WALL_FILLING)
7998   {
7999     element = Feld[newx][newy] = get_next_element(element);
8000     if (!game.magic_wall_active)
8001       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8002     Store[newx][newy] = Store[x][y];
8003   }
8004   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8005   {
8006     Feld[x][y] = get_next_element(element);
8007     if (!game.magic_wall_active)
8008       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8009     element = Feld[newx][newy] = Store[x][y];
8010
8011 #if USE_NEW_CUSTOM_VALUE
8012     InitField(newx, newy, FALSE);
8013 #endif
8014   }
8015   else if (element == EL_DC_MAGIC_WALL_FILLING)
8016   {
8017     element = Feld[newx][newy] = get_next_element(element);
8018     if (!game.magic_wall_active)
8019       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8020     Store[newx][newy] = Store[x][y];
8021   }
8022   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8023   {
8024     Feld[x][y] = get_next_element(element);
8025     if (!game.magic_wall_active)
8026       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8027     element = Feld[newx][newy] = Store[x][y];
8028
8029 #if USE_NEW_CUSTOM_VALUE
8030     InitField(newx, newy, FALSE);
8031 #endif
8032   }
8033   else if (element == EL_AMOEBA_DROPPING)
8034   {
8035     Feld[x][y] = get_next_element(element);
8036     element = Feld[newx][newy] = Store[x][y];
8037   }
8038   else if (element == EL_SOKOBAN_OBJECT)
8039   {
8040     if (Back[x][y])
8041       Feld[x][y] = Back[x][y];
8042
8043     if (Back[newx][newy])
8044       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8045
8046     Back[x][y] = Back[newx][newy] = 0;
8047   }
8048
8049   Store[x][y] = EL_EMPTY;
8050   MovPos[x][y] = 0;
8051   MovDir[x][y] = 0;
8052   MovDelay[x][y] = 0;
8053
8054   MovDelay[newx][newy] = 0;
8055
8056   if (CAN_CHANGE_OR_HAS_ACTION(element))
8057   {
8058     /* copy element change control values to new field */
8059     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8060     ChangePage[newx][newy]  = ChangePage[x][y];
8061     ChangeCount[newx][newy] = ChangeCount[x][y];
8062     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8063   }
8064
8065 #if USE_NEW_CUSTOM_VALUE
8066     CustomValue[newx][newy] = CustomValue[x][y];
8067 #endif
8068
8069   ChangeDelay[x][y] = 0;
8070   ChangePage[x][y] = -1;
8071   ChangeCount[x][y] = 0;
8072   ChangeEvent[x][y] = -1;
8073
8074 #if USE_NEW_CUSTOM_VALUE
8075   CustomValue[x][y] = 0;
8076 #endif
8077
8078   /* copy animation control values to new field */
8079   GfxFrame[newx][newy]  = GfxFrame[x][y];
8080   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8081   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8082   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8083
8084   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8085
8086   /* some elements can leave other elements behind after moving */
8087 #if 1
8088   if (ei->move_leave_element != EL_EMPTY &&
8089       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8090       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8091 #else
8092   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8093       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8094       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8095 #endif
8096   {
8097     int move_leave_element = ei->move_leave_element;
8098
8099 #if 1
8100 #if 1
8101     /* this makes it possible to leave the removed element again */
8102     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8103       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8104 #else
8105     /* this makes it possible to leave the removed element again */
8106     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8107       move_leave_element = stored;
8108 #endif
8109 #else
8110     /* this makes it possible to leave the removed element again */
8111     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8112         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8113       move_leave_element = stored;
8114 #endif
8115
8116     Feld[x][y] = move_leave_element;
8117
8118     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8119       MovDir[x][y] = direction;
8120
8121     InitField(x, y, FALSE);
8122
8123     if (GFX_CRUMBLED(Feld[x][y]))
8124       DrawLevelFieldCrumbledSandNeighbours(x, y);
8125
8126     if (ELEM_IS_PLAYER(move_leave_element))
8127       RelocatePlayer(x, y, move_leave_element);
8128   }
8129
8130   /* do this after checking for left-behind element */
8131   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8132
8133   if (!CAN_MOVE(element) ||
8134       (CAN_FALL(element) && direction == MV_DOWN &&
8135        (element == EL_SPRING ||
8136         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8137         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8138     GfxDir[x][y] = MovDir[newx][newy] = 0;
8139
8140   DrawLevelField(x, y);
8141   DrawLevelField(newx, newy);
8142
8143   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8144
8145   /* prevent pushed element from moving on in pushed direction */
8146   if (pushed_by_player && CAN_MOVE(element) &&
8147       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8148       !(element_info[element].move_pattern & direction))
8149     TurnRound(newx, newy);
8150
8151   /* prevent elements on conveyor belt from moving on in last direction */
8152   if (pushed_by_conveyor && CAN_FALL(element) &&
8153       direction & MV_HORIZONTAL)
8154     MovDir[newx][newy] = 0;
8155
8156   if (!pushed_by_player)
8157   {
8158     int nextx = newx + dx, nexty = newy + dy;
8159     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8160
8161     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8162
8163     if (CAN_FALL(element) && direction == MV_DOWN)
8164       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8165
8166     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8167       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8168
8169 #if USE_FIX_IMPACT_COLLISION
8170     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8171       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8172 #endif
8173   }
8174
8175   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8176   {
8177     TestIfBadThingTouchesPlayer(newx, newy);
8178     TestIfBadThingTouchesFriend(newx, newy);
8179
8180     if (!IS_CUSTOM_ELEMENT(element))
8181       TestIfBadThingTouchesOtherBadThing(newx, newy);
8182   }
8183   else if (element == EL_PENGUIN)
8184     TestIfFriendTouchesBadThing(newx, newy);
8185
8186   /* give the player one last chance (one more frame) to move away */
8187   if (CAN_FALL(element) && direction == MV_DOWN &&
8188       (last_line || (!IS_FREE(x, newy + 1) &&
8189                      (!IS_PLAYER(x, newy + 1) ||
8190                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8191     Impact(x, newy);
8192
8193   if (pushed_by_player && !game.use_change_when_pushing_bug)
8194   {
8195     int push_side = MV_DIR_OPPOSITE(direction);
8196     struct PlayerInfo *player = PLAYERINFO(x, y);
8197
8198     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8199                                player->index_bit, push_side);
8200     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8201                                         player->index_bit, push_side);
8202   }
8203
8204   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8205     MovDelay[newx][newy] = 1;
8206
8207   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8208
8209   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8210
8211 #if 0
8212   if (ChangePage[newx][newy] != -1)             /* delayed change */
8213   {
8214     int page = ChangePage[newx][newy];
8215     struct ElementChangeInfo *change = &ei->change_page[page];
8216
8217     ChangePage[newx][newy] = -1;
8218
8219     if (change->can_change)
8220     {
8221       if (ChangeElement(newx, newy, element, page))
8222       {
8223         if (change->post_change_function)
8224           change->post_change_function(newx, newy);
8225       }
8226     }
8227
8228     if (change->has_action)
8229       ExecuteCustomElementAction(newx, newy, element, page);
8230   }
8231 #endif
8232
8233   TestIfElementHitsCustomElement(newx, newy, direction);
8234   TestIfPlayerTouchesCustomElement(newx, newy);
8235   TestIfElementTouchesCustomElement(newx, newy);
8236
8237   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8238       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8239     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8240                              MV_DIR_OPPOSITE(direction));
8241 }
8242
8243 int AmoebeNachbarNr(int ax, int ay)
8244 {
8245   int i;
8246   int element = Feld[ax][ay];
8247   int group_nr = 0;
8248   static int xy[4][2] =
8249   {
8250     { 0, -1 },
8251     { -1, 0 },
8252     { +1, 0 },
8253     { 0, +1 }
8254   };
8255
8256   for (i = 0; i < NUM_DIRECTIONS; i++)
8257   {
8258     int x = ax + xy[i][0];
8259     int y = ay + xy[i][1];
8260
8261     if (!IN_LEV_FIELD(x, y))
8262       continue;
8263
8264     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8265       group_nr = AmoebaNr[x][y];
8266   }
8267
8268   return group_nr;
8269 }
8270
8271 void AmoebenVereinigen(int ax, int ay)
8272 {
8273   int i, x, y, xx, yy;
8274   int new_group_nr = AmoebaNr[ax][ay];
8275   static int xy[4][2] =
8276   {
8277     { 0, -1 },
8278     { -1, 0 },
8279     { +1, 0 },
8280     { 0, +1 }
8281   };
8282
8283   if (new_group_nr == 0)
8284     return;
8285
8286   for (i = 0; i < NUM_DIRECTIONS; i++)
8287   {
8288     x = ax + xy[i][0];
8289     y = ay + xy[i][1];
8290
8291     if (!IN_LEV_FIELD(x, y))
8292       continue;
8293
8294     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8295          Feld[x][y] == EL_BD_AMOEBA ||
8296          Feld[x][y] == EL_AMOEBA_DEAD) &&
8297         AmoebaNr[x][y] != new_group_nr)
8298     {
8299       int old_group_nr = AmoebaNr[x][y];
8300
8301       if (old_group_nr == 0)
8302         return;
8303
8304       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8305       AmoebaCnt[old_group_nr] = 0;
8306       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8307       AmoebaCnt2[old_group_nr] = 0;
8308
8309       SCAN_PLAYFIELD(xx, yy)
8310       {
8311         if (AmoebaNr[xx][yy] == old_group_nr)
8312           AmoebaNr[xx][yy] = new_group_nr;
8313       }
8314     }
8315   }
8316 }
8317
8318 void AmoebeUmwandeln(int ax, int ay)
8319 {
8320   int i, x, y;
8321
8322   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8323   {
8324     int group_nr = AmoebaNr[ax][ay];
8325
8326 #ifdef DEBUG
8327     if (group_nr == 0)
8328     {
8329       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8330       printf("AmoebeUmwandeln(): This should never happen!\n");
8331       return;
8332     }
8333 #endif
8334
8335     SCAN_PLAYFIELD(x, y)
8336     {
8337       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8338       {
8339         AmoebaNr[x][y] = 0;
8340         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8341       }
8342     }
8343
8344     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8345                             SND_AMOEBA_TURNING_TO_GEM :
8346                             SND_AMOEBA_TURNING_TO_ROCK));
8347     Bang(ax, ay);
8348   }
8349   else
8350   {
8351     static int xy[4][2] =
8352     {
8353       { 0, -1 },
8354       { -1, 0 },
8355       { +1, 0 },
8356       { 0, +1 }
8357     };
8358
8359     for (i = 0; i < NUM_DIRECTIONS; i++)
8360     {
8361       x = ax + xy[i][0];
8362       y = ay + xy[i][1];
8363
8364       if (!IN_LEV_FIELD(x, y))
8365         continue;
8366
8367       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8368       {
8369         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8370                               SND_AMOEBA_TURNING_TO_GEM :
8371                               SND_AMOEBA_TURNING_TO_ROCK));
8372         Bang(x, y);
8373       }
8374     }
8375   }
8376 }
8377
8378 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8379 {
8380   int x, y;
8381   int group_nr = AmoebaNr[ax][ay];
8382   boolean done = FALSE;
8383
8384 #ifdef DEBUG
8385   if (group_nr == 0)
8386   {
8387     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8388     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8389     return;
8390   }
8391 #endif
8392
8393   SCAN_PLAYFIELD(x, y)
8394   {
8395     if (AmoebaNr[x][y] == group_nr &&
8396         (Feld[x][y] == EL_AMOEBA_DEAD ||
8397          Feld[x][y] == EL_BD_AMOEBA ||
8398          Feld[x][y] == EL_AMOEBA_GROWING))
8399     {
8400       AmoebaNr[x][y] = 0;
8401       Feld[x][y] = new_element;
8402       InitField(x, y, FALSE);
8403       DrawLevelField(x, y);
8404       done = TRUE;
8405     }
8406   }
8407
8408   if (done)
8409     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8410                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8411                             SND_BD_AMOEBA_TURNING_TO_GEM));
8412 }
8413
8414 void AmoebeWaechst(int x, int y)
8415 {
8416   static unsigned long sound_delay = 0;
8417   static unsigned long sound_delay_value = 0;
8418
8419   if (!MovDelay[x][y])          /* start new growing cycle */
8420   {
8421     MovDelay[x][y] = 7;
8422
8423     if (DelayReached(&sound_delay, sound_delay_value))
8424     {
8425       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8426       sound_delay_value = 30;
8427     }
8428   }
8429
8430   if (MovDelay[x][y])           /* wait some time before growing bigger */
8431   {
8432     MovDelay[x][y]--;
8433     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8434     {
8435       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8436                                            6 - MovDelay[x][y]);
8437
8438       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8439     }
8440
8441     if (!MovDelay[x][y])
8442     {
8443       Feld[x][y] = Store[x][y];
8444       Store[x][y] = 0;
8445       DrawLevelField(x, y);
8446     }
8447   }
8448 }
8449
8450 void AmoebaDisappearing(int x, int y)
8451 {
8452   static unsigned long sound_delay = 0;
8453   static unsigned long sound_delay_value = 0;
8454
8455   if (!MovDelay[x][y])          /* start new shrinking cycle */
8456   {
8457     MovDelay[x][y] = 7;
8458
8459     if (DelayReached(&sound_delay, sound_delay_value))
8460       sound_delay_value = 30;
8461   }
8462
8463   if (MovDelay[x][y])           /* wait some time before shrinking */
8464   {
8465     MovDelay[x][y]--;
8466     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8467     {
8468       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8469                                            6 - MovDelay[x][y]);
8470
8471       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8472     }
8473
8474     if (!MovDelay[x][y])
8475     {
8476       Feld[x][y] = EL_EMPTY;
8477       DrawLevelField(x, y);
8478
8479       /* don't let mole enter this field in this cycle;
8480          (give priority to objects falling to this field from above) */
8481       Stop[x][y] = TRUE;
8482     }
8483   }
8484 }
8485
8486 void AmoebeAbleger(int ax, int ay)
8487 {
8488   int i;
8489   int element = Feld[ax][ay];
8490   int graphic = el2img(element);
8491   int newax = ax, neway = ay;
8492   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8493   static int xy[4][2] =
8494   {
8495     { 0, -1 },
8496     { -1, 0 },
8497     { +1, 0 },
8498     { 0, +1 }
8499   };
8500
8501   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8502   {
8503     Feld[ax][ay] = EL_AMOEBA_DEAD;
8504     DrawLevelField(ax, ay);
8505     return;
8506   }
8507
8508   if (IS_ANIMATED(graphic))
8509     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8510
8511   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8512     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8513
8514   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8515   {
8516     MovDelay[ax][ay]--;
8517     if (MovDelay[ax][ay])
8518       return;
8519   }
8520
8521   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8522   {
8523     int start = RND(4);
8524     int x = ax + xy[start][0];
8525     int y = ay + xy[start][1];
8526
8527     if (!IN_LEV_FIELD(x, y))
8528       return;
8529
8530     if (IS_FREE(x, y) ||
8531         CAN_GROW_INTO(Feld[x][y]) ||
8532         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8533         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8534     {
8535       newax = x;
8536       neway = y;
8537     }
8538
8539     if (newax == ax && neway == ay)
8540       return;
8541   }
8542   else                          /* normal or "filled" (BD style) amoeba */
8543   {
8544     int start = RND(4);
8545     boolean waiting_for_player = FALSE;
8546
8547     for (i = 0; i < NUM_DIRECTIONS; i++)
8548     {
8549       int j = (start + i) % 4;
8550       int x = ax + xy[j][0];
8551       int y = ay + xy[j][1];
8552
8553       if (!IN_LEV_FIELD(x, y))
8554         continue;
8555
8556       if (IS_FREE(x, y) ||
8557           CAN_GROW_INTO(Feld[x][y]) ||
8558           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8559           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8560       {
8561         newax = x;
8562         neway = y;
8563         break;
8564       }
8565       else if (IS_PLAYER(x, y))
8566         waiting_for_player = TRUE;
8567     }
8568
8569     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8570     {
8571       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8572       {
8573         Feld[ax][ay] = EL_AMOEBA_DEAD;
8574         DrawLevelField(ax, ay);
8575         AmoebaCnt[AmoebaNr[ax][ay]]--;
8576
8577         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8578         {
8579           if (element == EL_AMOEBA_FULL)
8580             AmoebeUmwandeln(ax, ay);
8581           else if (element == EL_BD_AMOEBA)
8582             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8583         }
8584       }
8585       return;
8586     }
8587     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8588     {
8589       /* amoeba gets larger by growing in some direction */
8590
8591       int new_group_nr = AmoebaNr[ax][ay];
8592
8593 #ifdef DEBUG
8594   if (new_group_nr == 0)
8595   {
8596     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8597     printf("AmoebeAbleger(): This should never happen!\n");
8598     return;
8599   }
8600 #endif
8601
8602       AmoebaNr[newax][neway] = new_group_nr;
8603       AmoebaCnt[new_group_nr]++;
8604       AmoebaCnt2[new_group_nr]++;
8605
8606       /* if amoeba touches other amoeba(s) after growing, unify them */
8607       AmoebenVereinigen(newax, neway);
8608
8609       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8610       {
8611         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8612         return;
8613       }
8614     }
8615   }
8616
8617   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8618       (neway == lev_fieldy - 1 && newax != ax))
8619   {
8620     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8621     Store[newax][neway] = element;
8622   }
8623   else if (neway == ay || element == EL_EMC_DRIPPER)
8624   {
8625     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8626
8627     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8628   }
8629   else
8630   {
8631     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8632     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8633     Store[ax][ay] = EL_AMOEBA_DROP;
8634     ContinueMoving(ax, ay);
8635     return;
8636   }
8637
8638   DrawLevelField(newax, neway);
8639 }
8640
8641 void Life(int ax, int ay)
8642 {
8643   int x1, y1, x2, y2;
8644   int life_time = 40;
8645   int element = Feld[ax][ay];
8646   int graphic = el2img(element);
8647   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8648                          level.biomaze);
8649   boolean changed = FALSE;
8650
8651   if (IS_ANIMATED(graphic))
8652     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8653
8654   if (Stop[ax][ay])
8655     return;
8656
8657   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8658     MovDelay[ax][ay] = life_time;
8659
8660   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8661   {
8662     MovDelay[ax][ay]--;
8663     if (MovDelay[ax][ay])
8664       return;
8665   }
8666
8667   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8668   {
8669     int xx = ax+x1, yy = ay+y1;
8670     int nachbarn = 0;
8671
8672     if (!IN_LEV_FIELD(xx, yy))
8673       continue;
8674
8675     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8676     {
8677       int x = xx+x2, y = yy+y2;
8678
8679       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8680         continue;
8681
8682       if (((Feld[x][y] == element ||
8683             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8684            !Stop[x][y]) ||
8685           (IS_FREE(x, y) && Stop[x][y]))
8686         nachbarn++;
8687     }
8688
8689     if (xx == ax && yy == ay)           /* field in the middle */
8690     {
8691       if (nachbarn < life_parameter[0] ||
8692           nachbarn > life_parameter[1])
8693       {
8694         Feld[xx][yy] = EL_EMPTY;
8695         if (!Stop[xx][yy])
8696           DrawLevelField(xx, yy);
8697         Stop[xx][yy] = TRUE;
8698         changed = TRUE;
8699       }
8700     }
8701     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8702     {                                   /* free border field */
8703       if (nachbarn >= life_parameter[2] &&
8704           nachbarn <= life_parameter[3])
8705       {
8706         Feld[xx][yy] = element;
8707         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8708         if (!Stop[xx][yy])
8709           DrawLevelField(xx, yy);
8710         Stop[xx][yy] = TRUE;
8711         changed = TRUE;
8712       }
8713     }
8714   }
8715
8716   if (changed)
8717     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8718                    SND_GAME_OF_LIFE_GROWING);
8719 }
8720
8721 static void InitRobotWheel(int x, int y)
8722 {
8723   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8724 }
8725
8726 static void RunRobotWheel(int x, int y)
8727 {
8728   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8729 }
8730
8731 static void StopRobotWheel(int x, int y)
8732 {
8733   if (ZX == x && ZY == y)
8734     ZX = ZY = -1;
8735 }
8736
8737 static void InitTimegateWheel(int x, int y)
8738 {
8739   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8740 }
8741
8742 static void RunTimegateWheel(int x, int y)
8743 {
8744   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8745 }
8746
8747 static void InitMagicBallDelay(int x, int y)
8748 {
8749 #if 1
8750   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8751 #else
8752   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8753 #endif
8754 }
8755
8756 static void ActivateMagicBall(int bx, int by)
8757 {
8758   int x, y;
8759
8760   if (level.ball_random)
8761   {
8762     int pos_border = RND(8);    /* select one of the eight border elements */
8763     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8764     int xx = pos_content % 3;
8765     int yy = pos_content / 3;
8766
8767     x = bx - 1 + xx;
8768     y = by - 1 + yy;
8769
8770     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8771       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8772   }
8773   else
8774   {
8775     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8776     {
8777       int xx = x - bx + 1;
8778       int yy = y - by + 1;
8779
8780       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8781         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8782     }
8783   }
8784
8785   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8786 }
8787
8788 void CheckExit(int x, int y)
8789 {
8790   if (local_player->gems_still_needed > 0 ||
8791       local_player->sokobanfields_still_needed > 0 ||
8792       local_player->lights_still_needed > 0)
8793   {
8794     int element = Feld[x][y];
8795     int graphic = el2img(element);
8796
8797     if (IS_ANIMATED(graphic))
8798       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8799
8800     return;
8801   }
8802
8803   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8804     return;
8805
8806   Feld[x][y] = EL_EXIT_OPENING;
8807
8808   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8809 }
8810
8811 void CheckExitEM(int x, int y)
8812 {
8813   if (local_player->gems_still_needed > 0 ||
8814       local_player->sokobanfields_still_needed > 0 ||
8815       local_player->lights_still_needed > 0)
8816   {
8817     int element = Feld[x][y];
8818     int graphic = el2img(element);
8819
8820     if (IS_ANIMATED(graphic))
8821       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8822
8823     return;
8824   }
8825
8826   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8827     return;
8828
8829   Feld[x][y] = EL_EM_EXIT_OPENING;
8830
8831   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8832 }
8833
8834 void CheckExitSteel(int x, int y)
8835 {
8836   if (local_player->gems_still_needed > 0 ||
8837       local_player->sokobanfields_still_needed > 0 ||
8838       local_player->lights_still_needed > 0)
8839   {
8840     int element = Feld[x][y];
8841     int graphic = el2img(element);
8842
8843     if (IS_ANIMATED(graphic))
8844       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8845
8846     return;
8847   }
8848
8849   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8850     return;
8851
8852   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8853
8854   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8855 }
8856
8857 void CheckExitSteelEM(int x, int y)
8858 {
8859   if (local_player->gems_still_needed > 0 ||
8860       local_player->sokobanfields_still_needed > 0 ||
8861       local_player->lights_still_needed > 0)
8862   {
8863     int element = Feld[x][y];
8864     int graphic = el2img(element);
8865
8866     if (IS_ANIMATED(graphic))
8867       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8868
8869     return;
8870   }
8871
8872   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8873     return;
8874
8875   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8876
8877   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8878 }
8879
8880 void CheckExitSP(int x, int y)
8881 {
8882   if (local_player->gems_still_needed > 0)
8883   {
8884     int element = Feld[x][y];
8885     int graphic = el2img(element);
8886
8887     if (IS_ANIMATED(graphic))
8888       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8889
8890     return;
8891   }
8892
8893   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8894     return;
8895
8896   Feld[x][y] = EL_SP_EXIT_OPENING;
8897
8898   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8899 }
8900
8901 static void CloseAllOpenTimegates()
8902 {
8903   int x, y;
8904
8905   SCAN_PLAYFIELD(x, y)
8906   {
8907     int element = Feld[x][y];
8908
8909     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8910     {
8911       Feld[x][y] = EL_TIMEGATE_CLOSING;
8912
8913       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8914     }
8915   }
8916 }
8917
8918 void DrawTwinkleOnField(int x, int y)
8919 {
8920   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8921     return;
8922
8923   if (Feld[x][y] == EL_BD_DIAMOND)
8924     return;
8925
8926   if (MovDelay[x][y] == 0)      /* next animation frame */
8927     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8928
8929   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8930   {
8931     MovDelay[x][y]--;
8932
8933     if (setup.direct_draw && MovDelay[x][y])
8934       SetDrawtoField(DRAW_BUFFERED);
8935
8936     DrawLevelElementAnimation(x, y, Feld[x][y]);
8937
8938     if (MovDelay[x][y] != 0)
8939     {
8940       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8941                                            10 - MovDelay[x][y]);
8942
8943       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8944
8945       if (setup.direct_draw)
8946       {
8947         int dest_x, dest_y;
8948
8949         dest_x = FX + SCREENX(x) * TILEX;
8950         dest_y = FY + SCREENY(y) * TILEY;
8951
8952         BlitBitmap(drawto_field, window,
8953                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8954         SetDrawtoField(DRAW_DIRECT);
8955       }
8956     }
8957   }
8958 }
8959
8960 void MauerWaechst(int x, int y)
8961 {
8962   int delay = 6;
8963
8964   if (!MovDelay[x][y])          /* next animation frame */
8965     MovDelay[x][y] = 3 * delay;
8966
8967   if (MovDelay[x][y])           /* wait some time before next frame */
8968   {
8969     MovDelay[x][y]--;
8970
8971     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8972     {
8973       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8975
8976       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8977     }
8978
8979     if (!MovDelay[x][y])
8980     {
8981       if (MovDir[x][y] == MV_LEFT)
8982       {
8983         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984           DrawLevelField(x - 1, y);
8985       }
8986       else if (MovDir[x][y] == MV_RIGHT)
8987       {
8988         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989           DrawLevelField(x + 1, y);
8990       }
8991       else if (MovDir[x][y] == MV_UP)
8992       {
8993         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994           DrawLevelField(x, y - 1);
8995       }
8996       else
8997       {
8998         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999           DrawLevelField(x, y + 1);
9000       }
9001
9002       Feld[x][y] = Store[x][y];
9003       Store[x][y] = 0;
9004       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005       DrawLevelField(x, y);
9006     }
9007   }
9008 }
9009
9010 void MauerAbleger(int ax, int ay)
9011 {
9012   int element = Feld[ax][ay];
9013   int graphic = el2img(element);
9014   boolean oben_frei = FALSE, unten_frei = FALSE;
9015   boolean links_frei = FALSE, rechts_frei = FALSE;
9016   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018   boolean new_wall = FALSE;
9019
9020   if (IS_ANIMATED(graphic))
9021     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9022
9023   if (!MovDelay[ax][ay])        /* start building new wall */
9024     MovDelay[ax][ay] = 6;
9025
9026   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9027   {
9028     MovDelay[ax][ay]--;
9029     if (MovDelay[ax][ay])
9030       return;
9031   }
9032
9033   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9034     oben_frei = TRUE;
9035   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9036     unten_frei = TRUE;
9037   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9038     links_frei = TRUE;
9039   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9040     rechts_frei = TRUE;
9041
9042   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043       element == EL_EXPANDABLE_WALL_ANY)
9044   {
9045     if (oben_frei)
9046     {
9047       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048       Store[ax][ay-1] = element;
9049       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9053       new_wall = TRUE;
9054     }
9055     if (unten_frei)
9056     {
9057       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058       Store[ax][ay+1] = element;
9059       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9063       new_wall = TRUE;
9064     }
9065   }
9066
9067   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068       element == EL_EXPANDABLE_WALL_ANY ||
9069       element == EL_EXPANDABLE_WALL ||
9070       element == EL_BD_EXPANDABLE_WALL)
9071   {
9072     if (links_frei)
9073     {
9074       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075       Store[ax-1][ay] = element;
9076       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9080       new_wall = TRUE;
9081     }
9082
9083     if (rechts_frei)
9084     {
9085       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086       Store[ax+1][ay] = element;
9087       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9091       new_wall = TRUE;
9092     }
9093   }
9094
9095   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096     DrawLevelField(ax, ay);
9097
9098   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9099     oben_massiv = TRUE;
9100   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101     unten_massiv = TRUE;
9102   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103     links_massiv = TRUE;
9104   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105     rechts_massiv = TRUE;
9106
9107   if (((oben_massiv && unten_massiv) ||
9108        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109        element == EL_EXPANDABLE_WALL) &&
9110       ((links_massiv && rechts_massiv) ||
9111        element == EL_EXPANDABLE_WALL_VERTICAL))
9112     Feld[ax][ay] = EL_WALL;
9113
9114   if (new_wall)
9115     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9116 }
9117
9118 void MauerAblegerStahl(int ax, int ay)
9119 {
9120   int element = Feld[ax][ay];
9121   int graphic = el2img(element);
9122   boolean oben_frei = FALSE, unten_frei = FALSE;
9123   boolean links_frei = FALSE, rechts_frei = FALSE;
9124   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126   boolean new_wall = FALSE;
9127
9128   if (IS_ANIMATED(graphic))
9129     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9130
9131   if (!MovDelay[ax][ay])        /* start building new wall */
9132     MovDelay[ax][ay] = 6;
9133
9134   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9135   {
9136     MovDelay[ax][ay]--;
9137     if (MovDelay[ax][ay])
9138       return;
9139   }
9140
9141   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9142     oben_frei = TRUE;
9143   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9144     unten_frei = TRUE;
9145   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9146     links_frei = TRUE;
9147   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9148     rechts_frei = TRUE;
9149
9150   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151       element == EL_EXPANDABLE_STEELWALL_ANY)
9152   {
9153     if (oben_frei)
9154     {
9155       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156       Store[ax][ay-1] = element;
9157       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9161       new_wall = TRUE;
9162     }
9163     if (unten_frei)
9164     {
9165       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166       Store[ax][ay+1] = element;
9167       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9171       new_wall = TRUE;
9172     }
9173   }
9174
9175   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176       element == EL_EXPANDABLE_STEELWALL_ANY)
9177   {
9178     if (links_frei)
9179     {
9180       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax-1][ay] = element;
9182       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9186       new_wall = TRUE;
9187     }
9188
9189     if (rechts_frei)
9190     {
9191       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192       Store[ax+1][ay] = element;
9193       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9197       new_wall = TRUE;
9198     }
9199   }
9200
9201   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9202     oben_massiv = TRUE;
9203   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204     unten_massiv = TRUE;
9205   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206     links_massiv = TRUE;
9207   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208     rechts_massiv = TRUE;
9209
9210   if (((oben_massiv && unten_massiv) ||
9211        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212       ((links_massiv && rechts_massiv) ||
9213        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214     Feld[ax][ay] = EL_WALL;
9215
9216   if (new_wall)
9217     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9218 }
9219
9220 void CheckForDragon(int x, int y)
9221 {
9222   int i, j;
9223   boolean dragon_found = FALSE;
9224   static int xy[4][2] =
9225   {
9226     { 0, -1 },
9227     { -1, 0 },
9228     { +1, 0 },
9229     { 0, +1 }
9230   };
9231
9232   for (i = 0; i < NUM_DIRECTIONS; i++)
9233   {
9234     for (j = 0; j < 4; j++)
9235     {
9236       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237
9238       if (IN_LEV_FIELD(xx, yy) &&
9239           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9240       {
9241         if (Feld[xx][yy] == EL_DRAGON)
9242           dragon_found = TRUE;
9243       }
9244       else
9245         break;
9246     }
9247   }
9248
9249   if (!dragon_found)
9250   {
9251     for (i = 0; i < NUM_DIRECTIONS; i++)
9252     {
9253       for (j = 0; j < 3; j++)
9254       {
9255         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9256   
9257         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9258         {
9259           Feld[xx][yy] = EL_EMPTY;
9260           DrawLevelField(xx, yy);
9261         }
9262         else
9263           break;
9264       }
9265     }
9266   }
9267 }
9268
9269 static void InitBuggyBase(int x, int y)
9270 {
9271   int element = Feld[x][y];
9272   int activating_delay = FRAMES_PER_SECOND / 4;
9273
9274   ChangeDelay[x][y] =
9275     (element == EL_SP_BUGGY_BASE ?
9276      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9278      activating_delay :
9279      element == EL_SP_BUGGY_BASE_ACTIVE ?
9280      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9281 }
9282
9283 static void WarnBuggyBase(int x, int y)
9284 {
9285   int i;
9286   static int xy[4][2] =
9287   {
9288     { 0, -1 },
9289     { -1, 0 },
9290     { +1, 0 },
9291     { 0, +1 }
9292   };
9293
9294   for (i = 0; i < NUM_DIRECTIONS; i++)
9295   {
9296     int xx = x + xy[i][0];
9297     int yy = y + xy[i][1];
9298
9299     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9300     {
9301       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9302
9303       break;
9304     }
9305   }
9306 }
9307
9308 static void InitTrap(int x, int y)
9309 {
9310   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9311 }
9312
9313 static void ActivateTrap(int x, int y)
9314 {
9315   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9316 }
9317
9318 static void ChangeActiveTrap(int x, int y)
9319 {
9320   int graphic = IMG_TRAP_ACTIVE;
9321
9322   /* if new animation frame was drawn, correct crumbled sand border */
9323   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324     DrawLevelFieldCrumbledSand(x, y);
9325 }
9326
9327 static int getSpecialActionElement(int element, int number, int base_element)
9328 {
9329   return (element != EL_EMPTY ? element :
9330           number != -1 ? base_element + number - 1 :
9331           EL_EMPTY);
9332 }
9333
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335                                    int value_min, int value_max)
9336 {
9337   int value_new = (operator == CA_MODE_SET      ? operand :
9338                    operator == CA_MODE_ADD      ? value_old + operand :
9339                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9340                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9341                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9342                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9343                    value_old);
9344
9345   return (value_new < value_min ? value_min :
9346           value_new > value_max ? value_max :
9347           value_new);
9348 }
9349
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9351 {
9352   struct ElementInfo *ei = &element_info[element];
9353   struct ElementChangeInfo *change = &ei->change_page[page];
9354   int target_element = change->target_element;
9355   int action_type = change->action_type;
9356   int action_mode = change->action_mode;
9357   int action_arg = change->action_arg;
9358   int i;
9359
9360   if (!change->has_action)
9361     return;
9362
9363   /* ---------- determine action paramater values -------------------------- */
9364
9365   int level_time_value =
9366     (level.time > 0 ? TimeLeft :
9367      TimePlayed);
9368
9369   int action_arg_element =
9370     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9371      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9372      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9373      EL_EMPTY);
9374
9375   int action_arg_direction =
9376     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9377      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9378      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9379      change->actual_trigger_side :
9380      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9381      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9382      MV_NONE);
9383
9384   int action_arg_number_min =
9385     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9386      CA_ARG_MIN);
9387
9388   int action_arg_number_max =
9389     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9390      action_type == CA_SET_LEVEL_GEMS ? 999 :
9391      action_type == CA_SET_LEVEL_TIME ? 9999 :
9392      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9393      action_type == CA_SET_CE_VALUE ? 9999 :
9394      action_type == CA_SET_CE_SCORE ? 9999 :
9395      CA_ARG_MAX);
9396
9397   int action_arg_number_reset =
9398     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9399      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9400      action_type == CA_SET_LEVEL_TIME ? level.time :
9401      action_type == CA_SET_LEVEL_SCORE ? 0 :
9402 #if USE_NEW_CUSTOM_VALUE
9403      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9404 #else
9405      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9406 #endif
9407      action_type == CA_SET_CE_SCORE ? 0 :
9408      0);
9409
9410   int action_arg_number =
9411     (action_arg <= CA_ARG_MAX ? action_arg :
9412      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9413      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9414      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9415      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9416      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9417      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9418 #if USE_NEW_CUSTOM_VALUE
9419      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9420 #else
9421      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9422 #endif
9423      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9424      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9425      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9426      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9427      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9428      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9429      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9430      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9431      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9432      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9433      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9434      -1);
9435
9436   int action_arg_number_old =
9437     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9438      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9439      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9440      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9441      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9442      0);
9443
9444   int action_arg_number_new =
9445     getModifiedActionNumber(action_arg_number_old,
9446                             action_mode, action_arg_number,
9447                             action_arg_number_min, action_arg_number_max);
9448
9449   int trigger_player_bits =
9450     (change->actual_trigger_player >= EL_PLAYER_1 &&
9451      change->actual_trigger_player <= EL_PLAYER_4 ?
9452      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9453      PLAYER_BITS_ANY);
9454
9455   int action_arg_player_bits =
9456     (action_arg >= CA_ARG_PLAYER_1 &&
9457      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9458      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9459      PLAYER_BITS_ANY);
9460
9461   /* ---------- execute action  -------------------------------------------- */
9462
9463   switch (action_type)
9464   {
9465     case CA_NO_ACTION:
9466     {
9467       return;
9468     }
9469
9470     /* ---------- level actions  ------------------------------------------- */
9471
9472     case CA_RESTART_LEVEL:
9473     {
9474       game.restart_level = TRUE;
9475
9476       break;
9477     }
9478
9479     case CA_SHOW_ENVELOPE:
9480     {
9481       int element = getSpecialActionElement(action_arg_element,
9482                                             action_arg_number, EL_ENVELOPE_1);
9483
9484       if (IS_ENVELOPE(element))
9485         local_player->show_envelope = element;
9486
9487       break;
9488     }
9489
9490     case CA_SET_LEVEL_TIME:
9491     {
9492       if (level.time > 0)       /* only modify limited time value */
9493       {
9494         TimeLeft = action_arg_number_new;
9495
9496 #if 1
9497         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9498
9499         DisplayGameControlValues();
9500 #else
9501         DrawGameValue_Time(TimeLeft);
9502 #endif
9503
9504         if (!TimeLeft && setup.time_limit)
9505           for (i = 0; i < MAX_PLAYERS; i++)
9506             KillPlayer(&stored_player[i]);
9507       }
9508
9509       break;
9510     }
9511
9512     case CA_SET_LEVEL_SCORE:
9513     {
9514       local_player->score = action_arg_number_new;
9515
9516 #if 1
9517       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9518
9519       DisplayGameControlValues();
9520 #else
9521       DrawGameValue_Score(local_player->score);
9522 #endif
9523
9524       break;
9525     }
9526
9527     case CA_SET_LEVEL_GEMS:
9528     {
9529       local_player->gems_still_needed = action_arg_number_new;
9530
9531 #if 1
9532       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9533
9534       DisplayGameControlValues();
9535 #else
9536       DrawGameValue_Emeralds(local_player->gems_still_needed);
9537 #endif
9538
9539       break;
9540     }
9541
9542 #if !USE_PLAYER_GRAVITY
9543     case CA_SET_LEVEL_GRAVITY:
9544     {
9545       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9546                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9547                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9548                       game.gravity);
9549       break;
9550     }
9551 #endif
9552
9553     case CA_SET_LEVEL_WIND:
9554     {
9555       game.wind_direction = action_arg_direction;
9556
9557       break;
9558     }
9559
9560     /* ---------- player actions  ------------------------------------------ */
9561
9562     case CA_MOVE_PLAYER:
9563     {
9564       /* automatically move to the next field in specified direction */
9565       for (i = 0; i < MAX_PLAYERS; i++)
9566         if (trigger_player_bits & (1 << i))
9567           stored_player[i].programmed_action = action_arg_direction;
9568
9569       break;
9570     }
9571
9572     case CA_EXIT_PLAYER:
9573     {
9574       for (i = 0; i < MAX_PLAYERS; i++)
9575         if (action_arg_player_bits & (1 << i))
9576           PlayerWins(&stored_player[i]);
9577
9578       break;
9579     }
9580
9581     case CA_KILL_PLAYER:
9582     {
9583       for (i = 0; i < MAX_PLAYERS; i++)
9584         if (action_arg_player_bits & (1 << i))
9585           KillPlayer(&stored_player[i]);
9586
9587       break;
9588     }
9589
9590     case CA_SET_PLAYER_KEYS:
9591     {
9592       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9593       int element = getSpecialActionElement(action_arg_element,
9594                                             action_arg_number, EL_KEY_1);
9595
9596       if (IS_KEY(element))
9597       {
9598         for (i = 0; i < MAX_PLAYERS; i++)
9599         {
9600           if (trigger_player_bits & (1 << i))
9601           {
9602             stored_player[i].key[KEY_NR(element)] = key_state;
9603
9604             DrawGameDoorValues();
9605           }
9606         }
9607       }
9608
9609       break;
9610     }
9611
9612     case CA_SET_PLAYER_SPEED:
9613     {
9614       for (i = 0; i < MAX_PLAYERS; i++)
9615       {
9616         if (trigger_player_bits & (1 << i))
9617         {
9618           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9619
9620           if (action_arg == CA_ARG_SPEED_FASTER &&
9621               stored_player[i].cannot_move)
9622           {
9623             action_arg_number = STEPSIZE_VERY_SLOW;
9624           }
9625           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9626                    action_arg == CA_ARG_SPEED_FASTER)
9627           {
9628             action_arg_number = 2;
9629             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9630                            CA_MODE_MULTIPLY);
9631           }
9632           else if (action_arg == CA_ARG_NUMBER_RESET)
9633           {
9634             action_arg_number = level.initial_player_stepsize[i];
9635           }
9636
9637           move_stepsize =
9638             getModifiedActionNumber(move_stepsize,
9639                                     action_mode,
9640                                     action_arg_number,
9641                                     action_arg_number_min,
9642                                     action_arg_number_max);
9643
9644           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9645         }
9646       }
9647
9648       break;
9649     }
9650
9651     case CA_SET_PLAYER_SHIELD:
9652     {
9653       for (i = 0; i < MAX_PLAYERS; i++)
9654       {
9655         if (trigger_player_bits & (1 << i))
9656         {
9657           if (action_arg == CA_ARG_SHIELD_OFF)
9658           {
9659             stored_player[i].shield_normal_time_left = 0;
9660             stored_player[i].shield_deadly_time_left = 0;
9661           }
9662           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9663           {
9664             stored_player[i].shield_normal_time_left = 999999;
9665           }
9666           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9667           {
9668             stored_player[i].shield_normal_time_left = 999999;
9669             stored_player[i].shield_deadly_time_left = 999999;
9670           }
9671         }
9672       }
9673
9674       break;
9675     }
9676
9677 #if USE_PLAYER_GRAVITY
9678     case CA_SET_PLAYER_GRAVITY:
9679     {
9680       for (i = 0; i < MAX_PLAYERS; i++)
9681       {
9682         if (trigger_player_bits & (1 << i))
9683         {
9684           stored_player[i].gravity =
9685             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9686              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9687              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9688              stored_player[i].gravity);
9689         }
9690       }
9691
9692       break;
9693     }
9694 #endif
9695
9696     case CA_SET_PLAYER_ARTWORK:
9697     {
9698       for (i = 0; i < MAX_PLAYERS; i++)
9699       {
9700         if (trigger_player_bits & (1 << i))
9701         {
9702           int artwork_element = action_arg_element;
9703
9704           if (action_arg == CA_ARG_ELEMENT_RESET)
9705             artwork_element =
9706               (level.use_artwork_element[i] ? level.artwork_element[i] :
9707                stored_player[i].element_nr);
9708
9709 #if USE_GFX_RESET_PLAYER_ARTWORK
9710           if (stored_player[i].artwork_element != artwork_element)
9711             stored_player[i].Frame = 0;
9712 #endif
9713
9714           stored_player[i].artwork_element = artwork_element;
9715
9716           SetPlayerWaiting(&stored_player[i], FALSE);
9717
9718           /* set number of special actions for bored and sleeping animation */
9719           stored_player[i].num_special_action_bored =
9720             get_num_special_action(artwork_element,
9721                                    ACTION_BORING_1, ACTION_BORING_LAST);
9722           stored_player[i].num_special_action_sleeping =
9723             get_num_special_action(artwork_element,
9724                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9725         }
9726       }
9727
9728       break;
9729     }
9730
9731     /* ---------- CE actions  ---------------------------------------------- */
9732
9733     case CA_SET_CE_VALUE:
9734     {
9735 #if USE_NEW_CUSTOM_VALUE
9736       int last_ce_value = CustomValue[x][y];
9737
9738       CustomValue[x][y] = action_arg_number_new;
9739
9740       if (CustomValue[x][y] != last_ce_value)
9741       {
9742         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9743         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9744
9745         if (CustomValue[x][y] == 0)
9746         {
9747           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9748           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9749         }
9750       }
9751 #endif
9752
9753       break;
9754     }
9755
9756     case CA_SET_CE_SCORE:
9757     {
9758 #if USE_NEW_CUSTOM_VALUE
9759       int last_ce_score = ei->collect_score;
9760
9761       ei->collect_score = action_arg_number_new;
9762
9763       if (ei->collect_score != last_ce_score)
9764       {
9765         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9766         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9767
9768         if (ei->collect_score == 0)
9769         {
9770           int xx, yy;
9771
9772           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9773           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9774
9775           /*
9776             This is a very special case that seems to be a mixture between
9777             CheckElementChange() and CheckTriggeredElementChange(): while
9778             the first one only affects single elements that are triggered
9779             directly, the second one affects multiple elements in the playfield
9780             that are triggered indirectly by another element. This is a third
9781             case: Changing the CE score always affects multiple identical CEs,
9782             so every affected CE must be checked, not only the single CE for
9783             which the CE score was changed in the first place (as every instance
9784             of that CE shares the same CE score, and therefore also can change)!
9785           */
9786           SCAN_PLAYFIELD(xx, yy)
9787           {
9788             if (Feld[xx][yy] == element)
9789               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9790                                  CE_SCORE_GETS_ZERO);
9791           }
9792         }
9793       }
9794 #endif
9795
9796       break;
9797     }
9798
9799     /* ---------- engine actions  ------------------------------------------ */
9800
9801     case CA_SET_ENGINE_SCAN_MODE:
9802     {
9803       InitPlayfieldScanMode(action_arg);
9804
9805       break;
9806     }
9807
9808     default:
9809       break;
9810   }
9811 }
9812
9813 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9814 {
9815   int old_element = Feld[x][y];
9816   int new_element = GetElementFromGroupElement(element);
9817   int previous_move_direction = MovDir[x][y];
9818 #if USE_NEW_CUSTOM_VALUE
9819   int last_ce_value = CustomValue[x][y];
9820 #endif
9821   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9822   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9823   boolean add_player_onto_element = (new_element_is_player &&
9824 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9825                                      /* this breaks SnakeBite when a snake is
9826                                         halfway through a door that closes */
9827                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9828                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9829 #endif
9830                                      IS_WALKABLE(old_element));
9831
9832 #if 0
9833   /* check if element under the player changes from accessible to unaccessible
9834      (needed for special case of dropping element which then changes) */
9835   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9836       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9837   {
9838     Bang(x, y);
9839
9840     return;
9841   }
9842 #endif
9843
9844   if (!add_player_onto_element)
9845   {
9846     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9847       RemoveMovingField(x, y);
9848     else
9849       RemoveField(x, y);
9850
9851     Feld[x][y] = new_element;
9852
9853 #if !USE_GFX_RESET_GFX_ANIMATION
9854     ResetGfxAnimation(x, y);
9855     ResetRandomAnimationValue(x, y);
9856 #endif
9857
9858     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9859       MovDir[x][y] = previous_move_direction;
9860
9861 #if USE_NEW_CUSTOM_VALUE
9862     if (element_info[new_element].use_last_ce_value)
9863       CustomValue[x][y] = last_ce_value;
9864 #endif
9865
9866     InitField_WithBug1(x, y, FALSE);
9867
9868     new_element = Feld[x][y];   /* element may have changed */
9869
9870 #if USE_GFX_RESET_GFX_ANIMATION
9871     ResetGfxAnimation(x, y);
9872     ResetRandomAnimationValue(x, y);
9873 #endif
9874
9875     DrawLevelField(x, y);
9876
9877     if (GFX_CRUMBLED(new_element))
9878       DrawLevelFieldCrumbledSandNeighbours(x, y);
9879   }
9880
9881 #if 1
9882   /* check if element under the player changes from accessible to unaccessible
9883      (needed for special case of dropping element which then changes) */
9884   /* (must be checked after creating new element for walkable group elements) */
9885 #if USE_FIX_KILLED_BY_NON_WALKABLE
9886   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9887       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9888   {
9889     Bang(x, y);
9890
9891     return;
9892   }
9893 #else
9894   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9895       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9896   {
9897     Bang(x, y);
9898
9899     return;
9900   }
9901 #endif
9902 #endif
9903
9904   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9905   if (new_element_is_player)
9906     RelocatePlayer(x, y, new_element);
9907
9908   if (is_change)
9909     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9910
9911   TestIfBadThingTouchesPlayer(x, y);
9912   TestIfPlayerTouchesCustomElement(x, y);
9913   TestIfElementTouchesCustomElement(x, y);
9914 }
9915
9916 static void CreateField(int x, int y, int element)
9917 {
9918   CreateFieldExt(x, y, element, FALSE);
9919 }
9920
9921 static void CreateElementFromChange(int x, int y, int element)
9922 {
9923   element = GET_VALID_RUNTIME_ELEMENT(element);
9924
9925 #if USE_STOP_CHANGED_ELEMENTS
9926   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9927   {
9928     int old_element = Feld[x][y];
9929
9930     /* prevent changed element from moving in same engine frame
9931        unless both old and new element can either fall or move */
9932     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9933         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9934       Stop[x][y] = TRUE;
9935   }
9936 #endif
9937
9938   CreateFieldExt(x, y, element, TRUE);
9939 }
9940
9941 static boolean ChangeElement(int x, int y, int element, int page)
9942 {
9943   struct ElementInfo *ei = &element_info[element];
9944   struct ElementChangeInfo *change = &ei->change_page[page];
9945   int ce_value = CustomValue[x][y];
9946   int ce_score = ei->collect_score;
9947   int target_element;
9948   int old_element = Feld[x][y];
9949
9950   /* always use default change event to prevent running into a loop */
9951   if (ChangeEvent[x][y] == -1)
9952     ChangeEvent[x][y] = CE_DELAY;
9953
9954   if (ChangeEvent[x][y] == CE_DELAY)
9955   {
9956     /* reset actual trigger element, trigger player and action element */
9957     change->actual_trigger_element = EL_EMPTY;
9958     change->actual_trigger_player = EL_PLAYER_1;
9959     change->actual_trigger_side = CH_SIDE_NONE;
9960     change->actual_trigger_ce_value = 0;
9961     change->actual_trigger_ce_score = 0;
9962   }
9963
9964   /* do not change elements more than a specified maximum number of changes */
9965   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9966     return FALSE;
9967
9968   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9969
9970   if (change->explode)
9971   {
9972     Bang(x, y);
9973
9974     return TRUE;
9975   }
9976
9977   if (change->use_target_content)
9978   {
9979     boolean complete_replace = TRUE;
9980     boolean can_replace[3][3];
9981     int xx, yy;
9982
9983     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9984     {
9985       boolean is_empty;
9986       boolean is_walkable;
9987       boolean is_diggable;
9988       boolean is_collectible;
9989       boolean is_removable;
9990       boolean is_destructible;
9991       int ex = x + xx - 1;
9992       int ey = y + yy - 1;
9993       int content_element = change->target_content.e[xx][yy];
9994       int e;
9995
9996       can_replace[xx][yy] = TRUE;
9997
9998       if (ex == x && ey == y)   /* do not check changing element itself */
9999         continue;
10000
10001       if (content_element == EL_EMPTY_SPACE)
10002       {
10003         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10004
10005         continue;
10006       }
10007
10008       if (!IN_LEV_FIELD(ex, ey))
10009       {
10010         can_replace[xx][yy] = FALSE;
10011         complete_replace = FALSE;
10012
10013         continue;
10014       }
10015
10016       e = Feld[ex][ey];
10017
10018       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10019         e = MovingOrBlocked2Element(ex, ey);
10020
10021       is_empty = (IS_FREE(ex, ey) ||
10022                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10023
10024       is_walkable     = (is_empty || IS_WALKABLE(e));
10025       is_diggable     = (is_empty || IS_DIGGABLE(e));
10026       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10027       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10028       is_removable    = (is_diggable || is_collectible);
10029
10030       can_replace[xx][yy] =
10031         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10032           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10033           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10034           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10035           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10036           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10037          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10038
10039       if (!can_replace[xx][yy])
10040         complete_replace = FALSE;
10041     }
10042
10043     if (!change->only_if_complete || complete_replace)
10044     {
10045       boolean something_has_changed = FALSE;
10046
10047       if (change->only_if_complete && change->use_random_replace &&
10048           RND(100) < change->random_percentage)
10049         return FALSE;
10050
10051       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10052       {
10053         int ex = x + xx - 1;
10054         int ey = y + yy - 1;
10055         int content_element;
10056
10057         if (can_replace[xx][yy] && (!change->use_random_replace ||
10058                                     RND(100) < change->random_percentage))
10059         {
10060           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10061             RemoveMovingField(ex, ey);
10062
10063           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10064
10065           content_element = change->target_content.e[xx][yy];
10066           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10067                                               ce_value, ce_score);
10068
10069           CreateElementFromChange(ex, ey, target_element);
10070
10071           something_has_changed = TRUE;
10072
10073           /* for symmetry reasons, freeze newly created border elements */
10074           if (ex != x || ey != y)
10075             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10076         }
10077       }
10078
10079       if (something_has_changed)
10080       {
10081         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10082         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10083       }
10084     }
10085   }
10086   else
10087   {
10088     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10089                                         ce_value, ce_score);
10090
10091     if (element == EL_DIAGONAL_GROWING ||
10092         element == EL_DIAGONAL_SHRINKING)
10093     {
10094       target_element = Store[x][y];
10095
10096       Store[x][y] = EL_EMPTY;
10097     }
10098
10099     CreateElementFromChange(x, y, target_element);
10100
10101     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10102     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10103   }
10104
10105   /* this uses direct change before indirect change */
10106   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10107
10108   return TRUE;
10109 }
10110
10111 #if USE_NEW_DELAYED_ACTION
10112
10113 static void HandleElementChange(int x, int y, int page)
10114 {
10115   int element = MovingOrBlocked2Element(x, y);
10116   struct ElementInfo *ei = &element_info[element];
10117   struct ElementChangeInfo *change = &ei->change_page[page];
10118
10119 #ifdef DEBUG
10120   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10121       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10122   {
10123     printf("\n\n");
10124     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10125            x, y, element, element_info[element].token_name);
10126     printf("HandleElementChange(): This should never happen!\n");
10127     printf("\n\n");
10128   }
10129 #endif
10130
10131   /* this can happen with classic bombs on walkable, changing elements */
10132   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10133   {
10134 #if 0
10135     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10136       ChangeDelay[x][y] = 0;
10137 #endif
10138
10139     return;
10140   }
10141
10142   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10143   {
10144     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10145
10146     if (change->can_change)
10147     {
10148 #if 1
10149       /* !!! not clear why graphic animation should be reset at all here !!! */
10150       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10151 #if USE_GFX_RESET_WHEN_NOT_MOVING
10152       /* when a custom element is about to change (for example by change delay),
10153          do not reset graphic animation when the custom element is moving */
10154       if (!IS_MOVING(x, y))
10155 #endif
10156       {
10157         ResetGfxAnimation(x, y);
10158         ResetRandomAnimationValue(x, y);
10159       }
10160 #endif
10161
10162       if (change->pre_change_function)
10163         change->pre_change_function(x, y);
10164     }
10165   }
10166
10167   ChangeDelay[x][y]--;
10168
10169   if (ChangeDelay[x][y] != 0)           /* continue element change */
10170   {
10171     if (change->can_change)
10172     {
10173       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10174
10175       if (IS_ANIMATED(graphic))
10176         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10177
10178       if (change->change_function)
10179         change->change_function(x, y);
10180     }
10181   }
10182   else                                  /* finish element change */
10183   {
10184     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10185     {
10186       page = ChangePage[x][y];
10187       ChangePage[x][y] = -1;
10188
10189       change = &ei->change_page[page];
10190     }
10191
10192     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10193     {
10194       ChangeDelay[x][y] = 1;            /* try change after next move step */
10195       ChangePage[x][y] = page;          /* remember page to use for change */
10196
10197       return;
10198     }
10199
10200     if (change->can_change)
10201     {
10202       if (ChangeElement(x, y, element, page))
10203       {
10204         if (change->post_change_function)
10205           change->post_change_function(x, y);
10206       }
10207     }
10208
10209     if (change->has_action)
10210       ExecuteCustomElementAction(x, y, element, page);
10211   }
10212 }
10213
10214 #else
10215
10216 static void HandleElementChange(int x, int y, int page)
10217 {
10218   int element = MovingOrBlocked2Element(x, y);
10219   struct ElementInfo *ei = &element_info[element];
10220   struct ElementChangeInfo *change = &ei->change_page[page];
10221
10222 #ifdef DEBUG
10223   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10224   {
10225     printf("\n\n");
10226     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10227            x, y, element, element_info[element].token_name);
10228     printf("HandleElementChange(): This should never happen!\n");
10229     printf("\n\n");
10230   }
10231 #endif
10232
10233   /* this can happen with classic bombs on walkable, changing elements */
10234   if (!CAN_CHANGE(element))
10235   {
10236 #if 0
10237     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10238       ChangeDelay[x][y] = 0;
10239 #endif
10240
10241     return;
10242   }
10243
10244   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10245   {
10246     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10247
10248     ResetGfxAnimation(x, y);
10249     ResetRandomAnimationValue(x, y);
10250
10251     if (change->pre_change_function)
10252       change->pre_change_function(x, y);
10253   }
10254
10255   ChangeDelay[x][y]--;
10256
10257   if (ChangeDelay[x][y] != 0)           /* continue element change */
10258   {
10259     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10260
10261     if (IS_ANIMATED(graphic))
10262       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10263
10264     if (change->change_function)
10265       change->change_function(x, y);
10266   }
10267   else                                  /* finish element change */
10268   {
10269     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10270     {
10271       page = ChangePage[x][y];
10272       ChangePage[x][y] = -1;
10273
10274       change = &ei->change_page[page];
10275     }
10276
10277     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10278     {
10279       ChangeDelay[x][y] = 1;            /* try change after next move step */
10280       ChangePage[x][y] = page;          /* remember page to use for change */
10281
10282       return;
10283     }
10284
10285     if (ChangeElement(x, y, element, page))
10286     {
10287       if (change->post_change_function)
10288         change->post_change_function(x, y);
10289     }
10290   }
10291 }
10292
10293 #endif
10294
10295 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10296                                               int trigger_element,
10297                                               int trigger_event,
10298                                               int trigger_player,
10299                                               int trigger_side,
10300                                               int trigger_page)
10301 {
10302   boolean change_done_any = FALSE;
10303   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10304   int i;
10305
10306   if (!(trigger_events[trigger_element][trigger_event]))
10307     return FALSE;
10308
10309 #if 0
10310   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10311          trigger_event, recursion_loop_depth, recursion_loop_detected,
10312          recursion_loop_element, EL_NAME(recursion_loop_element));
10313 #endif
10314
10315   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10316
10317   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10318   {
10319     int element = EL_CUSTOM_START + i;
10320     boolean change_done = FALSE;
10321     int p;
10322
10323     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10325       continue;
10326
10327     for (p = 0; p < element_info[element].num_change_pages; p++)
10328     {
10329       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10330
10331       if (change->can_change_or_has_action &&
10332           change->has_event[trigger_event] &&
10333           change->trigger_side & trigger_side &&
10334           change->trigger_player & trigger_player &&
10335           change->trigger_page & trigger_page_bits &&
10336           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10337       {
10338         change->actual_trigger_element = trigger_element;
10339         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10340         change->actual_trigger_side = trigger_side;
10341         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10342         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10343
10344         if ((change->can_change && !change_done) || change->has_action)
10345         {
10346           int x, y;
10347
10348           SCAN_PLAYFIELD(x, y)
10349           {
10350             if (Feld[x][y] == element)
10351             {
10352               if (change->can_change && !change_done)
10353               {
10354                 ChangeDelay[x][y] = 1;
10355                 ChangeEvent[x][y] = trigger_event;
10356
10357                 HandleElementChange(x, y, p);
10358               }
10359 #if USE_NEW_DELAYED_ACTION
10360               else if (change->has_action)
10361               {
10362                 ExecuteCustomElementAction(x, y, element, p);
10363                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10364               }
10365 #else
10366               if (change->has_action)
10367               {
10368                 ExecuteCustomElementAction(x, y, element, p);
10369                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10370               }
10371 #endif
10372             }
10373           }
10374
10375           if (change->can_change)
10376           {
10377             change_done = TRUE;
10378             change_done_any = TRUE;
10379           }
10380         }
10381       }
10382     }
10383   }
10384
10385   RECURSION_LOOP_DETECTION_END();
10386
10387   return change_done_any;
10388 }
10389
10390 static boolean CheckElementChangeExt(int x, int y,
10391                                      int element,
10392                                      int trigger_element,
10393                                      int trigger_event,
10394                                      int trigger_player,
10395                                      int trigger_side)
10396 {
10397   boolean change_done = FALSE;
10398   int p;
10399
10400   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10401       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10402     return FALSE;
10403
10404   if (Feld[x][y] == EL_BLOCKED)
10405   {
10406     Blocked2Moving(x, y, &x, &y);
10407     element = Feld[x][y];
10408   }
10409
10410 #if 0
10411   /* check if element has already changed */
10412   if (Feld[x][y] != element)
10413     return FALSE;
10414 #else
10415   /* check if element has already changed or is about to change after moving */
10416   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10417        Feld[x][y] != element) ||
10418
10419       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10420        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10421         ChangePage[x][y] != -1)))
10422     return FALSE;
10423 #endif
10424
10425 #if 0
10426   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10427          trigger_event, recursion_loop_depth, recursion_loop_detected,
10428          recursion_loop_element, EL_NAME(recursion_loop_element));
10429 #endif
10430
10431   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10432
10433   for (p = 0; p < element_info[element].num_change_pages; p++)
10434   {
10435     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10436
10437     /* check trigger element for all events where the element that is checked
10438        for changing interacts with a directly adjacent element -- this is
10439        different to element changes that affect other elements to change on the
10440        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10441     boolean check_trigger_element =
10442       (trigger_event == CE_TOUCHING_X ||
10443        trigger_event == CE_HITTING_X ||
10444        trigger_event == CE_HIT_BY_X ||
10445 #if 1
10446        /* this one was forgotten until 3.2.3 */
10447        trigger_event == CE_DIGGING_X);
10448 #endif
10449
10450     if (change->can_change_or_has_action &&
10451         change->has_event[trigger_event] &&
10452         change->trigger_side & trigger_side &&
10453         change->trigger_player & trigger_player &&
10454         (!check_trigger_element ||
10455          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10456     {
10457       change->actual_trigger_element = trigger_element;
10458       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10459       change->actual_trigger_side = trigger_side;
10460       change->actual_trigger_ce_value = CustomValue[x][y];
10461       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10462
10463       /* special case: trigger element not at (x,y) position for some events */
10464       if (check_trigger_element)
10465       {
10466         static struct
10467         {
10468           int dx, dy;
10469         } move_xy[] =
10470           {
10471             {  0,  0 },
10472             { -1,  0 },
10473             { +1,  0 },
10474             {  0,  0 },
10475             {  0, -1 },
10476             {  0,  0 }, { 0, 0 }, { 0, 0 },
10477             {  0, +1 }
10478           };
10479
10480         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10481         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10482
10483         change->actual_trigger_ce_value = CustomValue[xx][yy];
10484         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10485       }
10486
10487       if (change->can_change && !change_done)
10488       {
10489         ChangeDelay[x][y] = 1;
10490         ChangeEvent[x][y] = trigger_event;
10491
10492         HandleElementChange(x, y, p);
10493
10494         change_done = TRUE;
10495       }
10496 #if USE_NEW_DELAYED_ACTION
10497       else if (change->has_action)
10498       {
10499         ExecuteCustomElementAction(x, y, element, p);
10500         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10501       }
10502 #else
10503       if (change->has_action)
10504       {
10505         ExecuteCustomElementAction(x, y, element, p);
10506         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10507       }
10508 #endif
10509     }
10510   }
10511
10512   RECURSION_LOOP_DETECTION_END();
10513
10514   return change_done;
10515 }
10516
10517 static void PlayPlayerSound(struct PlayerInfo *player)
10518 {
10519   int jx = player->jx, jy = player->jy;
10520   int sound_element = player->artwork_element;
10521   int last_action = player->last_action_waiting;
10522   int action = player->action_waiting;
10523
10524   if (player->is_waiting)
10525   {
10526     if (action != last_action)
10527       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10528     else
10529       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10530   }
10531   else
10532   {
10533     if (action != last_action)
10534       StopSound(element_info[sound_element].sound[last_action]);
10535
10536     if (last_action == ACTION_SLEEPING)
10537       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10538   }
10539 }
10540
10541 static void PlayAllPlayersSound()
10542 {
10543   int i;
10544
10545   for (i = 0; i < MAX_PLAYERS; i++)
10546     if (stored_player[i].active)
10547       PlayPlayerSound(&stored_player[i]);
10548 }
10549
10550 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10551 {
10552   boolean last_waiting = player->is_waiting;
10553   int move_dir = player->MovDir;
10554
10555   player->dir_waiting = move_dir;
10556   player->last_action_waiting = player->action_waiting;
10557
10558   if (is_waiting)
10559   {
10560     if (!last_waiting)          /* not waiting -> waiting */
10561     {
10562       player->is_waiting = TRUE;
10563
10564       player->frame_counter_bored =
10565         FrameCounter +
10566         game.player_boring_delay_fixed +
10567         GetSimpleRandom(game.player_boring_delay_random);
10568       player->frame_counter_sleeping =
10569         FrameCounter +
10570         game.player_sleeping_delay_fixed +
10571         GetSimpleRandom(game.player_sleeping_delay_random);
10572
10573       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10574     }
10575
10576     if (game.player_sleeping_delay_fixed +
10577         game.player_sleeping_delay_random > 0 &&
10578         player->anim_delay_counter == 0 &&
10579         player->post_delay_counter == 0 &&
10580         FrameCounter >= player->frame_counter_sleeping)
10581       player->is_sleeping = TRUE;
10582     else if (game.player_boring_delay_fixed +
10583              game.player_boring_delay_random > 0 &&
10584              FrameCounter >= player->frame_counter_bored)
10585       player->is_bored = TRUE;
10586
10587     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10588                               player->is_bored ? ACTION_BORING :
10589                               ACTION_WAITING);
10590
10591     if (player->is_sleeping && player->use_murphy)
10592     {
10593       /* special case for sleeping Murphy when leaning against non-free tile */
10594
10595       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10596           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10597            !IS_MOVING(player->jx - 1, player->jy)))
10598         move_dir = MV_LEFT;
10599       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10600                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10601                 !IS_MOVING(player->jx + 1, player->jy)))
10602         move_dir = MV_RIGHT;
10603       else
10604         player->is_sleeping = FALSE;
10605
10606       player->dir_waiting = move_dir;
10607     }
10608
10609     if (player->is_sleeping)
10610     {
10611       if (player->num_special_action_sleeping > 0)
10612       {
10613         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10614         {
10615           int last_special_action = player->special_action_sleeping;
10616           int num_special_action = player->num_special_action_sleeping;
10617           int special_action =
10618             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10619              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10620              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10621              last_special_action + 1 : ACTION_SLEEPING);
10622           int special_graphic =
10623             el_act_dir2img(player->artwork_element, special_action, move_dir);
10624
10625           player->anim_delay_counter =
10626             graphic_info[special_graphic].anim_delay_fixed +
10627             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10628           player->post_delay_counter =
10629             graphic_info[special_graphic].post_delay_fixed +
10630             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10631
10632           player->special_action_sleeping = special_action;
10633         }
10634
10635         if (player->anim_delay_counter > 0)
10636         {
10637           player->action_waiting = player->special_action_sleeping;
10638           player->anim_delay_counter--;
10639         }
10640         else if (player->post_delay_counter > 0)
10641         {
10642           player->post_delay_counter--;
10643         }
10644       }
10645     }
10646     else if (player->is_bored)
10647     {
10648       if (player->num_special_action_bored > 0)
10649       {
10650         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10651         {
10652           int special_action =
10653             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10654           int special_graphic =
10655             el_act_dir2img(player->artwork_element, special_action, move_dir);
10656
10657           player->anim_delay_counter =
10658             graphic_info[special_graphic].anim_delay_fixed +
10659             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10660           player->post_delay_counter =
10661             graphic_info[special_graphic].post_delay_fixed +
10662             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10663
10664           player->special_action_bored = special_action;
10665         }
10666
10667         if (player->anim_delay_counter > 0)
10668         {
10669           player->action_waiting = player->special_action_bored;
10670           player->anim_delay_counter--;
10671         }
10672         else if (player->post_delay_counter > 0)
10673         {
10674           player->post_delay_counter--;
10675         }
10676       }
10677     }
10678   }
10679   else if (last_waiting)        /* waiting -> not waiting */
10680   {
10681     player->is_waiting = FALSE;
10682     player->is_bored = FALSE;
10683     player->is_sleeping = FALSE;
10684
10685     player->frame_counter_bored = -1;
10686     player->frame_counter_sleeping = -1;
10687
10688     player->anim_delay_counter = 0;
10689     player->post_delay_counter = 0;
10690
10691     player->dir_waiting = player->MovDir;
10692     player->action_waiting = ACTION_DEFAULT;
10693
10694     player->special_action_bored = ACTION_DEFAULT;
10695     player->special_action_sleeping = ACTION_DEFAULT;
10696   }
10697 }
10698
10699 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10700 {
10701   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10702   int left      = player_action & JOY_LEFT;
10703   int right     = player_action & JOY_RIGHT;
10704   int up        = player_action & JOY_UP;
10705   int down      = player_action & JOY_DOWN;
10706   int button1   = player_action & JOY_BUTTON_1;
10707   int button2   = player_action & JOY_BUTTON_2;
10708   int dx        = (left ? -1 : right ? 1 : 0);
10709   int dy        = (up   ? -1 : down  ? 1 : 0);
10710
10711   if (!player->active || tape.pausing)
10712     return 0;
10713
10714   if (player_action)
10715   {
10716     if (button1)
10717       snapped = SnapField(player, dx, dy);
10718     else
10719     {
10720       if (button2)
10721         dropped = DropElement(player);
10722
10723       moved = MovePlayer(player, dx, dy);
10724     }
10725
10726     if (tape.single_step && tape.recording && !tape.pausing)
10727     {
10728       if (button1 || (dropped && !moved))
10729       {
10730         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731         SnapField(player, 0, 0);                /* stop snapping */
10732       }
10733     }
10734
10735     SetPlayerWaiting(player, FALSE);
10736
10737     return player_action;
10738   }
10739   else
10740   {
10741     /* no actions for this player (no input at player's configured device) */
10742
10743     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10744     SnapField(player, 0, 0);
10745     CheckGravityMovementWhenNotMoving(player);
10746
10747     if (player->MovPos == 0)
10748       SetPlayerWaiting(player, TRUE);
10749
10750     if (player->MovPos == 0)    /* needed for tape.playing */
10751       player->is_moving = FALSE;
10752
10753     player->is_dropping = FALSE;
10754     player->is_dropping_pressed = FALSE;
10755     player->drop_pressed_delay = 0;
10756
10757     return 0;
10758   }
10759 }
10760
10761 static void CheckLevelTime()
10762 {
10763   int i;
10764
10765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10766   {
10767     if (level.native_em_level->lev->home == 0)  /* all players at home */
10768     {
10769       PlayerWins(local_player);
10770
10771       AllPlayersGone = TRUE;
10772
10773       level.native_em_level->lev->home = -1;
10774     }
10775
10776     if (level.native_em_level->ply[0]->alive == 0 &&
10777         level.native_em_level->ply[1]->alive == 0 &&
10778         level.native_em_level->ply[2]->alive == 0 &&
10779         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10780       AllPlayersGone = TRUE;
10781   }
10782
10783   if (TimeFrames >= FRAMES_PER_SECOND)
10784   {
10785     TimeFrames = 0;
10786     TapeTime++;
10787
10788     for (i = 0; i < MAX_PLAYERS; i++)
10789     {
10790       struct PlayerInfo *player = &stored_player[i];
10791
10792       if (SHIELD_ON(player))
10793       {
10794         player->shield_normal_time_left--;
10795
10796         if (player->shield_deadly_time_left > 0)
10797           player->shield_deadly_time_left--;
10798       }
10799     }
10800
10801     if (!local_player->LevelSolved && !level.use_step_counter)
10802     {
10803       TimePlayed++;
10804
10805       if (TimeLeft > 0)
10806       {
10807         TimeLeft--;
10808
10809         if (TimeLeft <= 10 && setup.time_limit)
10810           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10811
10812 #if 1
10813         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10814
10815         DisplayGameControlValues();
10816 #else
10817         DrawGameValue_Time(TimeLeft);
10818 #endif
10819
10820         if (!TimeLeft && setup.time_limit)
10821         {
10822           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10823             level.native_em_level->lev->killed_out_of_time = TRUE;
10824           else
10825             for (i = 0; i < MAX_PLAYERS; i++)
10826               KillPlayer(&stored_player[i]);
10827         }
10828       }
10829 #if 1
10830       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10831       {
10832         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10833
10834         DisplayGameControlValues();
10835       }
10836 #else
10837       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10838         DrawGameValue_Time(TimePlayed);
10839 #endif
10840
10841       level.native_em_level->lev->time =
10842         (level.time == 0 ? TimePlayed : TimeLeft);
10843     }
10844
10845     if (tape.recording || tape.playing)
10846       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10847   }
10848
10849   DrawGameDoorValues();
10850 }
10851
10852 void AdvanceFrameAndPlayerCounters(int player_nr)
10853 {
10854   int i;
10855
10856   /* advance frame counters (global frame counter and time frame counter) */
10857   FrameCounter++;
10858   TimeFrames++;
10859
10860   /* advance player counters (counters for move delay, move animation etc.) */
10861   for (i = 0; i < MAX_PLAYERS; i++)
10862   {
10863     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10864     int move_delay_value = stored_player[i].move_delay_value;
10865     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10866
10867     if (!advance_player_counters)       /* not all players may be affected */
10868       continue;
10869
10870 #if USE_NEW_PLAYER_ANIM
10871     if (move_frames == 0)       /* less than one move per game frame */
10872     {
10873       int stepsize = TILEX / move_delay_value;
10874       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10875       int count = (stored_player[i].is_moving ?
10876                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10877
10878       if (count % delay == 0)
10879         move_frames = 1;
10880     }
10881 #endif
10882
10883     stored_player[i].Frame += move_frames;
10884
10885     if (stored_player[i].MovPos != 0)
10886       stored_player[i].StepFrame += move_frames;
10887
10888     if (stored_player[i].move_delay > 0)
10889       stored_player[i].move_delay--;
10890
10891     /* due to bugs in previous versions, counter must count up, not down */
10892     if (stored_player[i].push_delay != -1)
10893       stored_player[i].push_delay++;
10894
10895     if (stored_player[i].drop_delay > 0)
10896       stored_player[i].drop_delay--;
10897
10898     if (stored_player[i].is_dropping_pressed)
10899       stored_player[i].drop_pressed_delay++;
10900   }
10901 }
10902
10903 void StartGameActions(boolean init_network_game, boolean record_tape,
10904                       long random_seed)
10905 {
10906   unsigned long new_random_seed = InitRND(random_seed);
10907
10908   if (record_tape)
10909     TapeStartRecording(new_random_seed);
10910
10911 #if defined(NETWORK_AVALIABLE)
10912   if (init_network_game)
10913   {
10914     SendToServer_StartPlaying();
10915
10916     return;
10917   }
10918 #endif
10919
10920   InitGame();
10921 }
10922
10923 void GameActions()
10924 {
10925   static unsigned long game_frame_delay = 0;
10926   unsigned long game_frame_delay_value;
10927   byte *recorded_player_action;
10928   byte summarized_player_action = 0;
10929   byte tape_action[MAX_PLAYERS];
10930   int i;
10931
10932   /* detect endless loops, caused by custom element programming */
10933   if (recursion_loop_detected && recursion_loop_depth == 0)
10934   {
10935     char *message = getStringCat3("Internal Error ! Element ",
10936                                   EL_NAME(recursion_loop_element),
10937                                   " caused endless loop ! Quit the game ?");
10938
10939     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10940           EL_NAME(recursion_loop_element));
10941
10942     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10943
10944     recursion_loop_detected = FALSE;    /* if game should be continued */
10945
10946     free(message);
10947
10948     return;
10949   }
10950
10951   if (game.restart_level)
10952     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10953
10954   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10955   {
10956     if (level.native_em_level->lev->home == 0)  /* all players at home */
10957     {
10958       PlayerWins(local_player);
10959
10960       AllPlayersGone = TRUE;
10961
10962       level.native_em_level->lev->home = -1;
10963     }
10964
10965     if (level.native_em_level->ply[0]->alive == 0 &&
10966         level.native_em_level->ply[1]->alive == 0 &&
10967         level.native_em_level->ply[2]->alive == 0 &&
10968         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10969       AllPlayersGone = TRUE;
10970   }
10971
10972   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10973     GameWon();
10974
10975   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10976     TapeStop();
10977
10978   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10979     return;
10980
10981   game_frame_delay_value =
10982     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10983
10984   if (tape.playing && tape.warp_forward && !tape.pausing)
10985     game_frame_delay_value = 0;
10986
10987   /* ---------- main game synchronization point ---------- */
10988
10989   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10990
10991   if (network_playing && !network_player_action_received)
10992   {
10993     /* try to get network player actions in time */
10994
10995 #if defined(NETWORK_AVALIABLE)
10996     /* last chance to get network player actions without main loop delay */
10997     HandleNetworking();
10998 #endif
10999
11000     /* game was quit by network peer */
11001     if (game_status != GAME_MODE_PLAYING)
11002       return;
11003
11004     if (!network_player_action_received)
11005       return;           /* failed to get network player actions in time */
11006
11007     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11008   }
11009
11010   if (tape.pausing)
11011     return;
11012
11013   /* at this point we know that we really continue executing the game */
11014
11015   network_player_action_received = FALSE;
11016
11017   /* when playing tape, read previously recorded player input from tape data */
11018   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11019
11020 #if 1
11021   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11022   if (tape.pausing)
11023     return;
11024 #endif
11025
11026   if (tape.set_centered_player)
11027   {
11028     game.centered_player_nr_next = tape.centered_player_nr_next;
11029     game.set_centered_player = TRUE;
11030   }
11031
11032   for (i = 0; i < MAX_PLAYERS; i++)
11033   {
11034     summarized_player_action |= stored_player[i].action;
11035
11036     if (!network_playing)
11037       stored_player[i].effective_action = stored_player[i].action;
11038   }
11039
11040 #if defined(NETWORK_AVALIABLE)
11041   if (network_playing)
11042     SendToServer_MovePlayer(summarized_player_action);
11043 #endif
11044
11045   if (!options.network && !setup.team_mode)
11046     local_player->effective_action = summarized_player_action;
11047
11048   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11049   {
11050     for (i = 0; i < MAX_PLAYERS; i++)
11051       stored_player[i].effective_action =
11052         (i == game.centered_player_nr ? summarized_player_action : 0);
11053   }
11054
11055   if (recorded_player_action != NULL)
11056     for (i = 0; i < MAX_PLAYERS; i++)
11057       stored_player[i].effective_action = recorded_player_action[i];
11058
11059   for (i = 0; i < MAX_PLAYERS; i++)
11060   {
11061     tape_action[i] = stored_player[i].effective_action;
11062
11063     /* (this can only happen in the R'n'D game engine) */
11064     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11065       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11066   }
11067
11068   /* only record actions from input devices, but not programmed actions */
11069   if (tape.recording)
11070     TapeRecordAction(tape_action);
11071
11072   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11073   {
11074     GameActions_EM_Main();
11075   }
11076   else
11077   {
11078     GameActions_RND();
11079   }
11080 }
11081
11082 void GameActions_EM_Main()
11083 {
11084   byte effective_action[MAX_PLAYERS];
11085   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11086   int i;
11087
11088   for (i = 0; i < MAX_PLAYERS; i++)
11089     effective_action[i] = stored_player[i].effective_action;
11090
11091   GameActions_EM(effective_action, warp_mode);
11092
11093   CheckLevelTime();
11094
11095   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11096 }
11097
11098 void GameActions_RND()
11099 {
11100   int magic_wall_x = 0, magic_wall_y = 0;
11101   int i, x, y, element, graphic;
11102
11103   InitPlayfieldScanModeVars();
11104
11105 #if USE_ONE_MORE_CHANGE_PER_FRAME
11106   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11107   {
11108     SCAN_PLAYFIELD(x, y)
11109     {
11110       ChangeCount[x][y] = 0;
11111       ChangeEvent[x][y] = -1;
11112     }
11113   }
11114 #endif
11115
11116   if (game.set_centered_player)
11117   {
11118     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11119
11120     /* switching to "all players" only possible if all players fit to screen */
11121     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11122     {
11123       game.centered_player_nr_next = game.centered_player_nr;
11124       game.set_centered_player = FALSE;
11125     }
11126
11127     /* do not switch focus to non-existing (or non-active) player */
11128     if (game.centered_player_nr_next >= 0 &&
11129         !stored_player[game.centered_player_nr_next].active)
11130     {
11131       game.centered_player_nr_next = game.centered_player_nr;
11132       game.set_centered_player = FALSE;
11133     }
11134   }
11135
11136   if (game.set_centered_player &&
11137       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11138   {
11139     int sx, sy;
11140
11141     if (game.centered_player_nr_next == -1)
11142     {
11143       setScreenCenteredToAllPlayers(&sx, &sy);
11144     }
11145     else
11146     {
11147       sx = stored_player[game.centered_player_nr_next].jx;
11148       sy = stored_player[game.centered_player_nr_next].jy;
11149     }
11150
11151     game.centered_player_nr = game.centered_player_nr_next;
11152     game.set_centered_player = FALSE;
11153
11154     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11155     DrawGameDoorValues();
11156   }
11157
11158   for (i = 0; i < MAX_PLAYERS; i++)
11159   {
11160     int actual_player_action = stored_player[i].effective_action;
11161
11162 #if 1
11163     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11164        - rnd_equinox_tetrachloride 048
11165        - rnd_equinox_tetrachloride_ii 096
11166        - rnd_emanuel_schmieg 002
11167        - doctor_sloan_ww 001, 020
11168     */
11169     if (stored_player[i].MovPos == 0)
11170       CheckGravityMovement(&stored_player[i]);
11171 #endif
11172
11173     /* overwrite programmed action with tape action */
11174     if (stored_player[i].programmed_action)
11175       actual_player_action = stored_player[i].programmed_action;
11176
11177     PlayerActions(&stored_player[i], actual_player_action);
11178
11179     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11180   }
11181
11182   ScrollScreen(NULL, SCROLL_GO_ON);
11183
11184   /* for backwards compatibility, the following code emulates a fixed bug that
11185      occured when pushing elements (causing elements that just made their last
11186      pushing step to already (if possible) make their first falling step in the
11187      same game frame, which is bad); this code is also needed to use the famous
11188      "spring push bug" which is used in older levels and might be wanted to be
11189      used also in newer levels, but in this case the buggy pushing code is only
11190      affecting the "spring" element and no other elements */
11191
11192   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11193   {
11194     for (i = 0; i < MAX_PLAYERS; i++)
11195     {
11196       struct PlayerInfo *player = &stored_player[i];
11197       int x = player->jx;
11198       int y = player->jy;
11199
11200       if (player->active && player->is_pushing && player->is_moving &&
11201           IS_MOVING(x, y) &&
11202           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11203            Feld[x][y] == EL_SPRING))
11204       {
11205         ContinueMoving(x, y);
11206
11207         /* continue moving after pushing (this is actually a bug) */
11208         if (!IS_MOVING(x, y))
11209           Stop[x][y] = FALSE;
11210       }
11211     }
11212   }
11213
11214 #if 0
11215   debug_print_timestamp(0, "start main loop profiling");
11216 #endif
11217
11218   SCAN_PLAYFIELD(x, y)
11219   {
11220     ChangeCount[x][y] = 0;
11221     ChangeEvent[x][y] = -1;
11222
11223     /* this must be handled before main playfield loop */
11224     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11225     {
11226       MovDelay[x][y]--;
11227       if (MovDelay[x][y] <= 0)
11228         RemoveField(x, y);
11229     }
11230
11231 #if USE_NEW_SNAP_DELAY
11232     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11233     {
11234       MovDelay[x][y]--;
11235       if (MovDelay[x][y] <= 0)
11236       {
11237         RemoveField(x, y);
11238         DrawLevelField(x, y);
11239
11240         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11241       }
11242     }
11243 #endif
11244
11245 #if DEBUG
11246     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11247     {
11248       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11249       printf("GameActions(): This should never happen!\n");
11250
11251       ChangePage[x][y] = -1;
11252     }
11253 #endif
11254
11255     Stop[x][y] = FALSE;
11256     if (WasJustMoving[x][y] > 0)
11257       WasJustMoving[x][y]--;
11258     if (WasJustFalling[x][y] > 0)
11259       WasJustFalling[x][y]--;
11260     if (CheckCollision[x][y] > 0)
11261       CheckCollision[x][y]--;
11262     if (CheckImpact[x][y] > 0)
11263       CheckImpact[x][y]--;
11264
11265     GfxFrame[x][y]++;
11266
11267     /* reset finished pushing action (not done in ContinueMoving() to allow
11268        continuous pushing animation for elements with zero push delay) */
11269     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11270     {
11271       ResetGfxAnimation(x, y);
11272       DrawLevelField(x, y);
11273     }
11274
11275 #if DEBUG
11276     if (IS_BLOCKED(x, y))
11277     {
11278       int oldx, oldy;
11279
11280       Blocked2Moving(x, y, &oldx, &oldy);
11281       if (!IS_MOVING(oldx, oldy))
11282       {
11283         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11284         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11285         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11286         printf("GameActions(): This should never happen!\n");
11287       }
11288     }
11289 #endif
11290   }
11291
11292 #if 0
11293   debug_print_timestamp(0, "- time for pre-main loop:");
11294 #endif
11295
11296 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11297   SCAN_PLAYFIELD(x, y)
11298   {
11299     element = Feld[x][y];
11300     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11301
11302 #if 1
11303     {
11304 #if 1
11305       int element2 = element;
11306       int graphic2 = graphic;
11307 #else
11308       int element2 = Feld[x][y];
11309       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11310 #endif
11311       int last_gfx_frame = GfxFrame[x][y];
11312
11313       if (graphic_info[graphic2].anim_global_sync)
11314         GfxFrame[x][y] = FrameCounter;
11315       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11316         GfxFrame[x][y] = CustomValue[x][y];
11317       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11318         GfxFrame[x][y] = element_info[element2].collect_score;
11319       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11320         GfxFrame[x][y] = ChangeDelay[x][y];
11321
11322       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11323         DrawLevelGraphicAnimation(x, y, graphic2);
11324     }
11325 #else
11326     ResetGfxFrame(x, y, TRUE);
11327 #endif
11328
11329 #if 1
11330     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11331         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11332       ResetRandomAnimationValue(x, y);
11333 #endif
11334
11335 #if 1
11336     SetRandomAnimationValue(x, y);
11337 #endif
11338
11339 #if 1
11340     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11341 #endif
11342   }
11343 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11344
11345 #if 0
11346   debug_print_timestamp(0, "- time for TEST loop:     -->");
11347 #endif
11348
11349   SCAN_PLAYFIELD(x, y)
11350   {
11351     element = Feld[x][y];
11352     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11353
11354     ResetGfxFrame(x, y, TRUE);
11355
11356     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11357         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11358       ResetRandomAnimationValue(x, y);
11359
11360     SetRandomAnimationValue(x, y);
11361
11362     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11363
11364     if (IS_INACTIVE(element))
11365     {
11366       if (IS_ANIMATED(graphic))
11367         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11368
11369       continue;
11370     }
11371
11372     /* this may take place after moving, so 'element' may have changed */
11373     if (IS_CHANGING(x, y) &&
11374         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11375     {
11376       int page = element_info[element].event_page_nr[CE_DELAY];
11377
11378 #if 1
11379       HandleElementChange(x, y, page);
11380 #else
11381       if (CAN_CHANGE(element))
11382         HandleElementChange(x, y, page);
11383
11384       if (HAS_ACTION(element))
11385         ExecuteCustomElementAction(x, y, element, page);
11386 #endif
11387
11388       element = Feld[x][y];
11389       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11390     }
11391
11392 #if 0   // ---------------------------------------------------------------------
11393
11394     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11395     {
11396       StartMoving(x, y);
11397
11398       element = Feld[x][y];
11399       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11400
11401       if (IS_ANIMATED(graphic) &&
11402           !IS_MOVING(x, y) &&
11403           !Stop[x][y])
11404         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11405
11406       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11407         DrawTwinkleOnField(x, y);
11408     }
11409     else if (IS_MOVING(x, y))
11410       ContinueMoving(x, y);
11411     else
11412     {
11413       switch (element)
11414       {
11415         case EL_ACID:
11416         case EL_EXIT_OPEN:
11417         case EL_EM_EXIT_OPEN:
11418         case EL_SP_EXIT_OPEN:
11419         case EL_STEEL_EXIT_OPEN:
11420         case EL_EM_STEEL_EXIT_OPEN:
11421         case EL_SP_TERMINAL:
11422         case EL_SP_TERMINAL_ACTIVE:
11423         case EL_EXTRA_TIME:
11424         case EL_SHIELD_NORMAL:
11425         case EL_SHIELD_DEADLY:
11426           if (IS_ANIMATED(graphic))
11427             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11428           break;
11429
11430         case EL_DYNAMITE_ACTIVE:
11431         case EL_EM_DYNAMITE_ACTIVE:
11432         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11433         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11434         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11435         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11436         case EL_SP_DISK_RED_ACTIVE:
11437           CheckDynamite(x, y);
11438           break;
11439
11440         case EL_AMOEBA_GROWING:
11441           AmoebeWaechst(x, y);
11442           break;
11443
11444         case EL_AMOEBA_SHRINKING:
11445           AmoebaDisappearing(x, y);
11446           break;
11447
11448 #if !USE_NEW_AMOEBA_CODE
11449         case EL_AMOEBA_WET:
11450         case EL_AMOEBA_DRY:
11451         case EL_AMOEBA_FULL:
11452         case EL_BD_AMOEBA:
11453         case EL_EMC_DRIPPER:
11454           AmoebeAbleger(x, y);
11455           break;
11456 #endif
11457
11458         case EL_GAME_OF_LIFE:
11459         case EL_BIOMAZE:
11460           Life(x, y);
11461           break;
11462
11463         case EL_EXIT_CLOSED:
11464           CheckExit(x, y);
11465           break;
11466
11467         case EL_EM_EXIT_CLOSED:
11468           CheckExitEM(x, y);
11469           break;
11470
11471         case EL_STEEL_EXIT_CLOSED:
11472           CheckExitSteel(x, y);
11473           break;
11474
11475         case EL_EM_STEEL_EXIT_CLOSED:
11476           CheckExitSteelEM(x, y);
11477           break;
11478
11479         case EL_SP_EXIT_CLOSED:
11480           CheckExitSP(x, y);
11481           break;
11482
11483         case EL_EXPANDABLE_WALL_GROWING:
11484         case EL_EXPANDABLE_STEELWALL_GROWING:
11485           MauerWaechst(x, y);
11486           break;
11487
11488         case EL_EXPANDABLE_WALL:
11489         case EL_EXPANDABLE_WALL_HORIZONTAL:
11490         case EL_EXPANDABLE_WALL_VERTICAL:
11491         case EL_EXPANDABLE_WALL_ANY:
11492         case EL_BD_EXPANDABLE_WALL:
11493           MauerAbleger(x, y);
11494           break;
11495
11496         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11497         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11498         case EL_EXPANDABLE_STEELWALL_ANY:
11499           MauerAblegerStahl(x, y);
11500           break;
11501
11502         case EL_FLAMES:
11503           CheckForDragon(x, y);
11504           break;
11505
11506         case EL_EXPLOSION:
11507           break;
11508
11509         case EL_ELEMENT_SNAPPING:
11510         case EL_DIAGONAL_SHRINKING:
11511         case EL_DIAGONAL_GROWING:
11512         {
11513           graphic =
11514             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11515
11516           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517           break;
11518         }
11519
11520         default:
11521           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11522             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11523           break;
11524       }
11525     }
11526
11527 #else   // ---------------------------------------------------------------------
11528
11529     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11530     {
11531       StartMoving(x, y);
11532
11533       element = Feld[x][y];
11534       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11535
11536       if (IS_ANIMATED(graphic) &&
11537           !IS_MOVING(x, y) &&
11538           !Stop[x][y])
11539         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11540
11541       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11542         DrawTwinkleOnField(x, y);
11543     }
11544     else if ((element == EL_ACID ||
11545               element == EL_EXIT_OPEN ||
11546               element == EL_EM_EXIT_OPEN ||
11547               element == EL_SP_EXIT_OPEN ||
11548               element == EL_STEEL_EXIT_OPEN ||
11549               element == EL_EM_STEEL_EXIT_OPEN ||
11550               element == EL_SP_TERMINAL ||
11551               element == EL_SP_TERMINAL_ACTIVE ||
11552               element == EL_EXTRA_TIME ||
11553               element == EL_SHIELD_NORMAL ||
11554               element == EL_SHIELD_DEADLY) &&
11555              IS_ANIMATED(graphic))
11556       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11557     else if (IS_MOVING(x, y))
11558       ContinueMoving(x, y);
11559     else if (IS_ACTIVE_BOMB(element))
11560       CheckDynamite(x, y);
11561     else if (element == EL_AMOEBA_GROWING)
11562       AmoebeWaechst(x, y);
11563     else if (element == EL_AMOEBA_SHRINKING)
11564       AmoebaDisappearing(x, y);
11565
11566 #if !USE_NEW_AMOEBA_CODE
11567     else if (IS_AMOEBALIVE(element))
11568       AmoebeAbleger(x, y);
11569 #endif
11570
11571     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11572       Life(x, y);
11573     else if (element == EL_EXIT_CLOSED)
11574       CheckExit(x, y);
11575     else if (element == EL_EM_EXIT_CLOSED)
11576       CheckExitEM(x, y);
11577     else if (element == EL_STEEL_EXIT_CLOSED)
11578       CheckExitSteel(x, y);
11579     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11580       CheckExitSteelEM(x, y);
11581     else if (element == EL_SP_EXIT_CLOSED)
11582       CheckExitSP(x, y);
11583     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11584              element == EL_EXPANDABLE_STEELWALL_GROWING)
11585       MauerWaechst(x, y);
11586     else if (element == EL_EXPANDABLE_WALL ||
11587              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11588              element == EL_EXPANDABLE_WALL_VERTICAL ||
11589              element == EL_EXPANDABLE_WALL_ANY ||
11590              element == EL_BD_EXPANDABLE_WALL)
11591       MauerAbleger(x, y);
11592     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11593              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11594              element == EL_EXPANDABLE_STEELWALL_ANY)
11595       MauerAblegerStahl(x, y);
11596     else if (element == EL_FLAMES)
11597       CheckForDragon(x, y);
11598     else if (element == EL_EXPLOSION)
11599       ; /* drawing of correct explosion animation is handled separately */
11600     else if (element == EL_ELEMENT_SNAPPING ||
11601              element == EL_DIAGONAL_SHRINKING ||
11602              element == EL_DIAGONAL_GROWING)
11603     {
11604       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11605
11606       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11607     }
11608     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11609       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11610
11611 #endif  // ---------------------------------------------------------------------
11612
11613     if (IS_BELT_ACTIVE(element))
11614       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11615
11616     if (game.magic_wall_active)
11617     {
11618       int jx = local_player->jx, jy = local_player->jy;
11619
11620       /* play the element sound at the position nearest to the player */
11621       if ((element == EL_MAGIC_WALL_FULL ||
11622            element == EL_MAGIC_WALL_ACTIVE ||
11623            element == EL_MAGIC_WALL_EMPTYING ||
11624            element == EL_BD_MAGIC_WALL_FULL ||
11625            element == EL_BD_MAGIC_WALL_ACTIVE ||
11626            element == EL_BD_MAGIC_WALL_EMPTYING ||
11627            element == EL_DC_MAGIC_WALL_FULL ||
11628            element == EL_DC_MAGIC_WALL_ACTIVE ||
11629            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11630           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11631       {
11632         magic_wall_x = x;
11633         magic_wall_y = y;
11634       }
11635     }
11636   }
11637
11638 #if 0
11639   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11640 #endif
11641
11642 #if USE_NEW_AMOEBA_CODE
11643   /* new experimental amoeba growth stuff */
11644   if (!(FrameCounter % 8))
11645   {
11646     static unsigned long random = 1684108901;
11647
11648     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11649     {
11650       x = RND(lev_fieldx);
11651       y = RND(lev_fieldy);
11652       element = Feld[x][y];
11653
11654       if (!IS_PLAYER(x,y) &&
11655           (element == EL_EMPTY ||
11656            CAN_GROW_INTO(element) ||
11657            element == EL_QUICKSAND_EMPTY ||
11658            element == EL_QUICKSAND_FAST_EMPTY ||
11659            element == EL_ACID_SPLASH_LEFT ||
11660            element == EL_ACID_SPLASH_RIGHT))
11661       {
11662         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11663             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11664             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11665             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11666           Feld[x][y] = EL_AMOEBA_DROP;
11667       }
11668
11669       random = random * 129 + 1;
11670     }
11671   }
11672 #endif
11673
11674 #if 0
11675   if (game.explosions_delayed)
11676 #endif
11677   {
11678     game.explosions_delayed = FALSE;
11679
11680     SCAN_PLAYFIELD(x, y)
11681     {
11682       element = Feld[x][y];
11683
11684       if (ExplodeField[x][y])
11685         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11686       else if (element == EL_EXPLOSION)
11687         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11688
11689       ExplodeField[x][y] = EX_TYPE_NONE;
11690     }
11691
11692     game.explosions_delayed = TRUE;
11693   }
11694
11695   if (game.magic_wall_active)
11696   {
11697     if (!(game.magic_wall_time_left % 4))
11698     {
11699       int element = Feld[magic_wall_x][magic_wall_y];
11700
11701       if (element == EL_BD_MAGIC_WALL_FULL ||
11702           element == EL_BD_MAGIC_WALL_ACTIVE ||
11703           element == EL_BD_MAGIC_WALL_EMPTYING)
11704         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11705       else if (element == EL_DC_MAGIC_WALL_FULL ||
11706                element == EL_DC_MAGIC_WALL_ACTIVE ||
11707                element == EL_DC_MAGIC_WALL_EMPTYING)
11708         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11709       else
11710         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11711     }
11712
11713     if (game.magic_wall_time_left > 0)
11714     {
11715       game.magic_wall_time_left--;
11716       if (!game.magic_wall_time_left)
11717       {
11718         SCAN_PLAYFIELD(x, y)
11719         {
11720           element = Feld[x][y];
11721
11722           if (element == EL_MAGIC_WALL_ACTIVE ||
11723               element == EL_MAGIC_WALL_FULL)
11724           {
11725             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11726             DrawLevelField(x, y);
11727           }
11728           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11729                    element == EL_BD_MAGIC_WALL_FULL)
11730           {
11731             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11732             DrawLevelField(x, y);
11733           }
11734           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11735                    element == EL_DC_MAGIC_WALL_FULL)
11736           {
11737             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11738             DrawLevelField(x, y);
11739           }
11740         }
11741
11742         game.magic_wall_active = FALSE;
11743       }
11744     }
11745   }
11746
11747   if (game.light_time_left > 0)
11748   {
11749     game.light_time_left--;
11750
11751     if (game.light_time_left == 0)
11752       RedrawAllLightSwitchesAndInvisibleElements();
11753   }
11754
11755   if (game.timegate_time_left > 0)
11756   {
11757     game.timegate_time_left--;
11758
11759     if (game.timegate_time_left == 0)
11760       CloseAllOpenTimegates();
11761   }
11762
11763   if (game.lenses_time_left > 0)
11764   {
11765     game.lenses_time_left--;
11766
11767     if (game.lenses_time_left == 0)
11768       RedrawAllInvisibleElementsForLenses();
11769   }
11770
11771   if (game.magnify_time_left > 0)
11772   {
11773     game.magnify_time_left--;
11774
11775     if (game.magnify_time_left == 0)
11776       RedrawAllInvisibleElementsForMagnifier();
11777   }
11778
11779   for (i = 0; i < MAX_PLAYERS; i++)
11780   {
11781     struct PlayerInfo *player = &stored_player[i];
11782
11783     if (SHIELD_ON(player))
11784     {
11785       if (player->shield_deadly_time_left)
11786         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11787       else if (player->shield_normal_time_left)
11788         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11789     }
11790   }
11791
11792   CheckLevelTime();
11793
11794   DrawAllPlayers();
11795   PlayAllPlayersSound();
11796
11797   if (options.debug)                    /* calculate frames per second */
11798   {
11799     static unsigned long fps_counter = 0;
11800     static int fps_frames = 0;
11801     unsigned long fps_delay_ms = Counter() - fps_counter;
11802
11803     fps_frames++;
11804
11805     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11806     {
11807       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11808
11809       fps_frames = 0;
11810       fps_counter = Counter();
11811     }
11812
11813     redraw_mask |= REDRAW_FPS;
11814   }
11815
11816   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11817
11818   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11819   {
11820     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11821
11822     local_player->show_envelope = 0;
11823   }
11824
11825 #if 0
11826   debug_print_timestamp(0, "stop main loop profiling ");
11827   printf("----------------------------------------------------------\n");
11828 #endif
11829
11830   /* use random number generator in every frame to make it less predictable */
11831   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11832     RND(1);
11833 }
11834
11835 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11836 {
11837   int min_x = x, min_y = y, max_x = x, max_y = y;
11838   int i;
11839
11840   for (i = 0; i < MAX_PLAYERS; i++)
11841   {
11842     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11843
11844     if (!stored_player[i].active || &stored_player[i] == player)
11845       continue;
11846
11847     min_x = MIN(min_x, jx);
11848     min_y = MIN(min_y, jy);
11849     max_x = MAX(max_x, jx);
11850     max_y = MAX(max_y, jy);
11851   }
11852
11853   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11854 }
11855
11856 static boolean AllPlayersInVisibleScreen()
11857 {
11858   int i;
11859
11860   for (i = 0; i < MAX_PLAYERS; i++)
11861   {
11862     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11863
11864     if (!stored_player[i].active)
11865       continue;
11866
11867     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11868       return FALSE;
11869   }
11870
11871   return TRUE;
11872 }
11873
11874 void ScrollLevel(int dx, int dy)
11875 {
11876 #if 1
11877   static Bitmap *bitmap_db_field2 = NULL;
11878   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11879   int x, y;
11880 #else
11881   int i, x, y;
11882 #endif
11883
11884 #if 0
11885   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11886   /* only horizontal XOR vertical scroll direction allowed */
11887   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11888     return;
11889 #endif
11890
11891 #if 1
11892   if (bitmap_db_field2 == NULL)
11893     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11894
11895   /* needed when blitting directly to same bitmap -- should not be needed with
11896      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11897   BlitBitmap(drawto_field, bitmap_db_field2,
11898              FX + TILEX * (dx == -1) - softscroll_offset,
11899              FY + TILEY * (dy == -1) - softscroll_offset,
11900              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11901              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11902              FX + TILEX * (dx == 1) - softscroll_offset,
11903              FY + TILEY * (dy == 1) - softscroll_offset);
11904   BlitBitmap(bitmap_db_field2, drawto_field,
11905              FX + TILEX * (dx == 1) - softscroll_offset,
11906              FY + TILEY * (dy == 1) - softscroll_offset,
11907              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11908              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11909              FX + TILEX * (dx == 1) - softscroll_offset,
11910              FY + TILEY * (dy == 1) - softscroll_offset);
11911
11912 #else
11913
11914 #if 1
11915   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11916   int xsize = (BX2 - BX1 + 1);
11917   int ysize = (BY2 - BY1 + 1);
11918   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11919   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11920   int step  = (start < end ? +1 : -1);
11921
11922   for (i = start; i != end; i += step)
11923   {
11924     BlitBitmap(drawto_field, drawto_field,
11925                FX + TILEX * (dx != 0 ? i + step : 0),
11926                FY + TILEY * (dy != 0 ? i + step : 0),
11927                TILEX * (dx != 0 ? 1 : xsize),
11928                TILEY * (dy != 0 ? 1 : ysize),
11929                FX + TILEX * (dx != 0 ? i : 0),
11930                FY + TILEY * (dy != 0 ? i : 0));
11931   }
11932
11933 #else
11934
11935   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11936
11937   BlitBitmap(drawto_field, drawto_field,
11938              FX + TILEX * (dx == -1) - softscroll_offset,
11939              FY + TILEY * (dy == -1) - softscroll_offset,
11940              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11941              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11942              FX + TILEX * (dx == 1) - softscroll_offset,
11943              FY + TILEY * (dy == 1) - softscroll_offset);
11944 #endif
11945 #endif
11946
11947   if (dx != 0)
11948   {
11949     x = (dx == 1 ? BX1 : BX2);
11950     for (y = BY1; y <= BY2; y++)
11951       DrawScreenField(x, y);
11952   }
11953
11954   if (dy != 0)
11955   {
11956     y = (dy == 1 ? BY1 : BY2);
11957     for (x = BX1; x <= BX2; x++)
11958       DrawScreenField(x, y);
11959   }
11960
11961   redraw_mask |= REDRAW_FIELD;
11962 }
11963
11964 static boolean canFallDown(struct PlayerInfo *player)
11965 {
11966   int jx = player->jx, jy = player->jy;
11967
11968   return (IN_LEV_FIELD(jx, jy + 1) &&
11969           (IS_FREE(jx, jy + 1) ||
11970            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11971           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11972           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11973 }
11974
11975 static boolean canPassField(int x, int y, int move_dir)
11976 {
11977   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11978   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11979   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11980   int nextx = x + dx;
11981   int nexty = y + dy;
11982   int element = Feld[x][y];
11983
11984   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11985           !CAN_MOVE(element) &&
11986           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11987           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11988           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11989 }
11990
11991 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11992 {
11993   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11994   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11995   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11996   int newx = x + dx;
11997   int newy = y + dy;
11998
11999   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12000           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12001           (IS_DIGGABLE(Feld[newx][newy]) ||
12002            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12003            canPassField(newx, newy, move_dir)));
12004 }
12005
12006 static void CheckGravityMovement(struct PlayerInfo *player)
12007 {
12008 #if USE_PLAYER_GRAVITY
12009   if (player->gravity && !player->programmed_action)
12010 #else
12011   if (game.gravity && !player->programmed_action)
12012 #endif
12013   {
12014     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12015     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12016     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12017     int jx = player->jx, jy = player->jy;
12018     boolean player_is_moving_to_valid_field =
12019       (!player_is_snapping &&
12020        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12021         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12022     boolean player_can_fall_down = canFallDown(player);
12023
12024     if (player_can_fall_down &&
12025         !player_is_moving_to_valid_field)
12026       player->programmed_action = MV_DOWN;
12027   }
12028 }
12029
12030 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12031 {
12032   return CheckGravityMovement(player);
12033
12034 #if USE_PLAYER_GRAVITY
12035   if (player->gravity && !player->programmed_action)
12036 #else
12037   if (game.gravity && !player->programmed_action)
12038 #endif
12039   {
12040     int jx = player->jx, jy = player->jy;
12041     boolean field_under_player_is_free =
12042       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12043     boolean player_is_standing_on_valid_field =
12044       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12045        (IS_WALKABLE(Feld[jx][jy]) &&
12046         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12047
12048     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12049       player->programmed_action = MV_DOWN;
12050   }
12051 }
12052
12053 /*
12054   MovePlayerOneStep()
12055   -----------------------------------------------------------------------------
12056   dx, dy:               direction (non-diagonal) to try to move the player to
12057   real_dx, real_dy:     direction as read from input device (can be diagonal)
12058 */
12059
12060 boolean MovePlayerOneStep(struct PlayerInfo *player,
12061                           int dx, int dy, int real_dx, int real_dy)
12062 {
12063   int jx = player->jx, jy = player->jy;
12064   int new_jx = jx + dx, new_jy = jy + dy;
12065 #if !USE_FIXED_DONT_RUN_INTO
12066   int element;
12067 #endif
12068   int can_move;
12069   boolean player_can_move = !player->cannot_move;
12070
12071   if (!player->active || (!dx && !dy))
12072     return MP_NO_ACTION;
12073
12074   player->MovDir = (dx < 0 ? MV_LEFT :
12075                     dx > 0 ? MV_RIGHT :
12076                     dy < 0 ? MV_UP :
12077                     dy > 0 ? MV_DOWN :  MV_NONE);
12078
12079   if (!IN_LEV_FIELD(new_jx, new_jy))
12080     return MP_NO_ACTION;
12081
12082   if (!player_can_move)
12083   {
12084     if (player->MovPos == 0)
12085     {
12086       player->is_moving = FALSE;
12087       player->is_digging = FALSE;
12088       player->is_collecting = FALSE;
12089       player->is_snapping = FALSE;
12090       player->is_pushing = FALSE;
12091     }
12092   }
12093
12094 #if 1
12095   if (!options.network && game.centered_player_nr == -1 &&
12096       !AllPlayersInSight(player, new_jx, new_jy))
12097     return MP_NO_ACTION;
12098 #else
12099   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12100     return MP_NO_ACTION;
12101 #endif
12102
12103 #if !USE_FIXED_DONT_RUN_INTO
12104   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12105
12106   /* (moved to DigField()) */
12107   if (player_can_move && DONT_RUN_INTO(element))
12108   {
12109     if (element == EL_ACID && dx == 0 && dy == 1)
12110     {
12111       SplashAcid(new_jx, new_jy);
12112       Feld[jx][jy] = EL_PLAYER_1;
12113       InitMovingField(jx, jy, MV_DOWN);
12114       Store[jx][jy] = EL_ACID;
12115       ContinueMoving(jx, jy);
12116       BuryPlayer(player);
12117     }
12118     else
12119       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12120
12121     return MP_MOVING;
12122   }
12123 #endif
12124
12125   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12126   if (can_move != MP_MOVING)
12127     return can_move;
12128
12129   /* check if DigField() has caused relocation of the player */
12130   if (player->jx != jx || player->jy != jy)
12131     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12132
12133   StorePlayer[jx][jy] = 0;
12134   player->last_jx = jx;
12135   player->last_jy = jy;
12136   player->jx = new_jx;
12137   player->jy = new_jy;
12138   StorePlayer[new_jx][new_jy] = player->element_nr;
12139
12140   if (player->move_delay_value_next != -1)
12141   {
12142     player->move_delay_value = player->move_delay_value_next;
12143     player->move_delay_value_next = -1;
12144   }
12145
12146   player->MovPos =
12147     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12148
12149   player->step_counter++;
12150
12151   PlayerVisit[jx][jy] = FrameCounter;
12152
12153 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12154   player->is_moving = TRUE;
12155 #endif
12156
12157 #if 1
12158   /* should better be called in MovePlayer(), but this breaks some tapes */
12159   ScrollPlayer(player, SCROLL_INIT);
12160 #endif
12161
12162   return MP_MOVING;
12163 }
12164
12165 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12166 {
12167   int jx = player->jx, jy = player->jy;
12168   int old_jx = jx, old_jy = jy;
12169   int moved = MP_NO_ACTION;
12170
12171   if (!player->active)
12172     return FALSE;
12173
12174   if (!dx && !dy)
12175   {
12176     if (player->MovPos == 0)
12177     {
12178       player->is_moving = FALSE;
12179       player->is_digging = FALSE;
12180       player->is_collecting = FALSE;
12181       player->is_snapping = FALSE;
12182       player->is_pushing = FALSE;
12183     }
12184
12185     return FALSE;
12186   }
12187
12188   if (player->move_delay > 0)
12189     return FALSE;
12190
12191   player->move_delay = -1;              /* set to "uninitialized" value */
12192
12193   /* store if player is automatically moved to next field */
12194   player->is_auto_moving = (player->programmed_action != MV_NONE);
12195
12196   /* remove the last programmed player action */
12197   player->programmed_action = 0;
12198
12199   if (player->MovPos)
12200   {
12201     /* should only happen if pre-1.2 tape recordings are played */
12202     /* this is only for backward compatibility */
12203
12204     int original_move_delay_value = player->move_delay_value;
12205
12206 #if DEBUG
12207     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12208            tape.counter);
12209 #endif
12210
12211     /* scroll remaining steps with finest movement resolution */
12212     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12213
12214     while (player->MovPos)
12215     {
12216       ScrollPlayer(player, SCROLL_GO_ON);
12217       ScrollScreen(NULL, SCROLL_GO_ON);
12218
12219       AdvanceFrameAndPlayerCounters(player->index_nr);
12220
12221       DrawAllPlayers();
12222       BackToFront();
12223     }
12224
12225     player->move_delay_value = original_move_delay_value;
12226   }
12227
12228   player->is_active = FALSE;
12229
12230   if (player->last_move_dir & MV_HORIZONTAL)
12231   {
12232     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12233       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12234   }
12235   else
12236   {
12237     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12238       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12239   }
12240
12241 #if USE_FIXED_BORDER_RUNNING_GFX
12242   if (!moved && !player->is_active)
12243   {
12244     player->is_moving = FALSE;
12245     player->is_digging = FALSE;
12246     player->is_collecting = FALSE;
12247     player->is_snapping = FALSE;
12248     player->is_pushing = FALSE;
12249   }
12250 #endif
12251
12252   jx = player->jx;
12253   jy = player->jy;
12254
12255 #if 1
12256   if (moved & MP_MOVING && !ScreenMovPos &&
12257       (player->index_nr == game.centered_player_nr ||
12258        game.centered_player_nr == -1))
12259 #else
12260   if (moved & MP_MOVING && !ScreenMovPos &&
12261       (player == local_player || !options.network))
12262 #endif
12263   {
12264     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12265     int offset = game.scroll_delay_value;
12266
12267     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12268     {
12269       /* actual player has left the screen -- scroll in that direction */
12270       if (jx != old_jx)         /* player has moved horizontally */
12271         scroll_x += (jx - old_jx);
12272       else                      /* player has moved vertically */
12273         scroll_y += (jy - old_jy);
12274     }
12275     else
12276     {
12277       if (jx != old_jx)         /* player has moved horizontally */
12278       {
12279         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12280             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12281           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12282
12283         /* don't scroll over playfield boundaries */
12284         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12285           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12286
12287         /* don't scroll more than one field at a time */
12288         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12289
12290         /* don't scroll against the player's moving direction */
12291         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12292             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12293           scroll_x = old_scroll_x;
12294       }
12295       else                      /* player has moved vertically */
12296       {
12297         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12298             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12299           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12300
12301         /* don't scroll over playfield boundaries */
12302         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12303           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12304
12305         /* don't scroll more than one field at a time */
12306         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12307
12308         /* don't scroll against the player's moving direction */
12309         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12310             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12311           scroll_y = old_scroll_y;
12312       }
12313     }
12314
12315     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12316     {
12317 #if 1
12318       if (!options.network && game.centered_player_nr == -1 &&
12319           !AllPlayersInVisibleScreen())
12320       {
12321         scroll_x = old_scroll_x;
12322         scroll_y = old_scroll_y;
12323       }
12324       else
12325 #else
12326       if (!options.network && !AllPlayersInVisibleScreen())
12327       {
12328         scroll_x = old_scroll_x;
12329         scroll_y = old_scroll_y;
12330       }
12331       else
12332 #endif
12333       {
12334         ScrollScreen(player, SCROLL_INIT);
12335         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12336       }
12337     }
12338   }
12339
12340   player->StepFrame = 0;
12341
12342   if (moved & MP_MOVING)
12343   {
12344     if (old_jx != jx && old_jy == jy)
12345       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12346     else if (old_jx == jx && old_jy != jy)
12347       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12348
12349     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12350
12351     player->last_move_dir = player->MovDir;
12352     player->is_moving = TRUE;
12353     player->is_snapping = FALSE;
12354     player->is_switching = FALSE;
12355     player->is_dropping = FALSE;
12356     player->is_dropping_pressed = FALSE;
12357     player->drop_pressed_delay = 0;
12358
12359 #if 0
12360     /* should better be called here than above, but this breaks some tapes */
12361     ScrollPlayer(player, SCROLL_INIT);
12362 #endif
12363   }
12364   else
12365   {
12366     CheckGravityMovementWhenNotMoving(player);
12367
12368     player->is_moving = FALSE;
12369
12370     /* at this point, the player is allowed to move, but cannot move right now
12371        (e.g. because of something blocking the way) -- ensure that the player
12372        is also allowed to move in the next frame (in old versions before 3.1.1,
12373        the player was forced to wait again for eight frames before next try) */
12374
12375     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12376       player->move_delay = 0;   /* allow direct movement in the next frame */
12377   }
12378
12379   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12380     player->move_delay = player->move_delay_value;
12381
12382   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12383   {
12384     TestIfPlayerTouchesBadThing(jx, jy);
12385     TestIfPlayerTouchesCustomElement(jx, jy);
12386   }
12387
12388   if (!player->active)
12389     RemovePlayer(player);
12390
12391   return moved;
12392 }
12393
12394 void ScrollPlayer(struct PlayerInfo *player, int mode)
12395 {
12396   int jx = player->jx, jy = player->jy;
12397   int last_jx = player->last_jx, last_jy = player->last_jy;
12398   int move_stepsize = TILEX / player->move_delay_value;
12399
12400 #if USE_NEW_PLAYER_SPEED
12401   if (!player->active)
12402     return;
12403
12404   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12405     return;
12406 #else
12407   if (!player->active || player->MovPos == 0)
12408     return;
12409 #endif
12410
12411   if (mode == SCROLL_INIT)
12412   {
12413     player->actual_frame_counter = FrameCounter;
12414     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12415
12416     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12417         Feld[last_jx][last_jy] == EL_EMPTY)
12418     {
12419       int last_field_block_delay = 0;   /* start with no blocking at all */
12420       int block_delay_adjustment = player->block_delay_adjustment;
12421
12422       /* if player blocks last field, add delay for exactly one move */
12423       if (player->block_last_field)
12424       {
12425         last_field_block_delay += player->move_delay_value;
12426
12427         /* when blocking enabled, prevent moving up despite gravity */
12428 #if USE_PLAYER_GRAVITY
12429         if (player->gravity && player->MovDir == MV_UP)
12430           block_delay_adjustment = -1;
12431 #else
12432         if (game.gravity && player->MovDir == MV_UP)
12433           block_delay_adjustment = -1;
12434 #endif
12435       }
12436
12437       /* add block delay adjustment (also possible when not blocking) */
12438       last_field_block_delay += block_delay_adjustment;
12439
12440       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12441       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12442     }
12443
12444 #if USE_NEW_PLAYER_SPEED
12445     if (player->MovPos != 0)    /* player has not yet reached destination */
12446       return;
12447 #else
12448     return;
12449 #endif
12450   }
12451   else if (!FrameReached(&player->actual_frame_counter, 1))
12452     return;
12453
12454 #if USE_NEW_PLAYER_SPEED
12455   if (player->MovPos != 0)
12456   {
12457     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12458     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12459
12460     /* before DrawPlayer() to draw correct player graphic for this case */
12461     if (player->MovPos == 0)
12462       CheckGravityMovement(player);
12463   }
12464 #else
12465   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12466   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12467
12468   /* before DrawPlayer() to draw correct player graphic for this case */
12469   if (player->MovPos == 0)
12470     CheckGravityMovement(player);
12471 #endif
12472
12473   if (player->MovPos == 0)      /* player reached destination field */
12474   {
12475     if (player->move_delay_reset_counter > 0)
12476     {
12477       player->move_delay_reset_counter--;
12478
12479       if (player->move_delay_reset_counter == 0)
12480       {
12481         /* continue with normal speed after quickly moving through gate */
12482         HALVE_PLAYER_SPEED(player);
12483
12484         /* be able to make the next move without delay */
12485         player->move_delay = 0;
12486       }
12487     }
12488
12489     player->last_jx = jx;
12490     player->last_jy = jy;
12491
12492     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12493         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12494         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12495         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12496         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12497         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12498     {
12499       DrawPlayer(player);       /* needed here only to cleanup last field */
12500       RemovePlayer(player);
12501
12502       if (local_player->friends_still_needed == 0 ||
12503           IS_SP_ELEMENT(Feld[jx][jy]))
12504         PlayerWins(player);
12505     }
12506
12507     /* this breaks one level: "machine", level 000 */
12508     {
12509       int move_direction = player->MovDir;
12510       int enter_side = MV_DIR_OPPOSITE(move_direction);
12511       int leave_side = move_direction;
12512       int old_jx = last_jx;
12513       int old_jy = last_jy;
12514       int old_element = Feld[old_jx][old_jy];
12515       int new_element = Feld[jx][jy];
12516
12517       if (IS_CUSTOM_ELEMENT(old_element))
12518         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12519                                    CE_LEFT_BY_PLAYER,
12520                                    player->index_bit, leave_side);
12521
12522       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12523                                           CE_PLAYER_LEAVES_X,
12524                                           player->index_bit, leave_side);
12525
12526       if (IS_CUSTOM_ELEMENT(new_element))
12527         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12528                                    player->index_bit, enter_side);
12529
12530       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12531                                           CE_PLAYER_ENTERS_X,
12532                                           player->index_bit, enter_side);
12533
12534       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12535                                         CE_MOVE_OF_X, move_direction);
12536     }
12537
12538     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12539     {
12540       TestIfPlayerTouchesBadThing(jx, jy);
12541       TestIfPlayerTouchesCustomElement(jx, jy);
12542
12543       /* needed because pushed element has not yet reached its destination,
12544          so it would trigger a change event at its previous field location */
12545       if (!player->is_pushing)
12546         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12547
12548       if (!player->active)
12549         RemovePlayer(player);
12550     }
12551
12552     if (!local_player->LevelSolved && level.use_step_counter)
12553     {
12554       int i;
12555
12556       TimePlayed++;
12557
12558       if (TimeLeft > 0)
12559       {
12560         TimeLeft--;
12561
12562         if (TimeLeft <= 10 && setup.time_limit)
12563           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12564
12565 #if 1
12566         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12567
12568         DisplayGameControlValues();
12569 #else
12570         DrawGameValue_Time(TimeLeft);
12571 #endif
12572
12573         if (!TimeLeft && setup.time_limit)
12574           for (i = 0; i < MAX_PLAYERS; i++)
12575             KillPlayer(&stored_player[i]);
12576       }
12577 #if 1
12578       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12579       {
12580         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12581
12582         DisplayGameControlValues();
12583       }
12584 #else
12585       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12586         DrawGameValue_Time(TimePlayed);
12587 #endif
12588     }
12589
12590     if (tape.single_step && tape.recording && !tape.pausing &&
12591         !player->programmed_action)
12592       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12593   }
12594 }
12595
12596 void ScrollScreen(struct PlayerInfo *player, int mode)
12597 {
12598   static unsigned long screen_frame_counter = 0;
12599
12600   if (mode == SCROLL_INIT)
12601   {
12602     /* set scrolling step size according to actual player's moving speed */
12603     ScrollStepSize = TILEX / player->move_delay_value;
12604
12605     screen_frame_counter = FrameCounter;
12606     ScreenMovDir = player->MovDir;
12607     ScreenMovPos = player->MovPos;
12608     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12609     return;
12610   }
12611   else if (!FrameReached(&screen_frame_counter, 1))
12612     return;
12613
12614   if (ScreenMovPos)
12615   {
12616     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12617     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12618     redraw_mask |= REDRAW_FIELD;
12619   }
12620   else
12621     ScreenMovDir = MV_NONE;
12622 }
12623
12624 void TestIfPlayerTouchesCustomElement(int x, int y)
12625 {
12626   static int xy[4][2] =
12627   {
12628     { 0, -1 },
12629     { -1, 0 },
12630     { +1, 0 },
12631     { 0, +1 }
12632   };
12633   static int trigger_sides[4][2] =
12634   {
12635     /* center side       border side */
12636     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12637     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12638     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12639     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12640   };
12641   static int touch_dir[4] =
12642   {
12643     MV_LEFT | MV_RIGHT,
12644     MV_UP   | MV_DOWN,
12645     MV_UP   | MV_DOWN,
12646     MV_LEFT | MV_RIGHT
12647   };
12648   int center_element = Feld[x][y];      /* should always be non-moving! */
12649   int i;
12650
12651   for (i = 0; i < NUM_DIRECTIONS; i++)
12652   {
12653     int xx = x + xy[i][0];
12654     int yy = y + xy[i][1];
12655     int center_side = trigger_sides[i][0];
12656     int border_side = trigger_sides[i][1];
12657     int border_element;
12658
12659     if (!IN_LEV_FIELD(xx, yy))
12660       continue;
12661
12662     if (IS_PLAYER(x, y))
12663     {
12664       struct PlayerInfo *player = PLAYERINFO(x, y);
12665
12666       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12667         border_element = Feld[xx][yy];          /* may be moving! */
12668       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12669         border_element = Feld[xx][yy];
12670       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12671         border_element = MovingOrBlocked2Element(xx, yy);
12672       else
12673         continue;               /* center and border element do not touch */
12674
12675       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12676                                  player->index_bit, border_side);
12677       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12678                                           CE_PLAYER_TOUCHES_X,
12679                                           player->index_bit, border_side);
12680     }
12681     else if (IS_PLAYER(xx, yy))
12682     {
12683       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12684
12685       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12686       {
12687         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12688           continue;             /* center and border element do not touch */
12689       }
12690
12691       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12692                                  player->index_bit, center_side);
12693       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12694                                           CE_PLAYER_TOUCHES_X,
12695                                           player->index_bit, center_side);
12696       break;
12697     }
12698   }
12699 }
12700
12701 #if USE_ELEMENT_TOUCHING_BUGFIX
12702
12703 void TestIfElementTouchesCustomElement(int x, int y)
12704 {
12705   static int xy[4][2] =
12706   {
12707     { 0, -1 },
12708     { -1, 0 },
12709     { +1, 0 },
12710     { 0, +1 }
12711   };
12712   static int trigger_sides[4][2] =
12713   {
12714     /* center side      border side */
12715     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12716     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12717     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12718     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12719   };
12720   static int touch_dir[4] =
12721   {
12722     MV_LEFT | MV_RIGHT,
12723     MV_UP   | MV_DOWN,
12724     MV_UP   | MV_DOWN,
12725     MV_LEFT | MV_RIGHT
12726   };
12727   boolean change_center_element = FALSE;
12728   int center_element = Feld[x][y];      /* should always be non-moving! */
12729   int border_element_old[NUM_DIRECTIONS];
12730   int i;
12731
12732   for (i = 0; i < NUM_DIRECTIONS; i++)
12733   {
12734     int xx = x + xy[i][0];
12735     int yy = y + xy[i][1];
12736     int border_element;
12737
12738     border_element_old[i] = -1;
12739
12740     if (!IN_LEV_FIELD(xx, yy))
12741       continue;
12742
12743     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12744       border_element = Feld[xx][yy];    /* may be moving! */
12745     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12746       border_element = Feld[xx][yy];
12747     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12748       border_element = MovingOrBlocked2Element(xx, yy);
12749     else
12750       continue;                 /* center and border element do not touch */
12751
12752     border_element_old[i] = border_element;
12753   }
12754
12755   for (i = 0; i < NUM_DIRECTIONS; i++)
12756   {
12757     int xx = x + xy[i][0];
12758     int yy = y + xy[i][1];
12759     int center_side = trigger_sides[i][0];
12760     int border_element = border_element_old[i];
12761
12762     if (border_element == -1)
12763       continue;
12764
12765     /* check for change of border element */
12766     CheckElementChangeBySide(xx, yy, border_element, center_element,
12767                              CE_TOUCHING_X, center_side);
12768   }
12769
12770   for (i = 0; i < NUM_DIRECTIONS; i++)
12771   {
12772     int border_side = trigger_sides[i][1];
12773     int border_element = border_element_old[i];
12774
12775     if (border_element == -1)
12776       continue;
12777
12778     /* check for change of center element (but change it only once) */
12779     if (!change_center_element)
12780       change_center_element =
12781         CheckElementChangeBySide(x, y, center_element, border_element,
12782                                  CE_TOUCHING_X, border_side);
12783   }
12784 }
12785
12786 #else
12787
12788 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12789 {
12790   static int xy[4][2] =
12791   {
12792     { 0, -1 },
12793     { -1, 0 },
12794     { +1, 0 },
12795     { 0, +1 }
12796   };
12797   static int trigger_sides[4][2] =
12798   {
12799     /* center side      border side */
12800     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12801     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12802     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12803     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12804   };
12805   static int touch_dir[4] =
12806   {
12807     MV_LEFT | MV_RIGHT,
12808     MV_UP   | MV_DOWN,
12809     MV_UP   | MV_DOWN,
12810     MV_LEFT | MV_RIGHT
12811   };
12812   boolean change_center_element = FALSE;
12813   int center_element = Feld[x][y];      /* should always be non-moving! */
12814   int i;
12815
12816   for (i = 0; i < NUM_DIRECTIONS; i++)
12817   {
12818     int xx = x + xy[i][0];
12819     int yy = y + xy[i][1];
12820     int center_side = trigger_sides[i][0];
12821     int border_side = trigger_sides[i][1];
12822     int border_element;
12823
12824     if (!IN_LEV_FIELD(xx, yy))
12825       continue;
12826
12827     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12828       border_element = Feld[xx][yy];    /* may be moving! */
12829     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12830       border_element = Feld[xx][yy];
12831     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12832       border_element = MovingOrBlocked2Element(xx, yy);
12833     else
12834       continue;                 /* center and border element do not touch */
12835
12836     /* check for change of center element (but change it only once) */
12837     if (!change_center_element)
12838       change_center_element =
12839         CheckElementChangeBySide(x, y, center_element, border_element,
12840                                  CE_TOUCHING_X, border_side);
12841
12842     /* check for change of border element */
12843     CheckElementChangeBySide(xx, yy, border_element, center_element,
12844                              CE_TOUCHING_X, center_side);
12845   }
12846 }
12847
12848 #endif
12849
12850 void TestIfElementHitsCustomElement(int x, int y, int direction)
12851 {
12852   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12853   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12854   int hitx = x + dx, hity = y + dy;
12855   int hitting_element = Feld[x][y];
12856   int touched_element;
12857
12858   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12859     return;
12860
12861   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12862                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12863
12864   if (IN_LEV_FIELD(hitx, hity))
12865   {
12866     int opposite_direction = MV_DIR_OPPOSITE(direction);
12867     int hitting_side = direction;
12868     int touched_side = opposite_direction;
12869     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12870                           MovDir[hitx][hity] != direction ||
12871                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12872
12873     object_hit = TRUE;
12874
12875     if (object_hit)
12876     {
12877       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12878                                CE_HITTING_X, touched_side);
12879
12880       CheckElementChangeBySide(hitx, hity, touched_element,
12881                                hitting_element, CE_HIT_BY_X, hitting_side);
12882
12883       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12884                                CE_HIT_BY_SOMETHING, opposite_direction);
12885     }
12886   }
12887
12888   /* "hitting something" is also true when hitting the playfield border */
12889   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12890                            CE_HITTING_SOMETHING, direction);
12891 }
12892
12893 #if 0
12894 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12895 {
12896   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12897   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12898   int hitx = x + dx, hity = y + dy;
12899   int hitting_element = Feld[x][y];
12900   int touched_element;
12901 #if 0
12902   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12903                         !IS_FREE(hitx, hity) &&
12904                         (!IS_MOVING(hitx, hity) ||
12905                          MovDir[hitx][hity] != direction ||
12906                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12907 #endif
12908
12909   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12910     return;
12911
12912 #if 0
12913   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12914     return;
12915 #endif
12916
12917   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12918                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12919
12920   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12921                            EP_CAN_SMASH_EVERYTHING, direction);
12922
12923   if (IN_LEV_FIELD(hitx, hity))
12924   {
12925     int opposite_direction = MV_DIR_OPPOSITE(direction);
12926     int hitting_side = direction;
12927     int touched_side = opposite_direction;
12928 #if 0
12929     int touched_element = MovingOrBlocked2Element(hitx, hity);
12930 #endif
12931 #if 1
12932     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12933                           MovDir[hitx][hity] != direction ||
12934                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12935
12936     object_hit = TRUE;
12937 #endif
12938
12939     if (object_hit)
12940     {
12941       int i;
12942
12943       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12944                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12945
12946       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12947                                CE_OTHER_IS_SMASHING, touched_side);
12948
12949       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12950                                CE_OTHER_GETS_SMASHED, hitting_side);
12951     }
12952   }
12953 }
12954 #endif
12955
12956 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12957 {
12958   int i, kill_x = -1, kill_y = -1;
12959
12960   int bad_element = -1;
12961   static int test_xy[4][2] =
12962   {
12963     { 0, -1 },
12964     { -1, 0 },
12965     { +1, 0 },
12966     { 0, +1 }
12967   };
12968   static int test_dir[4] =
12969   {
12970     MV_UP,
12971     MV_LEFT,
12972     MV_RIGHT,
12973     MV_DOWN
12974   };
12975
12976   for (i = 0; i < NUM_DIRECTIONS; i++)
12977   {
12978     int test_x, test_y, test_move_dir, test_element;
12979
12980     test_x = good_x + test_xy[i][0];
12981     test_y = good_y + test_xy[i][1];
12982
12983     if (!IN_LEV_FIELD(test_x, test_y))
12984       continue;
12985
12986     test_move_dir =
12987       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12988
12989     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12990
12991     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12992        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12993     */
12994     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12995         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12996     {
12997       kill_x = test_x;
12998       kill_y = test_y;
12999       bad_element = test_element;
13000
13001       break;
13002     }
13003   }
13004
13005   if (kill_x != -1 || kill_y != -1)
13006   {
13007     if (IS_PLAYER(good_x, good_y))
13008     {
13009       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13010
13011       if (player->shield_deadly_time_left > 0 &&
13012           !IS_INDESTRUCTIBLE(bad_element))
13013         Bang(kill_x, kill_y);
13014       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13015         KillPlayer(player);
13016     }
13017     else
13018       Bang(good_x, good_y);
13019   }
13020 }
13021
13022 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13023 {
13024   int i, kill_x = -1, kill_y = -1;
13025   int bad_element = Feld[bad_x][bad_y];
13026   static int test_xy[4][2] =
13027   {
13028     { 0, -1 },
13029     { -1, 0 },
13030     { +1, 0 },
13031     { 0, +1 }
13032   };
13033   static int touch_dir[4] =
13034   {
13035     MV_LEFT | MV_RIGHT,
13036     MV_UP   | MV_DOWN,
13037     MV_UP   | MV_DOWN,
13038     MV_LEFT | MV_RIGHT
13039   };
13040   static int test_dir[4] =
13041   {
13042     MV_UP,
13043     MV_LEFT,
13044     MV_RIGHT,
13045     MV_DOWN
13046   };
13047
13048   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13049     return;
13050
13051   for (i = 0; i < NUM_DIRECTIONS; i++)
13052   {
13053     int test_x, test_y, test_move_dir, test_element;
13054
13055     test_x = bad_x + test_xy[i][0];
13056     test_y = bad_y + test_xy[i][1];
13057     if (!IN_LEV_FIELD(test_x, test_y))
13058       continue;
13059
13060     test_move_dir =
13061       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13062
13063     test_element = Feld[test_x][test_y];
13064
13065     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13066        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13067     */
13068     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13069         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13070     {
13071       /* good thing is player or penguin that does not move away */
13072       if (IS_PLAYER(test_x, test_y))
13073       {
13074         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13075
13076         if (bad_element == EL_ROBOT && player->is_moving)
13077           continue;     /* robot does not kill player if he is moving */
13078
13079         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13080         {
13081           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13082             continue;           /* center and border element do not touch */
13083         }
13084
13085         kill_x = test_x;
13086         kill_y = test_y;
13087         break;
13088       }
13089       else if (test_element == EL_PENGUIN)
13090       {
13091         kill_x = test_x;
13092         kill_y = test_y;
13093         break;
13094       }
13095     }
13096   }
13097
13098   if (kill_x != -1 || kill_y != -1)
13099   {
13100     if (IS_PLAYER(kill_x, kill_y))
13101     {
13102       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13103
13104       if (player->shield_deadly_time_left > 0 &&
13105           !IS_INDESTRUCTIBLE(bad_element))
13106         Bang(bad_x, bad_y);
13107       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13108         KillPlayer(player);
13109     }
13110     else
13111       Bang(kill_x, kill_y);
13112   }
13113 }
13114
13115 void TestIfPlayerTouchesBadThing(int x, int y)
13116 {
13117   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13118 }
13119
13120 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13121 {
13122   TestIfGoodThingHitsBadThing(x, y, move_dir);
13123 }
13124
13125 void TestIfBadThingTouchesPlayer(int x, int y)
13126 {
13127   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13128 }
13129
13130 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13131 {
13132   TestIfBadThingHitsGoodThing(x, y, move_dir);
13133 }
13134
13135 void TestIfFriendTouchesBadThing(int x, int y)
13136 {
13137   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13138 }
13139
13140 void TestIfBadThingTouchesFriend(int x, int y)
13141 {
13142   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13143 }
13144
13145 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13146 {
13147   int i, kill_x = bad_x, kill_y = bad_y;
13148   static int xy[4][2] =
13149   {
13150     { 0, -1 },
13151     { -1, 0 },
13152     { +1, 0 },
13153     { 0, +1 }
13154   };
13155
13156   for (i = 0; i < NUM_DIRECTIONS; i++)
13157   {
13158     int x, y, element;
13159
13160     x = bad_x + xy[i][0];
13161     y = bad_y + xy[i][1];
13162     if (!IN_LEV_FIELD(x, y))
13163       continue;
13164
13165     element = Feld[x][y];
13166     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13167         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13168     {
13169       kill_x = x;
13170       kill_y = y;
13171       break;
13172     }
13173   }
13174
13175   if (kill_x != bad_x || kill_y != bad_y)
13176     Bang(bad_x, bad_y);
13177 }
13178
13179 void KillPlayer(struct PlayerInfo *player)
13180 {
13181   int jx = player->jx, jy = player->jy;
13182
13183   if (!player->active)
13184     return;
13185
13186   /* the following code was introduced to prevent an infinite loop when calling
13187      -> Bang()
13188      -> CheckTriggeredElementChangeExt()
13189      -> ExecuteCustomElementAction()
13190      -> KillPlayer()
13191      -> (infinitely repeating the above sequence of function calls)
13192      which occurs when killing the player while having a CE with the setting
13193      "kill player X when explosion of <player X>"; the solution using a new
13194      field "player->killed" was chosen for backwards compatibility, although
13195      clever use of the fields "player->active" etc. would probably also work */
13196 #if 1
13197   if (player->killed)
13198     return;
13199 #endif
13200
13201   player->killed = TRUE;
13202
13203   /* remove accessible field at the player's position */
13204   Feld[jx][jy] = EL_EMPTY;
13205
13206   /* deactivate shield (else Bang()/Explode() would not work right) */
13207   player->shield_normal_time_left = 0;
13208   player->shield_deadly_time_left = 0;
13209
13210   Bang(jx, jy);
13211   BuryPlayer(player);
13212 }
13213
13214 static void KillPlayerUnlessEnemyProtected(int x, int y)
13215 {
13216   if (!PLAYER_ENEMY_PROTECTED(x, y))
13217     KillPlayer(PLAYERINFO(x, y));
13218 }
13219
13220 static void KillPlayerUnlessExplosionProtected(int x, int y)
13221 {
13222   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13223     KillPlayer(PLAYERINFO(x, y));
13224 }
13225
13226 void BuryPlayer(struct PlayerInfo *player)
13227 {
13228   int jx = player->jx, jy = player->jy;
13229
13230   if (!player->active)
13231     return;
13232
13233   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13234   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13235
13236   player->GameOver = TRUE;
13237   RemovePlayer(player);
13238 }
13239
13240 void RemovePlayer(struct PlayerInfo *player)
13241 {
13242   int jx = player->jx, jy = player->jy;
13243   int i, found = FALSE;
13244
13245   player->present = FALSE;
13246   player->active = FALSE;
13247
13248   if (!ExplodeField[jx][jy])
13249     StorePlayer[jx][jy] = 0;
13250
13251   if (player->is_moving)
13252     DrawLevelField(player->last_jx, player->last_jy);
13253
13254   for (i = 0; i < MAX_PLAYERS; i++)
13255     if (stored_player[i].active)
13256       found = TRUE;
13257
13258   if (!found)
13259     AllPlayersGone = TRUE;
13260
13261   ExitX = ZX = jx;
13262   ExitY = ZY = jy;
13263 }
13264
13265 #if USE_NEW_SNAP_DELAY
13266 static void setFieldForSnapping(int x, int y, int element, int direction)
13267 {
13268   struct ElementInfo *ei = &element_info[element];
13269   int direction_bit = MV_DIR_TO_BIT(direction);
13270   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13271   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13272                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13273
13274   Feld[x][y] = EL_ELEMENT_SNAPPING;
13275   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13276
13277   ResetGfxAnimation(x, y);
13278
13279   GfxElement[x][y] = element;
13280   GfxAction[x][y] = action;
13281   GfxDir[x][y] = direction;
13282   GfxFrame[x][y] = -1;
13283 }
13284 #endif
13285
13286 /*
13287   =============================================================================
13288   checkDiagonalPushing()
13289   -----------------------------------------------------------------------------
13290   check if diagonal input device direction results in pushing of object
13291   (by checking if the alternative direction is walkable, diggable, ...)
13292   =============================================================================
13293 */
13294
13295 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13296                                     int x, int y, int real_dx, int real_dy)
13297 {
13298   int jx, jy, dx, dy, xx, yy;
13299
13300   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13301     return TRUE;
13302
13303   /* diagonal direction: check alternative direction */
13304   jx = player->jx;
13305   jy = player->jy;
13306   dx = x - jx;
13307   dy = y - jy;
13308   xx = jx + (dx == 0 ? real_dx : 0);
13309   yy = jy + (dy == 0 ? real_dy : 0);
13310
13311   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13312 }
13313
13314 /*
13315   =============================================================================
13316   DigField()
13317   -----------------------------------------------------------------------------
13318   x, y:                 field next to player (non-diagonal) to try to dig to
13319   real_dx, real_dy:     direction as read from input device (can be diagonal)
13320   =============================================================================
13321 */
13322
13323 int DigField(struct PlayerInfo *player,
13324              int oldx, int oldy, int x, int y,
13325              int real_dx, int real_dy, int mode)
13326 {
13327   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13328   boolean player_was_pushing = player->is_pushing;
13329   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13330   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13331   int jx = oldx, jy = oldy;
13332   int dx = x - jx, dy = y - jy;
13333   int nextx = x + dx, nexty = y + dy;
13334   int move_direction = (dx == -1 ? MV_LEFT  :
13335                         dx == +1 ? MV_RIGHT :
13336                         dy == -1 ? MV_UP    :
13337                         dy == +1 ? MV_DOWN  : MV_NONE);
13338   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13339   int dig_side = MV_DIR_OPPOSITE(move_direction);
13340   int old_element = Feld[jx][jy];
13341 #if USE_FIXED_DONT_RUN_INTO
13342   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13343 #else
13344   int element;
13345 #endif
13346   int collect_count;
13347
13348   if (is_player)                /* function can also be called by EL_PENGUIN */
13349   {
13350     if (player->MovPos == 0)
13351     {
13352       player->is_digging = FALSE;
13353       player->is_collecting = FALSE;
13354     }
13355
13356     if (player->MovPos == 0)    /* last pushing move finished */
13357       player->is_pushing = FALSE;
13358
13359     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13360     {
13361       player->is_switching = FALSE;
13362       player->push_delay = -1;
13363
13364       return MP_NO_ACTION;
13365     }
13366   }
13367
13368 #if !USE_FIXED_DONT_RUN_INTO
13369   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13370     return MP_NO_ACTION;
13371 #endif
13372
13373   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13374     old_element = Back[jx][jy];
13375
13376   /* in case of element dropped at player position, check background */
13377   else if (Back[jx][jy] != EL_EMPTY &&
13378            game.engine_version >= VERSION_IDENT(2,2,0,0))
13379     old_element = Back[jx][jy];
13380
13381   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13382     return MP_NO_ACTION;        /* field has no opening in this direction */
13383
13384   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13385     return MP_NO_ACTION;        /* field has no opening in this direction */
13386
13387 #if USE_FIXED_DONT_RUN_INTO
13388   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13389   {
13390     SplashAcid(x, y);
13391
13392     Feld[jx][jy] = player->artwork_element;
13393     InitMovingField(jx, jy, MV_DOWN);
13394     Store[jx][jy] = EL_ACID;
13395     ContinueMoving(jx, jy);
13396     BuryPlayer(player);
13397
13398     return MP_DONT_RUN_INTO;
13399   }
13400 #endif
13401
13402 #if USE_FIXED_DONT_RUN_INTO
13403   if (player_can_move && DONT_RUN_INTO(element))
13404   {
13405     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13406
13407     return MP_DONT_RUN_INTO;
13408   }
13409 #endif
13410
13411 #if USE_FIXED_DONT_RUN_INTO
13412   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13413     return MP_NO_ACTION;
13414 #endif
13415
13416 #if !USE_FIXED_DONT_RUN_INTO
13417   element = Feld[x][y];
13418 #endif
13419
13420   collect_count = element_info[element].collect_count_initial;
13421
13422   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13423     return MP_NO_ACTION;
13424
13425   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13426     player_can_move = player_can_move_or_snap;
13427
13428   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13429       game.engine_version >= VERSION_IDENT(2,2,0,0))
13430   {
13431     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13432                                player->index_bit, dig_side);
13433     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13434                                         player->index_bit, dig_side);
13435
13436     if (element == EL_DC_LANDMINE)
13437       Bang(x, y);
13438
13439     if (Feld[x][y] != element)          /* field changed by snapping */
13440       return MP_ACTION;
13441
13442     return MP_NO_ACTION;
13443   }
13444
13445 #if USE_PLAYER_GRAVITY
13446   if (player->gravity && is_player && !player->is_auto_moving &&
13447       canFallDown(player) && move_direction != MV_DOWN &&
13448       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13449     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13450 #else
13451   if (game.gravity && is_player && !player->is_auto_moving &&
13452       canFallDown(player) && move_direction != MV_DOWN &&
13453       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13454     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13455 #endif
13456
13457   if (player_can_move &&
13458       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13459   {
13460     int sound_element = SND_ELEMENT(element);
13461     int sound_action = ACTION_WALKING;
13462
13463     if (IS_RND_GATE(element))
13464     {
13465       if (!player->key[RND_GATE_NR(element)])
13466         return MP_NO_ACTION;
13467     }
13468     else if (IS_RND_GATE_GRAY(element))
13469     {
13470       if (!player->key[RND_GATE_GRAY_NR(element)])
13471         return MP_NO_ACTION;
13472     }
13473     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13474     {
13475       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13476         return MP_NO_ACTION;
13477     }
13478     else if (element == EL_EXIT_OPEN ||
13479              element == EL_EM_EXIT_OPEN ||
13480              element == EL_STEEL_EXIT_OPEN ||
13481              element == EL_EM_STEEL_EXIT_OPEN ||
13482              element == EL_SP_EXIT_OPEN ||
13483              element == EL_SP_EXIT_OPENING)
13484     {
13485       sound_action = ACTION_PASSING;    /* player is passing exit */
13486     }
13487     else if (element == EL_EMPTY)
13488     {
13489       sound_action = ACTION_MOVING;             /* nothing to walk on */
13490     }
13491
13492     /* play sound from background or player, whatever is available */
13493     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13494       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13495     else
13496       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13497   }
13498   else if (player_can_move &&
13499            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13500   {
13501     if (!ACCESS_FROM(element, opposite_direction))
13502       return MP_NO_ACTION;      /* field not accessible from this direction */
13503
13504     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13505       return MP_NO_ACTION;
13506
13507     if (IS_EM_GATE(element))
13508     {
13509       if (!player->key[EM_GATE_NR(element)])
13510         return MP_NO_ACTION;
13511     }
13512     else if (IS_EM_GATE_GRAY(element))
13513     {
13514       if (!player->key[EM_GATE_GRAY_NR(element)])
13515         return MP_NO_ACTION;
13516     }
13517     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13518     {
13519       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13520         return MP_NO_ACTION;
13521     }
13522     else if (IS_EMC_GATE(element))
13523     {
13524       if (!player->key[EMC_GATE_NR(element)])
13525         return MP_NO_ACTION;
13526     }
13527     else if (IS_EMC_GATE_GRAY(element))
13528     {
13529       if (!player->key[EMC_GATE_GRAY_NR(element)])
13530         return MP_NO_ACTION;
13531     }
13532     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13533     {
13534       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13535         return MP_NO_ACTION;
13536     }
13537     else if (element == EL_DC_GATE_WHITE ||
13538              element == EL_DC_GATE_WHITE_GRAY ||
13539              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13540     {
13541       if (player->num_white_keys == 0)
13542         return MP_NO_ACTION;
13543
13544       player->num_white_keys--;
13545     }
13546     else if (IS_SP_PORT(element))
13547     {
13548       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13549           element == EL_SP_GRAVITY_PORT_RIGHT ||
13550           element == EL_SP_GRAVITY_PORT_UP ||
13551           element == EL_SP_GRAVITY_PORT_DOWN)
13552 #if USE_PLAYER_GRAVITY
13553         player->gravity = !player->gravity;
13554 #else
13555         game.gravity = !game.gravity;
13556 #endif
13557       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13558                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13559                element == EL_SP_GRAVITY_ON_PORT_UP ||
13560                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13561 #if USE_PLAYER_GRAVITY
13562         player->gravity = TRUE;
13563 #else
13564         game.gravity = TRUE;
13565 #endif
13566       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13567                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13568                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13569                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13570 #if USE_PLAYER_GRAVITY
13571         player->gravity = FALSE;
13572 #else
13573         game.gravity = FALSE;
13574 #endif
13575     }
13576
13577     /* automatically move to the next field with double speed */
13578     player->programmed_action = move_direction;
13579
13580     if (player->move_delay_reset_counter == 0)
13581     {
13582       player->move_delay_reset_counter = 2;     /* two double speed steps */
13583
13584       DOUBLE_PLAYER_SPEED(player);
13585     }
13586
13587     PlayLevelSoundAction(x, y, ACTION_PASSING);
13588   }
13589   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13590   {
13591     RemoveField(x, y);
13592
13593     if (mode != DF_SNAP)
13594     {
13595       GfxElement[x][y] = GFX_ELEMENT(element);
13596       player->is_digging = TRUE;
13597     }
13598
13599     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13600
13601     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13602                                         player->index_bit, dig_side);
13603
13604     if (mode == DF_SNAP)
13605     {
13606 #if USE_NEW_SNAP_DELAY
13607       if (level.block_snap_field)
13608         setFieldForSnapping(x, y, element, move_direction);
13609       else
13610         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13611 #else
13612       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13613 #endif
13614
13615       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13616                                           player->index_bit, dig_side);
13617     }
13618   }
13619   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13620   {
13621     RemoveField(x, y);
13622
13623     if (is_player && mode != DF_SNAP)
13624     {
13625       GfxElement[x][y] = element;
13626       player->is_collecting = TRUE;
13627     }
13628
13629     if (element == EL_SPEED_PILL)
13630     {
13631       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13632     }
13633     else if (element == EL_EXTRA_TIME && level.time > 0)
13634     {
13635       TimeLeft += level.extra_time;
13636
13637 #if 1
13638       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13639
13640       DisplayGameControlValues();
13641 #else
13642       DrawGameValue_Time(TimeLeft);
13643 #endif
13644     }
13645     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13646     {
13647       player->shield_normal_time_left += level.shield_normal_time;
13648       if (element == EL_SHIELD_DEADLY)
13649         player->shield_deadly_time_left += level.shield_deadly_time;
13650     }
13651     else if (element == EL_DYNAMITE ||
13652              element == EL_EM_DYNAMITE ||
13653              element == EL_SP_DISK_RED)
13654     {
13655       if (player->inventory_size < MAX_INVENTORY_SIZE)
13656         player->inventory_element[player->inventory_size++] = element;
13657
13658       DrawGameDoorValues();
13659     }
13660     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13661     {
13662       player->dynabomb_count++;
13663       player->dynabombs_left++;
13664     }
13665     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13666     {
13667       player->dynabomb_size++;
13668     }
13669     else if (element == EL_DYNABOMB_INCREASE_POWER)
13670     {
13671       player->dynabomb_xl = TRUE;
13672     }
13673     else if (IS_KEY(element))
13674     {
13675       player->key[KEY_NR(element)] = TRUE;
13676
13677       DrawGameDoorValues();
13678     }
13679     else if (element == EL_DC_KEY_WHITE)
13680     {
13681       player->num_white_keys++;
13682
13683       /* display white keys? */
13684       /* DrawGameDoorValues(); */
13685     }
13686     else if (IS_ENVELOPE(element))
13687     {
13688       player->show_envelope = element;
13689     }
13690     else if (element == EL_EMC_LENSES)
13691     {
13692       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13693
13694       RedrawAllInvisibleElementsForLenses();
13695     }
13696     else if (element == EL_EMC_MAGNIFIER)
13697     {
13698       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13699
13700       RedrawAllInvisibleElementsForMagnifier();
13701     }
13702     else if (IS_DROPPABLE(element) ||
13703              IS_THROWABLE(element))     /* can be collected and dropped */
13704     {
13705       int i;
13706
13707       if (collect_count == 0)
13708         player->inventory_infinite_element = element;
13709       else
13710         for (i = 0; i < collect_count; i++)
13711           if (player->inventory_size < MAX_INVENTORY_SIZE)
13712             player->inventory_element[player->inventory_size++] = element;
13713
13714       DrawGameDoorValues();
13715     }
13716     else if (collect_count > 0)
13717     {
13718       local_player->gems_still_needed -= collect_count;
13719       if (local_player->gems_still_needed < 0)
13720         local_player->gems_still_needed = 0;
13721
13722 #if 1
13723       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13724
13725       DisplayGameControlValues();
13726 #else
13727       DrawGameValue_Emeralds(local_player->gems_still_needed);
13728 #endif
13729     }
13730
13731     RaiseScoreElement(element);
13732     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13733
13734     if (is_player)
13735       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13736                                           player->index_bit, dig_side);
13737
13738     if (mode == DF_SNAP)
13739     {
13740 #if USE_NEW_SNAP_DELAY
13741       if (level.block_snap_field)
13742         setFieldForSnapping(x, y, element, move_direction);
13743       else
13744         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13745 #else
13746       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13747 #endif
13748
13749       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13750                                           player->index_bit, dig_side);
13751     }
13752   }
13753   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13754   {
13755     if (mode == DF_SNAP && element != EL_BD_ROCK)
13756       return MP_NO_ACTION;
13757
13758     if (CAN_FALL(element) && dy)
13759       return MP_NO_ACTION;
13760
13761     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13762         !(element == EL_SPRING && level.use_spring_bug))
13763       return MP_NO_ACTION;
13764
13765     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13766         ((move_direction & MV_VERTICAL &&
13767           ((element_info[element].move_pattern & MV_LEFT &&
13768             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13769            (element_info[element].move_pattern & MV_RIGHT &&
13770             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13771          (move_direction & MV_HORIZONTAL &&
13772           ((element_info[element].move_pattern & MV_UP &&
13773             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13774            (element_info[element].move_pattern & MV_DOWN &&
13775             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13776       return MP_NO_ACTION;
13777
13778     /* do not push elements already moving away faster than player */
13779     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13780         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13781       return MP_NO_ACTION;
13782
13783     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13784     {
13785       if (player->push_delay_value == -1 || !player_was_pushing)
13786         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13787     }
13788     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13789     {
13790       if (player->push_delay_value == -1)
13791         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13792     }
13793     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13794     {
13795       if (!player->is_pushing)
13796         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13797     }
13798
13799     player->is_pushing = TRUE;
13800     player->is_active = TRUE;
13801
13802     if (!(IN_LEV_FIELD(nextx, nexty) &&
13803           (IS_FREE(nextx, nexty) ||
13804            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13805             IS_SB_ELEMENT(element)))))
13806       return MP_NO_ACTION;
13807
13808     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13809       return MP_NO_ACTION;
13810
13811     if (player->push_delay == -1)       /* new pushing; restart delay */
13812       player->push_delay = 0;
13813
13814     if (player->push_delay < player->push_delay_value &&
13815         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13816         element != EL_SPRING && element != EL_BALLOON)
13817     {
13818       /* make sure that there is no move delay before next try to push */
13819       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13820         player->move_delay = 0;
13821
13822       return MP_NO_ACTION;
13823     }
13824
13825     if (IS_SB_ELEMENT(element))
13826     {
13827       if (element == EL_SOKOBAN_FIELD_FULL)
13828       {
13829         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13830         local_player->sokobanfields_still_needed++;
13831       }
13832
13833       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13834       {
13835         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13836         local_player->sokobanfields_still_needed--;
13837       }
13838
13839       Feld[x][y] = EL_SOKOBAN_OBJECT;
13840
13841       if (Back[x][y] == Back[nextx][nexty])
13842         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13843       else if (Back[x][y] != 0)
13844         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13845                                     ACTION_EMPTYING);
13846       else
13847         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13848                                     ACTION_FILLING);
13849
13850       if (local_player->sokobanfields_still_needed == 0 &&
13851           game.emulation == EMU_SOKOBAN)
13852       {
13853         PlayerWins(player);
13854
13855         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13856       }
13857     }
13858     else
13859       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13860
13861     InitMovingField(x, y, move_direction);
13862     GfxAction[x][y] = ACTION_PUSHING;
13863
13864     if (mode == DF_SNAP)
13865       ContinueMoving(x, y);
13866     else
13867       MovPos[x][y] = (dx != 0 ? dx : dy);
13868
13869     Pushed[x][y] = TRUE;
13870     Pushed[nextx][nexty] = TRUE;
13871
13872     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13873       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13874     else
13875       player->push_delay_value = -1;    /* get new value later */
13876
13877     /* check for element change _after_ element has been pushed */
13878     if (game.use_change_when_pushing_bug)
13879     {
13880       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13881                                  player->index_bit, dig_side);
13882       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13883                                           player->index_bit, dig_side);
13884     }
13885   }
13886   else if (IS_SWITCHABLE(element))
13887   {
13888     if (PLAYER_SWITCHING(player, x, y))
13889     {
13890       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13891                                           player->index_bit, dig_side);
13892
13893       return MP_ACTION;
13894     }
13895
13896     player->is_switching = TRUE;
13897     player->switch_x = x;
13898     player->switch_y = y;
13899
13900     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13901
13902     if (element == EL_ROBOT_WHEEL)
13903     {
13904       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13905       ZX = x;
13906       ZY = y;
13907
13908       DrawLevelField(x, y);
13909     }
13910     else if (element == EL_SP_TERMINAL)
13911     {
13912       int xx, yy;
13913
13914       SCAN_PLAYFIELD(xx, yy)
13915       {
13916         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13917           Bang(xx, yy);
13918         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13919           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13920       }
13921     }
13922     else if (IS_BELT_SWITCH(element))
13923     {
13924       ToggleBeltSwitch(x, y);
13925     }
13926     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13927              element == EL_SWITCHGATE_SWITCH_DOWN ||
13928              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13929              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13930     {
13931       ToggleSwitchgateSwitch(x, y);
13932     }
13933     else if (element == EL_LIGHT_SWITCH ||
13934              element == EL_LIGHT_SWITCH_ACTIVE)
13935     {
13936       ToggleLightSwitch(x, y);
13937     }
13938     else if (element == EL_TIMEGATE_SWITCH ||
13939              element == EL_DC_TIMEGATE_SWITCH)
13940     {
13941       ActivateTimegateSwitch(x, y);
13942     }
13943     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13944              element == EL_BALLOON_SWITCH_RIGHT ||
13945              element == EL_BALLOON_SWITCH_UP    ||
13946              element == EL_BALLOON_SWITCH_DOWN  ||
13947              element == EL_BALLOON_SWITCH_NONE  ||
13948              element == EL_BALLOON_SWITCH_ANY)
13949     {
13950       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13951                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13952                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13953                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13954                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13955                              move_direction);
13956     }
13957     else if (element == EL_LAMP)
13958     {
13959       Feld[x][y] = EL_LAMP_ACTIVE;
13960       local_player->lights_still_needed--;
13961
13962       ResetGfxAnimation(x, y);
13963       DrawLevelField(x, y);
13964     }
13965     else if (element == EL_TIME_ORB_FULL)
13966     {
13967       Feld[x][y] = EL_TIME_ORB_EMPTY;
13968
13969       if (level.time > 0 || level.use_time_orb_bug)
13970       {
13971         TimeLeft += level.time_orb_time;
13972
13973 #if 1
13974         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13975
13976         DisplayGameControlValues();
13977 #else
13978         DrawGameValue_Time(TimeLeft);
13979 #endif
13980       }
13981
13982       ResetGfxAnimation(x, y);
13983       DrawLevelField(x, y);
13984     }
13985     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13986              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13987     {
13988       int xx, yy;
13989
13990       game.ball_state = !game.ball_state;
13991
13992       SCAN_PLAYFIELD(xx, yy)
13993       {
13994         int e = Feld[xx][yy];
13995
13996         if (game.ball_state)
13997         {
13998           if (e == EL_EMC_MAGIC_BALL)
13999             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14000           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14001             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14002         }
14003         else
14004         {
14005           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14006             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14007           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14008             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14009         }
14010       }
14011     }
14012
14013     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14014                                         player->index_bit, dig_side);
14015
14016     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14017                                         player->index_bit, dig_side);
14018
14019     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14020                                         player->index_bit, dig_side);
14021
14022     return MP_ACTION;
14023   }
14024   else
14025   {
14026     if (!PLAYER_SWITCHING(player, x, y))
14027     {
14028       player->is_switching = TRUE;
14029       player->switch_x = x;
14030       player->switch_y = y;
14031
14032       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14033                                  player->index_bit, dig_side);
14034       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14035                                           player->index_bit, dig_side);
14036
14037       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14038                                  player->index_bit, dig_side);
14039       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14040                                           player->index_bit, dig_side);
14041     }
14042
14043     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14044                                player->index_bit, dig_side);
14045     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14046                                         player->index_bit, dig_side);
14047
14048     return MP_NO_ACTION;
14049   }
14050
14051   player->push_delay = -1;
14052
14053   if (is_player)                /* function can also be called by EL_PENGUIN */
14054   {
14055     if (Feld[x][y] != element)          /* really digged/collected something */
14056     {
14057       player->is_collecting = !player->is_digging;
14058       player->is_active = TRUE;
14059     }
14060   }
14061
14062   return MP_MOVING;
14063 }
14064
14065 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14066 {
14067   int jx = player->jx, jy = player->jy;
14068   int x = jx + dx, y = jy + dy;
14069   int snap_direction = (dx == -1 ? MV_LEFT  :
14070                         dx == +1 ? MV_RIGHT :
14071                         dy == -1 ? MV_UP    :
14072                         dy == +1 ? MV_DOWN  : MV_NONE);
14073   boolean can_continue_snapping = (level.continuous_snapping &&
14074                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14075
14076   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14077     return FALSE;
14078
14079   if (!player->active || !IN_LEV_FIELD(x, y))
14080     return FALSE;
14081
14082   if (dx && dy)
14083     return FALSE;
14084
14085   if (!dx && !dy)
14086   {
14087     if (player->MovPos == 0)
14088       player->is_pushing = FALSE;
14089
14090     player->is_snapping = FALSE;
14091
14092     if (player->MovPos == 0)
14093     {
14094       player->is_moving = FALSE;
14095       player->is_digging = FALSE;
14096       player->is_collecting = FALSE;
14097     }
14098
14099     return FALSE;
14100   }
14101
14102 #if USE_NEW_CONTINUOUS_SNAPPING
14103   /* prevent snapping with already pressed snap key when not allowed */
14104   if (player->is_snapping && !can_continue_snapping)
14105     return FALSE;
14106 #else
14107   if (player->is_snapping)
14108     return FALSE;
14109 #endif
14110
14111   player->MovDir = snap_direction;
14112
14113   if (player->MovPos == 0)
14114   {
14115     player->is_moving = FALSE;
14116     player->is_digging = FALSE;
14117     player->is_collecting = FALSE;
14118   }
14119
14120   player->is_dropping = FALSE;
14121   player->is_dropping_pressed = FALSE;
14122   player->drop_pressed_delay = 0;
14123
14124   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14125     return FALSE;
14126
14127   player->is_snapping = TRUE;
14128   player->is_active = TRUE;
14129
14130   if (player->MovPos == 0)
14131   {
14132     player->is_moving = FALSE;
14133     player->is_digging = FALSE;
14134     player->is_collecting = FALSE;
14135   }
14136
14137   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14138     DrawLevelField(player->last_jx, player->last_jy);
14139
14140   DrawLevelField(x, y);
14141
14142   return TRUE;
14143 }
14144
14145 boolean DropElement(struct PlayerInfo *player)
14146 {
14147   int old_element, new_element;
14148   int dropx = player->jx, dropy = player->jy;
14149   int drop_direction = player->MovDir;
14150   int drop_side = drop_direction;
14151 #if 1
14152   int drop_element = get_next_drop_element(player);
14153 #else
14154   int drop_element = (player->inventory_size > 0 ?
14155                       player->inventory_element[player->inventory_size - 1] :
14156                       player->inventory_infinite_element != EL_UNDEFINED ?
14157                       player->inventory_infinite_element :
14158                       player->dynabombs_left > 0 ?
14159                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14160                       EL_UNDEFINED);
14161 #endif
14162
14163   player->is_dropping_pressed = TRUE;
14164
14165   /* do not drop an element on top of another element; when holding drop key
14166      pressed without moving, dropped element must move away before the next
14167      element can be dropped (this is especially important if the next element
14168      is dynamite, which can be placed on background for historical reasons) */
14169   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14170     return MP_ACTION;
14171
14172   if (IS_THROWABLE(drop_element))
14173   {
14174     dropx += GET_DX_FROM_DIR(drop_direction);
14175     dropy += GET_DY_FROM_DIR(drop_direction);
14176
14177     if (!IN_LEV_FIELD(dropx, dropy))
14178       return FALSE;
14179   }
14180
14181   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14182   new_element = drop_element;           /* default: no change when dropping */
14183
14184   /* check if player is active, not moving and ready to drop */
14185   if (!player->active || player->MovPos || player->drop_delay > 0)
14186     return FALSE;
14187
14188   /* check if player has anything that can be dropped */
14189   if (new_element == EL_UNDEFINED)
14190     return FALSE;
14191
14192   /* check if drop key was pressed long enough for EM style dynamite */
14193   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14194     return FALSE;
14195
14196   /* check if anything can be dropped at the current position */
14197   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14198     return FALSE;
14199
14200   /* collected custom elements can only be dropped on empty fields */
14201   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14202     return FALSE;
14203
14204   if (old_element != EL_EMPTY)
14205     Back[dropx][dropy] = old_element;   /* store old element on this field */
14206
14207   ResetGfxAnimation(dropx, dropy);
14208   ResetRandomAnimationValue(dropx, dropy);
14209
14210   if (player->inventory_size > 0 ||
14211       player->inventory_infinite_element != EL_UNDEFINED)
14212   {
14213     if (player->inventory_size > 0)
14214     {
14215       player->inventory_size--;
14216
14217       DrawGameDoorValues();
14218
14219       if (new_element == EL_DYNAMITE)
14220         new_element = EL_DYNAMITE_ACTIVE;
14221       else if (new_element == EL_EM_DYNAMITE)
14222         new_element = EL_EM_DYNAMITE_ACTIVE;
14223       else if (new_element == EL_SP_DISK_RED)
14224         new_element = EL_SP_DISK_RED_ACTIVE;
14225     }
14226
14227     Feld[dropx][dropy] = new_element;
14228
14229     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14230       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14231                           el2img(Feld[dropx][dropy]), 0);
14232
14233     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14234
14235     /* needed if previous element just changed to "empty" in the last frame */
14236     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14237
14238     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14239                                player->index_bit, drop_side);
14240     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14241                                         CE_PLAYER_DROPS_X,
14242                                         player->index_bit, drop_side);
14243
14244     TestIfElementTouchesCustomElement(dropx, dropy);
14245   }
14246   else          /* player is dropping a dyna bomb */
14247   {
14248     player->dynabombs_left--;
14249
14250     Feld[dropx][dropy] = new_element;
14251
14252     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14253       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14254                           el2img(Feld[dropx][dropy]), 0);
14255
14256     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14257   }
14258
14259   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14260     InitField_WithBug1(dropx, dropy, FALSE);
14261
14262   new_element = Feld[dropx][dropy];     /* element might have changed */
14263
14264   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14265       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14266   {
14267     int move_direction, nextx, nexty;
14268
14269     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14270       MovDir[dropx][dropy] = drop_direction;
14271
14272     move_direction = MovDir[dropx][dropy];
14273     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14274     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14275
14276     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14277
14278 #if USE_FIX_IMPACT_COLLISION
14279     /* do not cause impact style collision by dropping elements that can fall */
14280     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14281 #else
14282     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14283 #endif
14284   }
14285
14286   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14287   player->is_dropping = TRUE;
14288
14289   player->drop_pressed_delay = 0;
14290   player->is_dropping_pressed = FALSE;
14291
14292   player->drop_x = dropx;
14293   player->drop_y = dropy;
14294
14295   return TRUE;
14296 }
14297
14298 /* ------------------------------------------------------------------------- */
14299 /* game sound playing functions                                              */
14300 /* ------------------------------------------------------------------------- */
14301
14302 static int *loop_sound_frame = NULL;
14303 static int *loop_sound_volume = NULL;
14304
14305 void InitPlayLevelSound()
14306 {
14307   int num_sounds = getSoundListSize();
14308
14309   checked_free(loop_sound_frame);
14310   checked_free(loop_sound_volume);
14311
14312   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14313   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14314 }
14315
14316 static void PlayLevelSound(int x, int y, int nr)
14317 {
14318   int sx = SCREENX(x), sy = SCREENY(y);
14319   int volume, stereo_position;
14320   int max_distance = 8;
14321   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14322
14323   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14324       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14325     return;
14326
14327   if (!IN_LEV_FIELD(x, y) ||
14328       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14329       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14330     return;
14331
14332   volume = SOUND_MAX_VOLUME;
14333
14334   if (!IN_SCR_FIELD(sx, sy))
14335   {
14336     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14337     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14338
14339     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14340   }
14341
14342   stereo_position = (SOUND_MAX_LEFT +
14343                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14344                      (SCR_FIELDX + 2 * max_distance));
14345
14346   if (IS_LOOP_SOUND(nr))
14347   {
14348     /* This assures that quieter loop sounds do not overwrite louder ones,
14349        while restarting sound volume comparison with each new game frame. */
14350
14351     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14352       return;
14353
14354     loop_sound_volume[nr] = volume;
14355     loop_sound_frame[nr] = FrameCounter;
14356   }
14357
14358   PlaySoundExt(nr, volume, stereo_position, type);
14359 }
14360
14361 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14362 {
14363   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14364                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14365                  y < LEVELY(BY1) ? LEVELY(BY1) :
14366                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14367                  sound_action);
14368 }
14369
14370 static void PlayLevelSoundAction(int x, int y, int action)
14371 {
14372   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14373 }
14374
14375 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14376 {
14377   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14378
14379   if (sound_effect != SND_UNDEFINED)
14380     PlayLevelSound(x, y, sound_effect);
14381 }
14382
14383 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14384                                               int action)
14385 {
14386   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14387
14388   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14389     PlayLevelSound(x, y, sound_effect);
14390 }
14391
14392 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14393 {
14394   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14395
14396   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14397     PlayLevelSound(x, y, sound_effect);
14398 }
14399
14400 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14401 {
14402   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14403
14404   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14405     StopSound(sound_effect);
14406 }
14407
14408 static void PlayLevelMusic()
14409 {
14410   if (levelset.music[level_nr] != MUS_UNDEFINED)
14411     PlayMusic(levelset.music[level_nr]);        /* from config file */
14412   else
14413     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14414 }
14415
14416 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14417 {
14418   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14419   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14420   int x = xx - 1 - offset;
14421   int y = yy - 1 - offset;
14422
14423   switch (sample)
14424   {
14425     case SAMPLE_blank:
14426       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14427       break;
14428
14429     case SAMPLE_roll:
14430       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14431       break;
14432
14433     case SAMPLE_stone:
14434       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14435       break;
14436
14437     case SAMPLE_nut:
14438       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14439       break;
14440
14441     case SAMPLE_crack:
14442       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14443       break;
14444
14445     case SAMPLE_bug:
14446       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14447       break;
14448
14449     case SAMPLE_tank:
14450       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14451       break;
14452
14453     case SAMPLE_android_clone:
14454       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14455       break;
14456
14457     case SAMPLE_android_move:
14458       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14459       break;
14460
14461     case SAMPLE_spring:
14462       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14463       break;
14464
14465     case SAMPLE_slurp:
14466       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14467       break;
14468
14469     case SAMPLE_eater:
14470       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14471       break;
14472
14473     case SAMPLE_eater_eat:
14474       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14475       break;
14476
14477     case SAMPLE_alien:
14478       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14479       break;
14480
14481     case SAMPLE_collect:
14482       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14483       break;
14484
14485     case SAMPLE_diamond:
14486       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14487       break;
14488
14489     case SAMPLE_squash:
14490       /* !!! CHECK THIS !!! */
14491 #if 1
14492       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14493 #else
14494       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14495 #endif
14496       break;
14497
14498     case SAMPLE_wonderfall:
14499       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14500       break;
14501
14502     case SAMPLE_drip:
14503       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14504       break;
14505
14506     case SAMPLE_push:
14507       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14508       break;
14509
14510     case SAMPLE_dirt:
14511       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14512       break;
14513
14514     case SAMPLE_acid:
14515       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14516       break;
14517
14518     case SAMPLE_ball:
14519       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14520       break;
14521
14522     case SAMPLE_grow:
14523       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14524       break;
14525
14526     case SAMPLE_wonder:
14527       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14528       break;
14529
14530     case SAMPLE_door:
14531       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14532       break;
14533
14534     case SAMPLE_exit_open:
14535       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14536       break;
14537
14538     case SAMPLE_exit_leave:
14539       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14540       break;
14541
14542     case SAMPLE_dynamite:
14543       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14544       break;
14545
14546     case SAMPLE_tick:
14547       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14548       break;
14549
14550     case SAMPLE_press:
14551       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14552       break;
14553
14554     case SAMPLE_wheel:
14555       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14556       break;
14557
14558     case SAMPLE_boom:
14559       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14560       break;
14561
14562     case SAMPLE_die:
14563       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14564       break;
14565
14566     case SAMPLE_time:
14567       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14568       break;
14569
14570     default:
14571       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14572       break;
14573   }
14574 }
14575
14576 #if 0
14577 void ChangeTime(int value)
14578 {
14579   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14580
14581   *time += value;
14582
14583   /* EMC game engine uses value from time counter of RND game engine */
14584   level.native_em_level->lev->time = *time;
14585
14586   DrawGameValue_Time(*time);
14587 }
14588
14589 void RaiseScore(int value)
14590 {
14591   /* EMC game engine and RND game engine have separate score counters */
14592   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14593                 &level.native_em_level->lev->score : &local_player->score);
14594
14595   *score += value;
14596
14597   DrawGameValue_Score(*score);
14598 }
14599 #endif
14600
14601 void RaiseScore(int value)
14602 {
14603   local_player->score += value;
14604
14605 #if 1
14606   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14607
14608   DisplayGameControlValues();
14609 #else
14610   DrawGameValue_Score(local_player->score);
14611 #endif
14612 }
14613
14614 void RaiseScoreElement(int element)
14615 {
14616   switch (element)
14617   {
14618     case EL_EMERALD:
14619     case EL_BD_DIAMOND:
14620     case EL_EMERALD_YELLOW:
14621     case EL_EMERALD_RED:
14622     case EL_EMERALD_PURPLE:
14623     case EL_SP_INFOTRON:
14624       RaiseScore(level.score[SC_EMERALD]);
14625       break;
14626     case EL_DIAMOND:
14627       RaiseScore(level.score[SC_DIAMOND]);
14628       break;
14629     case EL_CRYSTAL:
14630       RaiseScore(level.score[SC_CRYSTAL]);
14631       break;
14632     case EL_PEARL:
14633       RaiseScore(level.score[SC_PEARL]);
14634       break;
14635     case EL_BUG:
14636     case EL_BD_BUTTERFLY:
14637     case EL_SP_ELECTRON:
14638       RaiseScore(level.score[SC_BUG]);
14639       break;
14640     case EL_SPACESHIP:
14641     case EL_BD_FIREFLY:
14642     case EL_SP_SNIKSNAK:
14643       RaiseScore(level.score[SC_SPACESHIP]);
14644       break;
14645     case EL_YAMYAM:
14646     case EL_DARK_YAMYAM:
14647       RaiseScore(level.score[SC_YAMYAM]);
14648       break;
14649     case EL_ROBOT:
14650       RaiseScore(level.score[SC_ROBOT]);
14651       break;
14652     case EL_PACMAN:
14653       RaiseScore(level.score[SC_PACMAN]);
14654       break;
14655     case EL_NUT:
14656       RaiseScore(level.score[SC_NUT]);
14657       break;
14658     case EL_DYNAMITE:
14659     case EL_EM_DYNAMITE:
14660     case EL_SP_DISK_RED:
14661     case EL_DYNABOMB_INCREASE_NUMBER:
14662     case EL_DYNABOMB_INCREASE_SIZE:
14663     case EL_DYNABOMB_INCREASE_POWER:
14664       RaiseScore(level.score[SC_DYNAMITE]);
14665       break;
14666     case EL_SHIELD_NORMAL:
14667     case EL_SHIELD_DEADLY:
14668       RaiseScore(level.score[SC_SHIELD]);
14669       break;
14670     case EL_EXTRA_TIME:
14671       RaiseScore(level.extra_time_score);
14672       break;
14673     case EL_KEY_1:
14674     case EL_KEY_2:
14675     case EL_KEY_3:
14676     case EL_KEY_4:
14677     case EL_EM_KEY_1:
14678     case EL_EM_KEY_2:
14679     case EL_EM_KEY_3:
14680     case EL_EM_KEY_4:
14681     case EL_EMC_KEY_5:
14682     case EL_EMC_KEY_6:
14683     case EL_EMC_KEY_7:
14684     case EL_EMC_KEY_8:
14685     case EL_DC_KEY_WHITE:
14686       RaiseScore(level.score[SC_KEY]);
14687       break;
14688     default:
14689       RaiseScore(element_info[element].collect_score);
14690       break;
14691   }
14692 }
14693
14694 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14695 {
14696   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14697   {
14698 #if defined(NETWORK_AVALIABLE)
14699     if (options.network)
14700       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14701     else
14702 #endif
14703     {
14704       if (quick_quit)
14705       {
14706 #if 1
14707
14708 #if 1
14709         FadeSkipNextFadeIn();
14710 #else
14711         fading = fading_none;
14712 #endif
14713
14714 #else
14715         OpenDoor(DOOR_CLOSE_1);
14716 #endif
14717
14718         game_status = GAME_MODE_MAIN;
14719
14720 #if 1
14721         DrawAndFadeInMainMenu(REDRAW_FIELD);
14722 #else
14723         DrawMainMenu();
14724 #endif
14725       }
14726       else
14727       {
14728 #if 0
14729         FadeOut(REDRAW_FIELD);
14730 #endif
14731
14732         game_status = GAME_MODE_MAIN;
14733
14734         DrawAndFadeInMainMenu(REDRAW_FIELD);
14735       }
14736     }
14737   }
14738   else          /* continue playing the game */
14739   {
14740     if (tape.playing && tape.deactivate_display)
14741       TapeDeactivateDisplayOff(TRUE);
14742
14743     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14744
14745     if (tape.playing && tape.deactivate_display)
14746       TapeDeactivateDisplayOn();
14747   }
14748 }
14749
14750 void RequestQuitGame(boolean ask_if_really_quit)
14751 {
14752   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14753   boolean skip_request = AllPlayersGone || quick_quit;
14754
14755   RequestQuitGameExt(skip_request, quick_quit,
14756                      "Do you really want to quit the game ?");
14757 }
14758
14759
14760 /* ------------------------------------------------------------------------- */
14761 /* random generator functions                                                */
14762 /* ------------------------------------------------------------------------- */
14763
14764 unsigned int InitEngineRandom_RND(long seed)
14765 {
14766   game.num_random_calls = 0;
14767
14768 #if 0
14769   unsigned int rnd_seed = InitEngineRandom(seed);
14770
14771   printf("::: START RND: %d\n", rnd_seed);
14772
14773   return rnd_seed;
14774 #else
14775
14776   return InitEngineRandom(seed);
14777
14778 #endif
14779
14780 }
14781
14782 unsigned int RND(int max)
14783 {
14784   if (max > 0)
14785   {
14786     game.num_random_calls++;
14787
14788     return GetEngineRandom(max);
14789   }
14790
14791   return 0;
14792 }
14793
14794
14795 /* ------------------------------------------------------------------------- */
14796 /* game engine snapshot handling functions                                   */
14797 /* ------------------------------------------------------------------------- */
14798
14799 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14800
14801 struct EngineSnapshotInfo
14802 {
14803   /* runtime values for custom element collect score */
14804   int collect_score[NUM_CUSTOM_ELEMENTS];
14805
14806   /* runtime values for group element choice position */
14807   int choice_pos[NUM_GROUP_ELEMENTS];
14808
14809   /* runtime values for belt position animations */
14810   int belt_graphic[4 * NUM_BELT_PARTS];
14811   int belt_anim_mode[4 * NUM_BELT_PARTS];
14812 };
14813
14814 struct EngineSnapshotNodeInfo
14815 {
14816   void *buffer_orig;
14817   void *buffer_copy;
14818   int size;
14819 };
14820
14821 static struct EngineSnapshotInfo engine_snapshot_rnd;
14822 static ListNode *engine_snapshot_list = NULL;
14823 static char *snapshot_level_identifier = NULL;
14824 static int snapshot_level_nr = -1;
14825
14826 void FreeEngineSnapshot()
14827 {
14828   while (engine_snapshot_list != NULL)
14829     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14830                        checked_free);
14831
14832   setString(&snapshot_level_identifier, NULL);
14833   snapshot_level_nr = -1;
14834 }
14835
14836 static void SaveEngineSnapshotValues_RND()
14837 {
14838   static int belt_base_active_element[4] =
14839   {
14840     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14841     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14842     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14843     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14844   };
14845   int i, j;
14846
14847   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14848   {
14849     int element = EL_CUSTOM_START + i;
14850
14851     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14852   }
14853
14854   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14855   {
14856     int element = EL_GROUP_START + i;
14857
14858     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14859   }
14860
14861   for (i = 0; i < 4; i++)
14862   {
14863     for (j = 0; j < NUM_BELT_PARTS; j++)
14864     {
14865       int element = belt_base_active_element[i] + j;
14866       int graphic = el2img(element);
14867       int anim_mode = graphic_info[graphic].anim_mode;
14868
14869       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14870       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14871     }
14872   }
14873 }
14874
14875 static void LoadEngineSnapshotValues_RND()
14876 {
14877   unsigned long num_random_calls = game.num_random_calls;
14878   int i, j;
14879
14880   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14881   {
14882     int element = EL_CUSTOM_START + i;
14883
14884     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14885   }
14886
14887   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14888   {
14889     int element = EL_GROUP_START + i;
14890
14891     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14892   }
14893
14894   for (i = 0; i < 4; i++)
14895   {
14896     for (j = 0; j < NUM_BELT_PARTS; j++)
14897     {
14898       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14899       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14900
14901       graphic_info[graphic].anim_mode = anim_mode;
14902     }
14903   }
14904
14905   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14906   {
14907     InitRND(tape.random_seed);
14908     for (i = 0; i < num_random_calls; i++)
14909       RND(1);
14910   }
14911
14912   if (game.num_random_calls != num_random_calls)
14913   {
14914     Error(ERR_INFO, "number of random calls out of sync");
14915     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14916     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14917     Error(ERR_EXIT, "this should not happen -- please debug");
14918   }
14919 }
14920
14921 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14922 {
14923   struct EngineSnapshotNodeInfo *bi =
14924     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14925
14926   bi->buffer_orig = buffer;
14927   bi->buffer_copy = checked_malloc(size);
14928   bi->size = size;
14929
14930   memcpy(bi->buffer_copy, buffer, size);
14931
14932   addNodeToList(&engine_snapshot_list, NULL, bi);
14933 }
14934
14935 void SaveEngineSnapshot()
14936 {
14937   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14938
14939   if (level_editor_test_game)   /* do not save snapshots from editor */
14940     return;
14941
14942   /* copy some special values to a structure better suited for the snapshot */
14943
14944   SaveEngineSnapshotValues_RND();
14945   SaveEngineSnapshotValues_EM();
14946
14947   /* save values stored in special snapshot structure */
14948
14949   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14950   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14951
14952   /* save further RND engine values */
14953
14954   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14955   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14956   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14957
14958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14959   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14960   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14961   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14962
14963   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14964   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14966   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14967   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14968
14969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14971   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14972
14973   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14974
14975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14976
14977   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14978   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14979
14980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14981   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14983   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14988   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14996   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14998
14999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15000   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15001
15002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15003   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15005
15006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15007   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15008
15009   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15010   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15012   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15013   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15014
15015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15016   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15017
15018   /* save level identification information */
15019
15020   setString(&snapshot_level_identifier, leveldir_current->identifier);
15021   snapshot_level_nr = level_nr;
15022
15023 #if 0
15024   ListNode *node = engine_snapshot_list;
15025   int num_bytes = 0;
15026
15027   while (node != NULL)
15028   {
15029     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15030
15031     node = node->next;
15032   }
15033
15034   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15035 #endif
15036 }
15037
15038 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15039 {
15040   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15041 }
15042
15043 void LoadEngineSnapshot()
15044 {
15045   ListNode *node = engine_snapshot_list;
15046
15047   if (engine_snapshot_list == NULL)
15048     return;
15049
15050   while (node != NULL)
15051   {
15052     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15053
15054     node = node->next;
15055   }
15056
15057   /* restore special values from snapshot structure */
15058
15059   LoadEngineSnapshotValues_RND();
15060   LoadEngineSnapshotValues_EM();
15061 }
15062
15063 boolean CheckEngineSnapshot()
15064 {
15065   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15066           snapshot_level_nr == level_nr);
15067 }
15068
15069
15070 /* ---------- new game button stuff ---------------------------------------- */
15071
15072 /* graphic position values for game buttons */
15073 #define GAME_BUTTON_XSIZE       30
15074 #define GAME_BUTTON_YSIZE       30
15075 #define GAME_BUTTON_XPOS        5
15076 #define GAME_BUTTON_YPOS        215
15077 #define SOUND_BUTTON_XPOS       5
15078 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15079
15080 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15081 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15082 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15083 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15084 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15085 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15086
15087 static struct
15088 {
15089   int *x, *y;
15090   int gd_x, gd_y;
15091   int gadget_id;
15092   char *infotext;
15093 } gamebutton_info[NUM_GAME_BUTTONS] =
15094 {
15095 #if 1
15096   {
15097     &game.button.stop.x,        &game.button.stop.y,
15098     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15099     GAME_CTRL_ID_STOP,
15100     "stop game"
15101   },
15102   {
15103     &game.button.pause.x,       &game.button.pause.y,
15104     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15105     GAME_CTRL_ID_PAUSE,
15106     "pause game"
15107   },
15108   {
15109     &game.button.play.x,        &game.button.play.y,
15110     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15111     GAME_CTRL_ID_PLAY,
15112     "play game"
15113   },
15114   {
15115     &game.button.sound_music.x, &game.button.sound_music.y,
15116     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15117     SOUND_CTRL_ID_MUSIC,
15118     "background music on/off"
15119   },
15120   {
15121     &game.button.sound_loops.x, &game.button.sound_loops.y,
15122     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15123     SOUND_CTRL_ID_LOOPS,
15124     "sound loops on/off"
15125   },
15126   {
15127     &game.button.sound_simple.x,&game.button.sound_simple.y,
15128     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15129     SOUND_CTRL_ID_SIMPLE,
15130     "normal sounds on/off"
15131   }
15132 #else
15133   {
15134     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15135     GAME_CTRL_ID_STOP,
15136     "stop game"
15137   },
15138   {
15139     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15140     GAME_CTRL_ID_PAUSE,
15141     "pause game"
15142   },
15143   {
15144     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15145     GAME_CTRL_ID_PLAY,
15146     "play game"
15147   },
15148   {
15149     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15150     SOUND_CTRL_ID_MUSIC,
15151     "background music on/off"
15152   },
15153   {
15154     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15155     SOUND_CTRL_ID_LOOPS,
15156     "sound loops on/off"
15157   },
15158   {
15159     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15160     SOUND_CTRL_ID_SIMPLE,
15161     "normal sounds on/off"
15162   }
15163 #endif
15164 };
15165
15166 void CreateGameButtons()
15167 {
15168   int i;
15169
15170   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15171   {
15172     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15173     struct GadgetInfo *gi;
15174     int button_type;
15175     boolean checked;
15176     unsigned long event_mask;
15177     int x, y;
15178     int gd_xoffset, gd_yoffset;
15179     int gd_x1, gd_x2, gd_y1, gd_y2;
15180     int id = i;
15181
15182     x = DX + *gamebutton_info[i].x;
15183     y = DY + *gamebutton_info[i].y;
15184     gd_xoffset = gamebutton_info[i].gd_x;
15185     gd_yoffset = gamebutton_info[i].gd_y;
15186     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15187     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15188
15189     if (id == GAME_CTRL_ID_STOP ||
15190         id == GAME_CTRL_ID_PAUSE ||
15191         id == GAME_CTRL_ID_PLAY)
15192     {
15193       button_type = GD_TYPE_NORMAL_BUTTON;
15194       checked = FALSE;
15195       event_mask = GD_EVENT_RELEASED;
15196       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15197       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15198     }
15199     else
15200     {
15201       button_type = GD_TYPE_CHECK_BUTTON;
15202       checked =
15203         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15204          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15205          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15206       event_mask = GD_EVENT_PRESSED;
15207       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15208       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15209     }
15210
15211     gi = CreateGadget(GDI_CUSTOM_ID, id,
15212                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15213 #if 1
15214                       GDI_X, x,
15215                       GDI_Y, y,
15216 #else
15217                       GDI_X, DX + gd_xoffset,
15218                       GDI_Y, DY + gd_yoffset,
15219 #endif
15220                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15221                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15222                       GDI_TYPE, button_type,
15223                       GDI_STATE, GD_BUTTON_UNPRESSED,
15224                       GDI_CHECKED, checked,
15225                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15226                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15227                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15228                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15229                       GDI_EVENT_MASK, event_mask,
15230                       GDI_CALLBACK_ACTION, HandleGameButtons,
15231                       GDI_END);
15232
15233     if (gi == NULL)
15234       Error(ERR_EXIT, "cannot create gadget");
15235
15236     game_gadget[id] = gi;
15237   }
15238 }
15239
15240 void FreeGameButtons()
15241 {
15242   int i;
15243
15244   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15245     FreeGadget(game_gadget[i]);
15246 }
15247
15248 static void MapGameButtons()
15249 {
15250   int i;
15251
15252   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15253     MapGadget(game_gadget[i]);
15254 }
15255
15256 void UnmapGameButtons()
15257 {
15258   int i;
15259
15260   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15261     UnmapGadget(game_gadget[i]);
15262 }
15263
15264 static void HandleGameButtons(struct GadgetInfo *gi)
15265 {
15266   int id = gi->custom_id;
15267
15268   if (game_status != GAME_MODE_PLAYING)
15269     return;
15270
15271   switch (id)
15272   {
15273     case GAME_CTRL_ID_STOP:
15274       if (tape.playing)
15275         TapeStop();
15276       else
15277         RequestQuitGame(TRUE);
15278       break;
15279
15280     case GAME_CTRL_ID_PAUSE:
15281       if (options.network)
15282       {
15283 #if defined(NETWORK_AVALIABLE)
15284         if (tape.pausing)
15285           SendToServer_ContinuePlaying();
15286         else
15287           SendToServer_PausePlaying();
15288 #endif
15289       }
15290       else
15291         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15292       break;
15293
15294     case GAME_CTRL_ID_PLAY:
15295       if (tape.pausing)
15296       {
15297 #if defined(NETWORK_AVALIABLE)
15298         if (options.network)
15299           SendToServer_ContinuePlaying();
15300         else
15301 #endif
15302         {
15303           tape.pausing = FALSE;
15304           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15305         }
15306       }
15307       break;
15308
15309     case SOUND_CTRL_ID_MUSIC:
15310       if (setup.sound_music)
15311       { 
15312         setup.sound_music = FALSE;
15313         FadeMusic();
15314       }
15315       else if (audio.music_available)
15316       { 
15317         setup.sound = setup.sound_music = TRUE;
15318
15319         SetAudioMode(setup.sound);
15320
15321         PlayLevelMusic();
15322       }
15323       break;
15324
15325     case SOUND_CTRL_ID_LOOPS:
15326       if (setup.sound_loops)
15327         setup.sound_loops = FALSE;
15328       else if (audio.loops_available)
15329       {
15330         setup.sound = setup.sound_loops = TRUE;
15331         SetAudioMode(setup.sound);
15332       }
15333       break;
15334
15335     case SOUND_CTRL_ID_SIMPLE:
15336       if (setup.sound_simple)
15337         setup.sound_simple = FALSE;
15338       else if (audio.sound_available)
15339       {
15340         setup.sound = setup.sound_simple = TRUE;
15341         SetAudioMode(setup.sound);
15342       }
15343       break;
15344
15345     default:
15346       break;
15347   }
15348 }