rnd-20070403-3-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   game_status = GAME_MODE_PSEUDO_PANEL;
2031
2032   for (i = 0; game_controls[i].nr != -1; i++)
2033   {
2034     int nr = game_controls[i].nr;
2035     int type = game_controls[i].type;
2036     struct TextPosInfo *pos = game_controls[i].pos;
2037     int value = game_control_value[nr];
2038     int last_value = last_game_control_value[nr];
2039     int size = pos->size;
2040     int font = pos->font;
2041
2042     if (value == last_value)
2043       continue;
2044
2045     last_game_control_value[nr] = value;
2046
2047 #if 0
2048     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2049 #endif
2050
2051     if (PANEL_DEACTIVATED(pos))
2052       continue;
2053
2054     if (type == TYPE_INTEGER)
2055     {
2056       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2057       {
2058         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2059
2060         if (use_dynamic_size)           /* use dynamic number of digits */
2061         {
2062           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2063           int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2064           int size2 = size1 + 1;
2065           int font1 = pos->font;
2066           int font2 = pos->font_alt;
2067
2068           size = (value < value_change ? size1 : size2);
2069           font = (value < value_change ? font1 : font2);
2070
2071           /* clear background if value just changed its size (dynamic digits) */
2072           if ((last_value < value_change) != (value < value_change))
2073           {
2074             int width1 = size1 * getFontWidth(font1);
2075             int width2 = size2 * getFontWidth(font2);
2076             int max_width = MAX(width1, width2);
2077             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2078
2079             pos->width = max_width;
2080
2081             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2082                                        max_width, max_height);
2083           }
2084         }
2085
2086         pos->width = size * getFontWidth(font);
2087       }
2088
2089       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2090     }
2091     else if (type == TYPE_ELEMENT)
2092     {
2093       int dst_x = PANEL_XPOS(pos);
2094       int dst_y = PANEL_YPOS(pos);
2095
2096       if (value == EL_UNDEFINED || value == EL_EMPTY)
2097       {
2098         int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2099         int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2100
2101         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2102                    size, size, dst_x, dst_y);
2103       }
2104       else
2105       {
2106         int graphic = el2panelimg(value);
2107
2108         DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2109       }
2110     }
2111     else if (type == TYPE_STRING)
2112     {
2113       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
2114                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
2115                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2116
2117       if (s != NULL)
2118       {
2119         char *s_cut = getStringCopyN(s, size);
2120
2121         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2122
2123         free(s_cut);
2124       }
2125     }
2126
2127     redraw_mask |= REDRAW_DOOR_1;
2128   }
2129
2130   game_status = GAME_MODE_PLAYING;
2131 }
2132
2133 void DrawGameValue_Emeralds(int value)
2134 {
2135   struct TextPosInfo *pos = &game.panel.gems;
2136 #if 1
2137   int font_nr = pos->font;
2138 #else
2139   int font_nr = FONT_TEXT_2;
2140 #endif
2141   int font_width = getFontWidth(font_nr);
2142   int chars = pos->size;
2143
2144 #if 1
2145   return;       /* !!! USE NEW STUFF !!! */
2146 #endif
2147
2148   if (PANEL_DEACTIVATED(pos))
2149     return;
2150
2151   pos->width = chars * font_width;
2152
2153   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2154 }
2155
2156 void DrawGameValue_Dynamite(int value)
2157 {
2158   struct TextPosInfo *pos = &game.panel.inventory_count;
2159 #if 1
2160   int font_nr = pos->font;
2161 #else
2162   int font_nr = FONT_TEXT_2;
2163 #endif
2164   int font_width = getFontWidth(font_nr);
2165   int chars = pos->size;
2166
2167 #if 1
2168   return;       /* !!! USE NEW STUFF !!! */
2169 #endif
2170
2171   if (PANEL_DEACTIVATED(pos))
2172     return;
2173
2174   pos->width = chars * font_width;
2175
2176   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2177 }
2178
2179 void DrawGameValue_Score(int value)
2180 {
2181   struct TextPosInfo *pos = &game.panel.score;
2182 #if 1
2183   int font_nr = pos->font;
2184 #else
2185   int font_nr = FONT_TEXT_2;
2186 #endif
2187   int font_width = getFontWidth(font_nr);
2188   int chars = pos->size;
2189
2190 #if 1
2191   return;       /* !!! USE NEW STUFF !!! */
2192 #endif
2193
2194   if (PANEL_DEACTIVATED(pos))
2195     return;
2196
2197   pos->width = chars * font_width;
2198
2199   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2200 }
2201
2202 void DrawGameValue_Time(int value)
2203 {
2204   struct TextPosInfo *pos = &game.panel.time;
2205   static int last_value = -1;
2206   int chars1 = 3;
2207   int chars2 = 4;
2208   int chars = pos->size;
2209 #if 1
2210   int font1_nr = pos->font;
2211   int font2_nr = pos->font_alt;
2212 #else
2213   int font1_nr = FONT_TEXT_2;
2214   int font2_nr = FONT_TEXT_1;
2215 #endif
2216   int font_nr = font1_nr;
2217   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2218
2219 #if 1
2220   return;       /* !!! USE NEW STUFF !!! */
2221 #endif
2222
2223   if (PANEL_DEACTIVATED(pos))
2224     return;
2225
2226   if (use_dynamic_chars)                /* use dynamic number of chars */
2227   {
2228     chars   = (value < 1000 ? chars1   : chars2);
2229     font_nr = (value < 1000 ? font1_nr : font2_nr);
2230   }
2231
2232   /* clear background if value just changed its size (dynamic chars only) */
2233   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2234   {
2235     int width1 = chars1 * getFontWidth(font1_nr);
2236     int width2 = chars2 * getFontWidth(font2_nr);
2237     int max_width = MAX(width1, width2);
2238     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2239
2240     pos->width = max_width;
2241
2242     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2243                                max_width, max_height);
2244   }
2245
2246   pos->width = chars * getFontWidth(font_nr);
2247
2248   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2249
2250   last_value = value;
2251 }
2252
2253 void DrawGameValue_Level(int value)
2254 {
2255   struct TextPosInfo *pos = &game.panel.level_number;
2256   int chars1 = 2;
2257   int chars2 = 3;
2258   int chars = pos->size;
2259 #if 1
2260   int font1_nr = pos->font;
2261   int font2_nr = pos->font_alt;
2262 #else
2263   int font1_nr = FONT_TEXT_2;
2264   int font2_nr = FONT_TEXT_1;
2265 #endif
2266   int font_nr = font1_nr;
2267   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2268
2269 #if 1
2270   return;       /* !!! USE NEW STUFF !!! */
2271 #endif
2272
2273   if (PANEL_DEACTIVATED(pos))
2274     return;
2275
2276   if (use_dynamic_chars)                /* use dynamic number of chars */
2277   {
2278     chars   = (level_nr < 100 ? chars1   : chars2);
2279     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2280   }
2281
2282   pos->width = chars * getFontWidth(font_nr);
2283
2284   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2285 }
2286
2287 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2288 {
2289 #if 0
2290   struct TextPosInfo *pos = &game.panel.keys;
2291 #endif
2292 #if 0
2293   int base_key_graphic = EL_KEY_1;
2294 #endif
2295   int i;
2296
2297 #if 1
2298   return;       /* !!! USE NEW STUFF !!! */
2299 #endif
2300
2301 #if 0
2302   if (PANEL_DEACTIVATED(pos))
2303     return;
2304 #endif
2305
2306 #if 0
2307   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308     base_key_graphic = EL_EM_KEY_1;
2309 #endif
2310
2311 #if 0
2312   pos->width = 4 * MINI_TILEX;
2313 #endif
2314
2315 #if 1
2316   for (i = 0; i < MAX_NUM_KEYS; i++)
2317 #else
2318   /* currently only 4 of 8 possible keys are displayed */
2319   for (i = 0; i < STD_NUM_KEYS; i++)
2320 #endif
2321   {
2322 #if 1
2323     struct TextPosInfo *pos = &game.panel.key[i];
2324 #endif
2325     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2326     int src_y = DOOR_GFX_PAGEY1 + 123;
2327 #if 1
2328     int dst_x = PANEL_XPOS(pos);
2329     int dst_y = PANEL_YPOS(pos);
2330 #else
2331     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2332     int dst_y = PANEL_YPOS(pos);
2333 #endif
2334
2335 #if 1
2336     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2337                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2338                    EL_KEY_1) + i;
2339     int graphic = el2edimg(element);
2340 #endif
2341
2342 #if 1
2343     if (PANEL_DEACTIVATED(pos))
2344       continue;
2345 #endif
2346
2347 #if 0
2348     /* masked blit with tiles from half-size scaled bitmap does not work yet
2349        (no mask bitmap created for these sizes after loading and scaling) --
2350        solution: load without creating mask, scale, then create final mask */
2351
2352     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2353                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2354
2355     if (key[i])
2356     {
2357 #if 0
2358       int graphic = el2edimg(base_key_graphic + i);
2359 #endif
2360       Bitmap *src_bitmap;
2361       int src_x, src_y;
2362
2363       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2364
2365       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2366                     dst_x - src_x, dst_y - src_y);
2367       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2368                        dst_x, dst_y);
2369     }
2370 #else
2371 #if 1
2372     if (key[i])
2373       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2374     else
2375       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2376                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2377 #else
2378     if (key[i])
2379       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2380     else
2381       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2382                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2383 #endif
2384 #endif
2385   }
2386 }
2387
2388 #else
2389
2390 void DrawGameValue_Emeralds(int value)
2391 {
2392   int font_nr = FONT_TEXT_2;
2393   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2394
2395   if (PANEL_DEACTIVATED(game.panel.gems))
2396     return;
2397
2398   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2399 }
2400
2401 void DrawGameValue_Dynamite(int value)
2402 {
2403   int font_nr = FONT_TEXT_2;
2404   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2405
2406   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2407     return;
2408
2409   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2410 }
2411
2412 void DrawGameValue_Score(int value)
2413 {
2414   int font_nr = FONT_TEXT_2;
2415   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2416
2417   if (PANEL_DEACTIVATED(game.panel.score))
2418     return;
2419
2420   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2421 }
2422
2423 void DrawGameValue_Time(int value)
2424 {
2425   int font1_nr = FONT_TEXT_2;
2426 #if 1
2427   int font2_nr = FONT_TEXT_1;
2428 #else
2429   int font2_nr = FONT_LEVEL_NUMBER;
2430 #endif
2431   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2432   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2433
2434   if (PANEL_DEACTIVATED(game.panel.time))
2435     return;
2436
2437   /* clear background if value just changed its size */
2438   if (value == 999 || value == 1000)
2439     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2440
2441   if (value < 1000)
2442     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2443   else
2444     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2445 }
2446
2447 void DrawGameValue_Level(int value)
2448 {
2449   int font1_nr = FONT_TEXT_2;
2450 #if 1
2451   int font2_nr = FONT_TEXT_1;
2452 #else
2453   int font2_nr = FONT_LEVEL_NUMBER;
2454 #endif
2455
2456   if (PANEL_DEACTIVATED(game.panel.level))
2457     return;
2458
2459   if (level_nr < 100)
2460     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2461   else
2462     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2463 }
2464
2465 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2466 {
2467   int base_key_graphic = EL_KEY_1;
2468   int i;
2469
2470   if (PANEL_DEACTIVATED(game.panel.keys))
2471     return;
2472
2473   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2474     base_key_graphic = EL_EM_KEY_1;
2475
2476   /* currently only 4 of 8 possible keys are displayed */
2477   for (i = 0; i < STD_NUM_KEYS; i++)
2478   {
2479     int x = XX_KEYS + i * MINI_TILEX;
2480     int y = YY_KEYS;
2481
2482     if (key[i])
2483       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2484     else
2485       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2486                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2487   }
2488 }
2489
2490 #endif
2491
2492 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2493                        int key_bits)
2494 {
2495   int key[MAX_NUM_KEYS];
2496   int i;
2497
2498   /* prevent EM engine from updating time/score values parallel to GameWon() */
2499   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2500       local_player->LevelSolved)
2501     return;
2502
2503   for (i = 0; i < MAX_NUM_KEYS; i++)
2504     key[i] = key_bits & (1 << i);
2505
2506   DrawGameValue_Level(level_nr);
2507
2508   DrawGameValue_Emeralds(emeralds);
2509   DrawGameValue_Dynamite(dynamite);
2510   DrawGameValue_Score(score);
2511   DrawGameValue_Time(time);
2512
2513   DrawGameValue_Keys(key);
2514 }
2515
2516 void DrawGameDoorValues()
2517 {
2518   UpdateGameControlValues();
2519   DisplayGameControlValues();
2520 }
2521
2522 void DrawGameDoorValues_OLD()
2523 {
2524   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2525   int dynamite_value = 0;
2526   int score_value = (local_player->LevelSolved ? local_player->score_final :
2527                      local_player->score);
2528   int gems_value = local_player->gems_still_needed;
2529   int key_bits = 0;
2530   int i, j;
2531
2532   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2533   {
2534     DrawGameDoorValues_EM();
2535
2536     return;
2537   }
2538
2539   if (game.centered_player_nr == -1)
2540   {
2541     for (i = 0; i < MAX_PLAYERS; i++)
2542     {
2543       for (j = 0; j < MAX_NUM_KEYS; j++)
2544         if (stored_player[i].key[j])
2545           key_bits |= (1 << j);
2546
2547       dynamite_value += stored_player[i].inventory_size;
2548     }
2549   }
2550   else
2551   {
2552     int player_nr = game.centered_player_nr;
2553
2554     for (i = 0; i < MAX_NUM_KEYS; i++)
2555       if (stored_player[player_nr].key[i])
2556         key_bits |= (1 << i);
2557
2558     dynamite_value = stored_player[player_nr].inventory_size;
2559   }
2560
2561   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2562                     key_bits);
2563 }
2564
2565
2566 /*
2567   =============================================================================
2568   InitGameEngine()
2569   -----------------------------------------------------------------------------
2570   initialize game engine due to level / tape version number
2571   =============================================================================
2572 */
2573
2574 static void InitGameEngine()
2575 {
2576   int i, j, k, l, x, y;
2577
2578   /* set game engine from tape file when re-playing, else from level file */
2579   game.engine_version = (tape.playing ? tape.engine_version :
2580                          level.game_version);
2581
2582   /* ---------------------------------------------------------------------- */
2583   /* set flags for bugs and changes according to active game engine version */
2584   /* ---------------------------------------------------------------------- */
2585
2586   /*
2587     Summary of bugfix/change:
2588     Fixed handling for custom elements that change when pushed by the player.
2589
2590     Fixed/changed in version:
2591     3.1.0
2592
2593     Description:
2594     Before 3.1.0, custom elements that "change when pushing" changed directly
2595     after the player started pushing them (until then handled in "DigField()").
2596     Since 3.1.0, these custom elements are not changed until the "pushing"
2597     move of the element is finished (now handled in "ContinueMoving()").
2598
2599     Affected levels/tapes:
2600     The first condition is generally needed for all levels/tapes before version
2601     3.1.0, which might use the old behaviour before it was changed; known tapes
2602     that are affected are some tapes from the level set "Walpurgis Gardens" by
2603     Jamie Cullen.
2604     The second condition is an exception from the above case and is needed for
2605     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2606     above (including some development versions of 3.1.0), but before it was
2607     known that this change would break tapes like the above and was fixed in
2608     3.1.1, so that the changed behaviour was active although the engine version
2609     while recording maybe was before 3.1.0. There is at least one tape that is
2610     affected by this exception, which is the tape for the one-level set "Bug
2611     Machine" by Juergen Bonhagen.
2612   */
2613
2614   game.use_change_when_pushing_bug =
2615     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2616      !(tape.playing &&
2617        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2618        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2619
2620   /*
2621     Summary of bugfix/change:
2622     Fixed handling for blocking the field the player leaves when moving.
2623
2624     Fixed/changed in version:
2625     3.1.1
2626
2627     Description:
2628     Before 3.1.1, when "block last field when moving" was enabled, the field
2629     the player is leaving when moving was blocked for the time of the move,
2630     and was directly unblocked afterwards. This resulted in the last field
2631     being blocked for exactly one less than the number of frames of one player
2632     move. Additionally, even when blocking was disabled, the last field was
2633     blocked for exactly one frame.
2634     Since 3.1.1, due to changes in player movement handling, the last field
2635     is not blocked at all when blocking is disabled. When blocking is enabled,
2636     the last field is blocked for exactly the number of frames of one player
2637     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2638     last field is blocked for exactly one more than the number of frames of
2639     one player move.
2640
2641     Affected levels/tapes:
2642     (!!! yet to be determined -- probably many !!!)
2643   */
2644
2645   game.use_block_last_field_bug =
2646     (game.engine_version < VERSION_IDENT(3,1,1,0));
2647
2648   /*
2649     Summary of bugfix/change:
2650     Changed behaviour of CE changes with multiple changes per single frame.
2651
2652     Fixed/changed in version:
2653     3.2.0-6
2654
2655     Description:
2656     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2657     This resulted in race conditions where CEs seem to behave strange in some
2658     situations (where triggered CE changes were just skipped because there was
2659     already a CE change on that tile in the playfield in that engine frame).
2660     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2661     (The number of changes per frame must be limited in any case, because else
2662     it is easily possible to define CE changes that would result in an infinite
2663     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2664     should be set large enough so that it would only be reached in cases where
2665     the corresponding CE change conditions run into a loop. Therefore, it seems
2666     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2667     maximal number of change pages for custom elements.)
2668
2669     Affected levels/tapes:
2670     Probably many.
2671   */
2672
2673 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2674   game.max_num_changes_per_frame = 1;
2675 #else
2676   game.max_num_changes_per_frame =
2677     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2678 #endif
2679
2680   /* ---------------------------------------------------------------------- */
2681
2682   /* default scan direction: scan playfield from top/left to bottom/right */
2683   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2684
2685   /* dynamically adjust element properties according to game engine version */
2686   InitElementPropertiesEngine(game.engine_version);
2687
2688 #if 0
2689   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2690   printf("          tape version == %06d [%s] [file: %06d]\n",
2691          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2692          tape.file_version);
2693   printf("       => game.engine_version == %06d\n", game.engine_version);
2694 #endif
2695
2696   /* ---------- initialize player's initial move delay --------------------- */
2697
2698   /* dynamically adjust player properties according to level information */
2699   for (i = 0; i < MAX_PLAYERS; i++)
2700     game.initial_move_delay_value[i] =
2701       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2702
2703   /* dynamically adjust player properties according to game engine version */
2704   for (i = 0; i < MAX_PLAYERS; i++)
2705     game.initial_move_delay[i] =
2706       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2707        game.initial_move_delay_value[i] : 0);
2708
2709   /* ---------- initialize player's initial push delay --------------------- */
2710
2711   /* dynamically adjust player properties according to game engine version */
2712   game.initial_push_delay_value =
2713     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2714
2715   /* ---------- initialize changing elements ------------------------------- */
2716
2717   /* initialize changing elements information */
2718   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2719   {
2720     struct ElementInfo *ei = &element_info[i];
2721
2722     /* this pointer might have been changed in the level editor */
2723     ei->change = &ei->change_page[0];
2724
2725     if (!IS_CUSTOM_ELEMENT(i))
2726     {
2727       ei->change->target_element = EL_EMPTY_SPACE;
2728       ei->change->delay_fixed = 0;
2729       ei->change->delay_random = 0;
2730       ei->change->delay_frames = 1;
2731     }
2732
2733     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2734     {
2735       ei->has_change_event[j] = FALSE;
2736
2737       ei->event_page_nr[j] = 0;
2738       ei->event_page[j] = &ei->change_page[0];
2739     }
2740   }
2741
2742   /* add changing elements from pre-defined list */
2743   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2744   {
2745     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2746     struct ElementInfo *ei = &element_info[ch_delay->element];
2747
2748     ei->change->target_element       = ch_delay->target_element;
2749     ei->change->delay_fixed          = ch_delay->change_delay;
2750
2751     ei->change->pre_change_function  = ch_delay->pre_change_function;
2752     ei->change->change_function      = ch_delay->change_function;
2753     ei->change->post_change_function = ch_delay->post_change_function;
2754
2755     ei->change->can_change = TRUE;
2756     ei->change->can_change_or_has_action = TRUE;
2757
2758     ei->has_change_event[CE_DELAY] = TRUE;
2759
2760     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2761     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2762   }
2763
2764   /* ---------- initialize internal run-time variables ------------- */
2765
2766   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2767   {
2768     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2769
2770     for (j = 0; j < ei->num_change_pages; j++)
2771     {
2772       ei->change_page[j].can_change_or_has_action =
2773         (ei->change_page[j].can_change |
2774          ei->change_page[j].has_action);
2775     }
2776   }
2777
2778   /* add change events from custom element configuration */
2779   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2780   {
2781     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2782
2783     for (j = 0; j < ei->num_change_pages; j++)
2784     {
2785       if (!ei->change_page[j].can_change_or_has_action)
2786         continue;
2787
2788       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2789       {
2790         /* only add event page for the first page found with this event */
2791         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2792         {
2793           ei->has_change_event[k] = TRUE;
2794
2795           ei->event_page_nr[k] = j;
2796           ei->event_page[k] = &ei->change_page[j];
2797         }
2798       }
2799     }
2800   }
2801
2802   /* ---------- initialize run-time trigger player and element ------------- */
2803
2804   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2805   {
2806     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2807
2808     for (j = 0; j < ei->num_change_pages; j++)
2809     {
2810       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2811       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2812       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2813       ei->change_page[j].actual_trigger_ce_value = 0;
2814       ei->change_page[j].actual_trigger_ce_score = 0;
2815     }
2816   }
2817
2818   /* ---------- initialize trigger events ---------------------------------- */
2819
2820   /* initialize trigger events information */
2821   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2822     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2823       trigger_events[i][j] = FALSE;
2824
2825   /* add trigger events from element change event properties */
2826   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2827   {
2828     struct ElementInfo *ei = &element_info[i];
2829
2830     for (j = 0; j < ei->num_change_pages; j++)
2831     {
2832       if (!ei->change_page[j].can_change_or_has_action)
2833         continue;
2834
2835       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2836       {
2837         int trigger_element = ei->change_page[j].trigger_element;
2838
2839         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2840         {
2841           if (ei->change_page[j].has_event[k])
2842           {
2843             if (IS_GROUP_ELEMENT(trigger_element))
2844             {
2845               struct ElementGroupInfo *group =
2846                 element_info[trigger_element].group;
2847
2848               for (l = 0; l < group->num_elements_resolved; l++)
2849                 trigger_events[group->element_resolved[l]][k] = TRUE;
2850             }
2851             else if (trigger_element == EL_ANY_ELEMENT)
2852               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2853                 trigger_events[l][k] = TRUE;
2854             else
2855               trigger_events[trigger_element][k] = TRUE;
2856           }
2857         }
2858       }
2859     }
2860   }
2861
2862   /* ---------- initialize push delay -------------------------------------- */
2863
2864   /* initialize push delay values to default */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     if (!IS_CUSTOM_ELEMENT(i))
2868     {
2869       /* set default push delay values (corrected since version 3.0.7-1) */
2870       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2871       {
2872         element_info[i].push_delay_fixed = 2;
2873         element_info[i].push_delay_random = 8;
2874       }
2875       else
2876       {
2877         element_info[i].push_delay_fixed = 8;
2878         element_info[i].push_delay_random = 8;
2879       }
2880     }
2881   }
2882
2883   /* set push delay value for certain elements from pre-defined list */
2884   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2885   {
2886     int e = push_delay_list[i].element;
2887
2888     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2889     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2890   }
2891
2892   /* set push delay value for Supaplex elements for newer engine versions */
2893   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2894   {
2895     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2896     {
2897       if (IS_SP_ELEMENT(i))
2898       {
2899         /* set SP push delay to just enough to push under a falling zonk */
2900         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2901
2902         element_info[i].push_delay_fixed  = delay;
2903         element_info[i].push_delay_random = 0;
2904       }
2905     }
2906   }
2907
2908   /* ---------- initialize move stepsize ----------------------------------- */
2909
2910   /* initialize move stepsize values to default */
2911   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2912     if (!IS_CUSTOM_ELEMENT(i))
2913       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2914
2915   /* set move stepsize value for certain elements from pre-defined list */
2916   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2917   {
2918     int e = move_stepsize_list[i].element;
2919
2920     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2921   }
2922
2923   /* ---------- initialize collect score ----------------------------------- */
2924
2925   /* initialize collect score values for custom elements from initial value */
2926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927     if (IS_CUSTOM_ELEMENT(i))
2928       element_info[i].collect_score = element_info[i].collect_score_initial;
2929
2930   /* ---------- initialize collect count ----------------------------------- */
2931
2932   /* initialize collect count values for non-custom elements */
2933   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2934     if (!IS_CUSTOM_ELEMENT(i))
2935       element_info[i].collect_count_initial = 0;
2936
2937   /* add collect count values for all elements from pre-defined list */
2938   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2939     element_info[collect_count_list[i].element].collect_count_initial =
2940       collect_count_list[i].count;
2941
2942   /* ---------- initialize access direction -------------------------------- */
2943
2944   /* initialize access direction values to default (access from every side) */
2945   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2946     if (!IS_CUSTOM_ELEMENT(i))
2947       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2948
2949   /* set access direction value for certain elements from pre-defined list */
2950   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2951     element_info[access_direction_list[i].element].access_direction =
2952       access_direction_list[i].direction;
2953
2954   /* ---------- initialize explosion content ------------------------------- */
2955   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2956   {
2957     if (IS_CUSTOM_ELEMENT(i))
2958       continue;
2959
2960     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2961     {
2962       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2963
2964       element_info[i].content.e[x][y] =
2965         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2966          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2967          i == EL_PLAYER_3 ? EL_EMERALD :
2968          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2969          i == EL_MOLE ? EL_EMERALD_RED :
2970          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2971          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2972          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2973          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2974          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2975          i == EL_WALL_EMERALD ? EL_EMERALD :
2976          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2977          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2978          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2979          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2980          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2981          i == EL_WALL_PEARL ? EL_PEARL :
2982          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2983          EL_EMPTY);
2984     }
2985   }
2986
2987   /* ---------- initialize recursion detection ------------------------------ */
2988   recursion_loop_depth = 0;
2989   recursion_loop_detected = FALSE;
2990   recursion_loop_element = EL_UNDEFINED;
2991
2992   /* ---------- initialize graphics engine ---------------------------------- */
2993   game.scroll_delay_value =
2994     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2995      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
2996   game.scroll_delay_value =
2997     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2998 }
2999
3000 int get_num_special_action(int element, int action_first, int action_last)
3001 {
3002   int num_special_action = 0;
3003   int i, j;
3004
3005   for (i = action_first; i <= action_last; i++)
3006   {
3007     boolean found = FALSE;
3008
3009     for (j = 0; j < NUM_DIRECTIONS; j++)
3010       if (el_act_dir2img(element, i, j) !=
3011           el_act_dir2img(element, ACTION_DEFAULT, j))
3012         found = TRUE;
3013
3014     if (found)
3015       num_special_action++;
3016     else
3017       break;
3018   }
3019
3020   return num_special_action;
3021 }
3022
3023
3024 /*
3025   =============================================================================
3026   InitGame()
3027   -----------------------------------------------------------------------------
3028   initialize and start new game
3029   =============================================================================
3030 */
3031
3032 void InitGame()
3033 {
3034   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3035   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3036   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3037 #if 0
3038   boolean do_fading = (game_status == GAME_MODE_MAIN);
3039 #endif
3040   int i, j, x, y;
3041
3042   game_status = GAME_MODE_PLAYING;
3043
3044   InitGameEngine();
3045   InitGameControlValues();
3046
3047   /* don't play tapes over network */
3048   network_playing = (options.network && !tape.playing);
3049
3050   for (i = 0; i < MAX_PLAYERS; i++)
3051   {
3052     struct PlayerInfo *player = &stored_player[i];
3053
3054     player->index_nr = i;
3055     player->index_bit = (1 << i);
3056     player->element_nr = EL_PLAYER_1 + i;
3057
3058     player->present = FALSE;
3059     player->active = FALSE;
3060     player->killed = FALSE;
3061
3062     player->action = 0;
3063     player->effective_action = 0;
3064     player->programmed_action = 0;
3065
3066     player->score = 0;
3067     player->score_final = 0;
3068
3069     player->gems_still_needed = level.gems_needed;
3070     player->sokobanfields_still_needed = 0;
3071     player->lights_still_needed = 0;
3072     player->friends_still_needed = 0;
3073
3074     for (j = 0; j < MAX_NUM_KEYS; j++)
3075       player->key[j] = FALSE;
3076
3077     player->num_white_keys = 0;
3078
3079     player->dynabomb_count = 0;
3080     player->dynabomb_size = 1;
3081     player->dynabombs_left = 0;
3082     player->dynabomb_xl = FALSE;
3083
3084     player->MovDir = MV_NONE;
3085     player->MovPos = 0;
3086     player->GfxPos = 0;
3087     player->GfxDir = MV_NONE;
3088     player->GfxAction = ACTION_DEFAULT;
3089     player->Frame = 0;
3090     player->StepFrame = 0;
3091
3092     player->use_murphy = FALSE;
3093     player->artwork_element =
3094       (level.use_artwork_element[i] ? level.artwork_element[i] :
3095        player->element_nr);
3096
3097     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3098     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3099
3100     player->gravity = level.initial_player_gravity[i];
3101
3102     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3103
3104     player->actual_frame_counter = 0;
3105
3106     player->step_counter = 0;
3107
3108     player->last_move_dir = MV_NONE;
3109
3110     player->is_active = FALSE;
3111
3112     player->is_waiting = FALSE;
3113     player->is_moving = FALSE;
3114     player->is_auto_moving = FALSE;
3115     player->is_digging = FALSE;
3116     player->is_snapping = FALSE;
3117     player->is_collecting = FALSE;
3118     player->is_pushing = FALSE;
3119     player->is_switching = FALSE;
3120     player->is_dropping = FALSE;
3121     player->is_dropping_pressed = FALSE;
3122
3123     player->is_bored = FALSE;
3124     player->is_sleeping = FALSE;
3125
3126     player->frame_counter_bored = -1;
3127     player->frame_counter_sleeping = -1;
3128
3129     player->anim_delay_counter = 0;
3130     player->post_delay_counter = 0;
3131
3132     player->dir_waiting = MV_NONE;
3133     player->action_waiting = ACTION_DEFAULT;
3134     player->last_action_waiting = ACTION_DEFAULT;
3135     player->special_action_bored = ACTION_DEFAULT;
3136     player->special_action_sleeping = ACTION_DEFAULT;
3137
3138     player->switch_x = -1;
3139     player->switch_y = -1;
3140
3141     player->drop_x = -1;
3142     player->drop_y = -1;
3143
3144     player->show_envelope = 0;
3145
3146     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3147
3148     player->push_delay       = -1;      /* initialized when pushing starts */
3149     player->push_delay_value = game.initial_push_delay_value;
3150
3151     player->drop_delay = 0;
3152     player->drop_pressed_delay = 0;
3153
3154     player->last_jx = -1;
3155     player->last_jy = -1;
3156     player->jx = -1;
3157     player->jy = -1;
3158
3159     player->shield_normal_time_left = 0;
3160     player->shield_deadly_time_left = 0;
3161
3162     player->inventory_infinite_element = EL_UNDEFINED;
3163     player->inventory_size = 0;
3164
3165     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3166     SnapField(player, 0, 0);
3167
3168     player->LevelSolved = FALSE;
3169     player->GameOver = FALSE;
3170
3171     player->LevelSolved_GameWon = FALSE;
3172     player->LevelSolved_GameEnd = FALSE;
3173     player->LevelSolved_PanelOff = FALSE;
3174     player->LevelSolved_SaveTape = FALSE;
3175     player->LevelSolved_SaveScore = FALSE;
3176   }
3177
3178   network_player_action_received = FALSE;
3179
3180 #if defined(NETWORK_AVALIABLE)
3181   /* initial null action */
3182   if (network_playing)
3183     SendToServer_MovePlayer(MV_NONE);
3184 #endif
3185
3186   ZX = ZY = -1;
3187   ExitX = ExitY = -1;
3188
3189   FrameCounter = 0;
3190   TimeFrames = 0;
3191   TimePlayed = 0;
3192   TimeLeft = level.time;
3193   TapeTime = 0;
3194
3195   ScreenMovDir = MV_NONE;
3196   ScreenMovPos = 0;
3197   ScreenGfxPos = 0;
3198
3199   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3200
3201   AllPlayersGone = FALSE;
3202
3203   game.yamyam_content_nr = 0;
3204   game.magic_wall_active = FALSE;
3205   game.magic_wall_time_left = 0;
3206   game.light_time_left = 0;
3207   game.timegate_time_left = 0;
3208   game.switchgate_pos = 0;
3209   game.wind_direction = level.wind_direction_initial;
3210
3211 #if !USE_PLAYER_GRAVITY
3212   game.gravity = FALSE;
3213   game.explosions_delayed = TRUE;
3214 #endif
3215
3216   game.lenses_time_left = 0;
3217   game.magnify_time_left = 0;
3218
3219   game.ball_state = level.ball_state_initial;
3220   game.ball_content_nr = 0;
3221
3222   game.envelope_active = FALSE;
3223
3224   /* set focus to local player for network games, else to all players */
3225   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3226   game.centered_player_nr_next = game.centered_player_nr;
3227   game.set_centered_player = FALSE;
3228
3229   if (network_playing && tape.recording)
3230   {
3231     /* store client dependent player focus when recording network games */
3232     tape.centered_player_nr_next = game.centered_player_nr_next;
3233     tape.set_centered_player = TRUE;
3234   }
3235
3236   for (i = 0; i < NUM_BELTS; i++)
3237   {
3238     game.belt_dir[i] = MV_NONE;
3239     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3240   }
3241
3242   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3243     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3244
3245   SCAN_PLAYFIELD(x, y)
3246   {
3247     Feld[x][y] = level.field[x][y];
3248     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3249     ChangeDelay[x][y] = 0;
3250     ChangePage[x][y] = -1;
3251 #if USE_NEW_CUSTOM_VALUE
3252     CustomValue[x][y] = 0;              /* initialized in InitField() */
3253 #endif
3254     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3255     AmoebaNr[x][y] = 0;
3256     WasJustMoving[x][y] = 0;
3257     WasJustFalling[x][y] = 0;
3258     CheckCollision[x][y] = 0;
3259     CheckImpact[x][y] = 0;
3260     Stop[x][y] = FALSE;
3261     Pushed[x][y] = FALSE;
3262
3263     ChangeCount[x][y] = 0;
3264     ChangeEvent[x][y] = -1;
3265
3266     ExplodePhase[x][y] = 0;
3267     ExplodeDelay[x][y] = 0;
3268     ExplodeField[x][y] = EX_TYPE_NONE;
3269
3270     RunnerVisit[x][y] = 0;
3271     PlayerVisit[x][y] = 0;
3272
3273     GfxFrame[x][y] = 0;
3274     GfxRandom[x][y] = INIT_GFX_RANDOM();
3275     GfxElement[x][y] = EL_UNDEFINED;
3276     GfxAction[x][y] = ACTION_DEFAULT;
3277     GfxDir[x][y] = MV_NONE;
3278   }
3279
3280   SCAN_PLAYFIELD(x, y)
3281   {
3282     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3283       emulate_bd = FALSE;
3284     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3285       emulate_sb = FALSE;
3286     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3287       emulate_sp = FALSE;
3288
3289     InitField(x, y, TRUE);
3290   }
3291
3292   InitBeltMovement();
3293
3294   for (i = 0; i < MAX_PLAYERS; i++)
3295   {
3296     struct PlayerInfo *player = &stored_player[i];
3297
3298     /* set number of special actions for bored and sleeping animation */
3299     player->num_special_action_bored =
3300       get_num_special_action(player->artwork_element,
3301                              ACTION_BORING_1, ACTION_BORING_LAST);
3302     player->num_special_action_sleeping =
3303       get_num_special_action(player->artwork_element,
3304                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3305   }
3306
3307   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3308                     emulate_sb ? EMU_SOKOBAN :
3309                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3310
3311 #if USE_NEW_ALL_SLIPPERY
3312   /* initialize type of slippery elements */
3313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3314   {
3315     if (!IS_CUSTOM_ELEMENT(i))
3316     {
3317       /* default: elements slip down either to the left or right randomly */
3318       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3319
3320       /* SP style elements prefer to slip down on the left side */
3321       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3322         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3323
3324       /* BD style elements prefer to slip down on the left side */
3325       if (game.emulation == EMU_BOULDERDASH)
3326         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3327     }
3328   }
3329 #endif
3330
3331   /* initialize explosion and ignition delay */
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333   {
3334     if (!IS_CUSTOM_ELEMENT(i))
3335     {
3336       int num_phase = 8;
3337       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3338                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3339                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3340       int last_phase = (num_phase + 1) * delay;
3341       int half_phase = (num_phase / 2) * delay;
3342
3343       element_info[i].explosion_delay = last_phase - 1;
3344       element_info[i].ignition_delay = half_phase;
3345
3346       if (i == EL_BLACK_ORB)
3347         element_info[i].ignition_delay = 1;
3348     }
3349
3350 #if 0
3351     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3352       element_info[i].explosion_delay = 1;
3353
3354     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3355       element_info[i].ignition_delay = 1;
3356 #endif
3357   }
3358
3359   /* correct non-moving belts to start moving left */
3360   for (i = 0; i < NUM_BELTS; i++)
3361     if (game.belt_dir[i] == MV_NONE)
3362       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3363
3364   /* check if any connected player was not found in playfield */
3365   for (i = 0; i < MAX_PLAYERS; i++)
3366   {
3367     struct PlayerInfo *player = &stored_player[i];
3368
3369     if (player->connected && !player->present)
3370     {
3371       for (j = 0; j < MAX_PLAYERS; j++)
3372       {
3373         struct PlayerInfo *some_player = &stored_player[j];
3374         int jx = some_player->jx, jy = some_player->jy;
3375
3376         /* assign first free player found that is present in the playfield */
3377         if (some_player->present && !some_player->connected)
3378         {
3379           player->present = TRUE;
3380           player->active = TRUE;
3381
3382           some_player->present = FALSE;
3383           some_player->active = FALSE;
3384
3385           player->artwork_element = some_player->artwork_element;
3386
3387           player->block_last_field       = some_player->block_last_field;
3388           player->block_delay_adjustment = some_player->block_delay_adjustment;
3389
3390           StorePlayer[jx][jy] = player->element_nr;
3391           player->jx = player->last_jx = jx;
3392           player->jy = player->last_jy = jy;
3393
3394           break;
3395         }
3396       }
3397     }
3398   }
3399
3400   if (tape.playing)
3401   {
3402     /* when playing a tape, eliminate all players who do not participate */
3403
3404     for (i = 0; i < MAX_PLAYERS; i++)
3405     {
3406       if (stored_player[i].active && !tape.player_participates[i])
3407       {
3408         struct PlayerInfo *player = &stored_player[i];
3409         int jx = player->jx, jy = player->jy;
3410
3411         player->active = FALSE;
3412         StorePlayer[jx][jy] = 0;
3413         Feld[jx][jy] = EL_EMPTY;
3414       }
3415     }
3416   }
3417   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3418   {
3419     /* when in single player mode, eliminate all but the first active player */
3420
3421     for (i = 0; i < MAX_PLAYERS; i++)
3422     {
3423       if (stored_player[i].active)
3424       {
3425         for (j = i + 1; j < MAX_PLAYERS; j++)
3426         {
3427           if (stored_player[j].active)
3428           {
3429             struct PlayerInfo *player = &stored_player[j];
3430             int jx = player->jx, jy = player->jy;
3431
3432             player->active = FALSE;
3433             player->present = FALSE;
3434
3435             StorePlayer[jx][jy] = 0;
3436             Feld[jx][jy] = EL_EMPTY;
3437           }
3438         }
3439       }
3440     }
3441   }
3442
3443   /* when recording the game, store which players take part in the game */
3444   if (tape.recording)
3445   {
3446     for (i = 0; i < MAX_PLAYERS; i++)
3447       if (stored_player[i].active)
3448         tape.player_participates[i] = TRUE;
3449   }
3450
3451   if (options.debug)
3452   {
3453     for (i = 0; i < MAX_PLAYERS; i++)
3454     {
3455       struct PlayerInfo *player = &stored_player[i];
3456
3457       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3458              i+1,
3459              player->present,
3460              player->connected,
3461              player->active);
3462       if (local_player == player)
3463         printf("Player  %d is local player.\n", i+1);
3464     }
3465   }
3466
3467   if (BorderElement == EL_EMPTY)
3468   {
3469     SBX_Left = 0;
3470     SBX_Right = lev_fieldx - SCR_FIELDX;
3471     SBY_Upper = 0;
3472     SBY_Lower = lev_fieldy - SCR_FIELDY;
3473   }
3474   else
3475   {
3476     SBX_Left = -1;
3477     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3478     SBY_Upper = -1;
3479     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3480   }
3481
3482   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3483     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3484
3485   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3486     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3487
3488   /* if local player not found, look for custom element that might create
3489      the player (make some assumptions about the right custom element) */
3490   if (!local_player->present)
3491   {
3492     int start_x = 0, start_y = 0;
3493     int found_rating = 0;
3494     int found_element = EL_UNDEFINED;
3495     int player_nr = local_player->index_nr;
3496
3497     SCAN_PLAYFIELD(x, y)
3498     {
3499       int element = Feld[x][y];
3500       int content;
3501       int xx, yy;
3502       boolean is_player;
3503
3504       if (level.use_start_element[player_nr] &&
3505           level.start_element[player_nr] == element &&
3506           found_rating < 4)
3507       {
3508         start_x = x;
3509         start_y = y;
3510
3511         found_rating = 4;
3512         found_element = element;
3513       }
3514
3515       if (!IS_CUSTOM_ELEMENT(element))
3516         continue;
3517
3518       if (CAN_CHANGE(element))
3519       {
3520         for (i = 0; i < element_info[element].num_change_pages; i++)
3521         {
3522           /* check for player created from custom element as single target */
3523           content = element_info[element].change_page[i].target_element;
3524           is_player = ELEM_IS_PLAYER(content);
3525
3526           if (is_player && (found_rating < 3 ||
3527                             (found_rating == 3 && element < found_element)))
3528           {
3529             start_x = x;
3530             start_y = y;
3531
3532             found_rating = 3;
3533             found_element = element;
3534           }
3535         }
3536       }
3537
3538       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3539       {
3540         /* check for player created from custom element as explosion content */
3541         content = element_info[element].content.e[xx][yy];
3542         is_player = ELEM_IS_PLAYER(content);
3543
3544         if (is_player && (found_rating < 2 ||
3545                           (found_rating == 2 && element < found_element)))
3546         {
3547           start_x = x + xx - 1;
3548           start_y = y + yy - 1;
3549
3550           found_rating = 2;
3551           found_element = element;
3552         }
3553
3554         if (!CAN_CHANGE(element))
3555           continue;
3556
3557         for (i = 0; i < element_info[element].num_change_pages; i++)
3558         {
3559           /* check for player created from custom element as extended target */
3560           content =
3561             element_info[element].change_page[i].target_content.e[xx][yy];
3562
3563           is_player = ELEM_IS_PLAYER(content);
3564
3565           if (is_player && (found_rating < 1 ||
3566                             (found_rating == 1 && element < found_element)))
3567           {
3568             start_x = x + xx - 1;
3569             start_y = y + yy - 1;
3570
3571             found_rating = 1;
3572             found_element = element;
3573           }
3574         }
3575       }
3576     }
3577
3578     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3579                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3580                 start_x - MIDPOSX);
3581
3582     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3583                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3584                 start_y - MIDPOSY);
3585   }
3586   else
3587   {
3588     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3589                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3590                 local_player->jx - MIDPOSX);
3591
3592     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3593                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3594                 local_player->jy - MIDPOSY);
3595   }
3596
3597   /* do not use PLAYING mask for fading out from main screen */
3598   game_status = GAME_MODE_MAIN;
3599
3600   StopAnimation();
3601
3602   if (!game.restart_level)
3603     CloseDoor(DOOR_CLOSE_1);
3604
3605 #if 1
3606   if (level_editor_test_game)
3607     FadeSkipNextFadeIn();
3608   else
3609     FadeSetEnterScreen();
3610 #else
3611   if (level_editor_test_game)
3612     fading = fading_none;
3613   else
3614     fading = menu.destination;
3615 #endif
3616
3617 #if 1
3618   FadeOut(REDRAW_FIELD);
3619 #else
3620   if (do_fading)
3621     FadeOut(REDRAW_FIELD);
3622 #endif
3623
3624   game_status = GAME_MODE_PLAYING;
3625
3626   /* !!! FIX THIS (START) !!! */
3627   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3628   {
3629     InitGameEngine_EM();
3630
3631     /* blit playfield from scroll buffer to normal back buffer for fading in */
3632     BlitScreenToBitmap_EM(backbuffer);
3633   }
3634   else
3635   {
3636     DrawLevel();
3637     DrawAllPlayers();
3638
3639     /* after drawing the level, correct some elements */
3640     if (game.timegate_time_left == 0)
3641       CloseAllOpenTimegates();
3642
3643     /* blit playfield from scroll buffer to normal back buffer for fading in */
3644     if (setup.soft_scrolling)
3645       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3646
3647     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3648   }
3649   /* !!! FIX THIS (END) !!! */
3650
3651 #if 1
3652   FadeIn(REDRAW_FIELD);
3653 #else
3654   if (do_fading)
3655     FadeIn(REDRAW_FIELD);
3656
3657   BackToFront();
3658 #endif
3659
3660   if (!game.restart_level)
3661   {
3662     /* copy default game door content to main double buffer */
3663     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3664                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3665   }
3666
3667   SetPanelBackground();
3668   SetDrawBackgroundMask(REDRAW_DOOR_1);
3669
3670   DrawGameDoorValues();
3671
3672   if (!game.restart_level)
3673   {
3674     UnmapGameButtons();
3675     UnmapTapeButtons();
3676     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3677     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3678     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3679     MapGameButtons();
3680     MapTapeButtons();
3681
3682     /* copy actual game door content to door double buffer for OpenDoor() */
3683     BlitBitmap(drawto, bitmap_db_door,
3684                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3685
3686     OpenDoor(DOOR_OPEN_ALL);
3687
3688     PlaySound(SND_GAME_STARTING);
3689
3690     if (setup.sound_music)
3691       PlayLevelMusic();
3692
3693     KeyboardAutoRepeatOffUnlessAutoplay();
3694
3695     if (options.debug)
3696     {
3697       for (i = 0; i < MAX_PLAYERS; i++)
3698         printf("Player %d %sactive.\n",
3699                i + 1, (stored_player[i].active ? "" : "not "));
3700     }
3701   }
3702
3703 #if 1
3704   UnmapAllGadgets();
3705
3706   MapGameButtons();
3707   MapTapeButtons();
3708 #endif
3709
3710   game.restart_level = FALSE;
3711 }
3712
3713 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3714 {
3715   /* this is used for non-R'n'D game engines to update certain engine values */
3716
3717   /* needed to determine if sounds are played within the visible screen area */
3718   scroll_x = actual_scroll_x;
3719   scroll_y = actual_scroll_y;
3720 }
3721
3722 void InitMovDir(int x, int y)
3723 {
3724   int i, element = Feld[x][y];
3725   static int xy[4][2] =
3726   {
3727     {  0, +1 },
3728     { +1,  0 },
3729     {  0, -1 },
3730     { -1,  0 }
3731   };
3732   static int direction[3][4] =
3733   {
3734     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3735     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3736     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3737   };
3738
3739   switch (element)
3740   {
3741     case EL_BUG_RIGHT:
3742     case EL_BUG_UP:
3743     case EL_BUG_LEFT:
3744     case EL_BUG_DOWN:
3745       Feld[x][y] = EL_BUG;
3746       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3747       break;
3748
3749     case EL_SPACESHIP_RIGHT:
3750     case EL_SPACESHIP_UP:
3751     case EL_SPACESHIP_LEFT:
3752     case EL_SPACESHIP_DOWN:
3753       Feld[x][y] = EL_SPACESHIP;
3754       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3755       break;
3756
3757     case EL_BD_BUTTERFLY_RIGHT:
3758     case EL_BD_BUTTERFLY_UP:
3759     case EL_BD_BUTTERFLY_LEFT:
3760     case EL_BD_BUTTERFLY_DOWN:
3761       Feld[x][y] = EL_BD_BUTTERFLY;
3762       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3763       break;
3764
3765     case EL_BD_FIREFLY_RIGHT:
3766     case EL_BD_FIREFLY_UP:
3767     case EL_BD_FIREFLY_LEFT:
3768     case EL_BD_FIREFLY_DOWN:
3769       Feld[x][y] = EL_BD_FIREFLY;
3770       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3771       break;
3772
3773     case EL_PACMAN_RIGHT:
3774     case EL_PACMAN_UP:
3775     case EL_PACMAN_LEFT:
3776     case EL_PACMAN_DOWN:
3777       Feld[x][y] = EL_PACMAN;
3778       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3779       break;
3780
3781     case EL_YAMYAM_LEFT:
3782     case EL_YAMYAM_RIGHT:
3783     case EL_YAMYAM_UP:
3784     case EL_YAMYAM_DOWN:
3785       Feld[x][y] = EL_YAMYAM;
3786       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3787       break;
3788
3789     case EL_SP_SNIKSNAK:
3790       MovDir[x][y] = MV_UP;
3791       break;
3792
3793     case EL_SP_ELECTRON:
3794       MovDir[x][y] = MV_LEFT;
3795       break;
3796
3797     case EL_MOLE_LEFT:
3798     case EL_MOLE_RIGHT:
3799     case EL_MOLE_UP:
3800     case EL_MOLE_DOWN:
3801       Feld[x][y] = EL_MOLE;
3802       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3803       break;
3804
3805     default:
3806       if (IS_CUSTOM_ELEMENT(element))
3807       {
3808         struct ElementInfo *ei = &element_info[element];
3809         int move_direction_initial = ei->move_direction_initial;
3810         int move_pattern = ei->move_pattern;
3811
3812         if (move_direction_initial == MV_START_PREVIOUS)
3813         {
3814           if (MovDir[x][y] != MV_NONE)
3815             return;
3816
3817           move_direction_initial = MV_START_AUTOMATIC;
3818         }
3819
3820         if (move_direction_initial == MV_START_RANDOM)
3821           MovDir[x][y] = 1 << RND(4);
3822         else if (move_direction_initial & MV_ANY_DIRECTION)
3823           MovDir[x][y] = move_direction_initial;
3824         else if (move_pattern == MV_ALL_DIRECTIONS ||
3825                  move_pattern == MV_TURNING_LEFT ||
3826                  move_pattern == MV_TURNING_RIGHT ||
3827                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3828                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3829                  move_pattern == MV_TURNING_RANDOM)
3830           MovDir[x][y] = 1 << RND(4);
3831         else if (move_pattern == MV_HORIZONTAL)
3832           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3833         else if (move_pattern == MV_VERTICAL)
3834           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3835         else if (move_pattern & MV_ANY_DIRECTION)
3836           MovDir[x][y] = element_info[element].move_pattern;
3837         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3838                  move_pattern == MV_ALONG_RIGHT_SIDE)
3839         {
3840           /* use random direction as default start direction */
3841           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3842             MovDir[x][y] = 1 << RND(4);
3843
3844           for (i = 0; i < NUM_DIRECTIONS; i++)
3845           {
3846             int x1 = x + xy[i][0];
3847             int y1 = y + xy[i][1];
3848
3849             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3850             {
3851               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3852                 MovDir[x][y] = direction[0][i];
3853               else
3854                 MovDir[x][y] = direction[1][i];
3855
3856               break;
3857             }
3858           }
3859         }                
3860       }
3861       else
3862       {
3863         MovDir[x][y] = 1 << RND(4);
3864
3865         if (element != EL_BUG &&
3866             element != EL_SPACESHIP &&
3867             element != EL_BD_BUTTERFLY &&
3868             element != EL_BD_FIREFLY)
3869           break;
3870
3871         for (i = 0; i < NUM_DIRECTIONS; i++)
3872         {
3873           int x1 = x + xy[i][0];
3874           int y1 = y + xy[i][1];
3875
3876           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3877           {
3878             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3879             {
3880               MovDir[x][y] = direction[0][i];
3881               break;
3882             }
3883             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3884                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3885             {
3886               MovDir[x][y] = direction[1][i];
3887               break;
3888             }
3889           }
3890         }
3891       }
3892       break;
3893   }
3894
3895   GfxDir[x][y] = MovDir[x][y];
3896 }
3897
3898 void InitAmoebaNr(int x, int y)
3899 {
3900   int i;
3901   int group_nr = AmoebeNachbarNr(x, y);
3902
3903   if (group_nr == 0)
3904   {
3905     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3906     {
3907       if (AmoebaCnt[i] == 0)
3908       {
3909         group_nr = i;
3910         break;
3911       }
3912     }
3913   }
3914
3915   AmoebaNr[x][y] = group_nr;
3916   AmoebaCnt[group_nr]++;
3917   AmoebaCnt2[group_nr]++;
3918 }
3919
3920 static void PlayerWins(struct PlayerInfo *player)
3921 {
3922   player->LevelSolved = TRUE;
3923   player->GameOver = TRUE;
3924
3925   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3926                          level.native_em_level->lev->score : player->score);
3927 }
3928
3929 void GameWon()
3930 {
3931   static int time, time_final;
3932   static int score, score_final;
3933   static int game_over_delay_1 = 0;
3934   static int game_over_delay_2 = 0;
3935   int game_over_delay_value_1 = 50;
3936   int game_over_delay_value_2 = 50;
3937
3938   if (!local_player->LevelSolved_GameWon)
3939   {
3940     int i;
3941
3942     /* do not start end game actions before the player stops moving (to exit) */
3943     if (local_player->MovPos)
3944       return;
3945
3946     local_player->LevelSolved_GameWon = TRUE;
3947     local_player->LevelSolved_SaveTape = tape.recording;
3948     local_player->LevelSolved_SaveScore = !tape.playing;
3949
3950     if (tape.auto_play)         /* tape might already be stopped here */
3951       tape.auto_play_level_solved = TRUE;
3952
3953 #if 1
3954     TapeStop();
3955 #endif
3956
3957     game_over_delay_1 = game_over_delay_value_1;
3958     game_over_delay_2 = game_over_delay_value_2;
3959
3960     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3961     score = score_final = local_player->score_final;
3962
3963     if (TimeLeft > 0)
3964     {
3965       time_final = 0;
3966       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3967     }
3968     else if (level.time == 0 && TimePlayed < 999)
3969     {
3970       time_final = 999;
3971       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3972     }
3973
3974     local_player->score_final = score_final;
3975
3976     if (level_editor_test_game)
3977     {
3978       time = time_final;
3979       score = score_final;
3980
3981 #if 1
3982       game_control_value[GAME_CONTROL_TIME] = time;
3983       game_control_value[GAME_CONTROL_SCORE] = score;
3984
3985       DisplayGameControlValues();
3986 #else
3987       DrawGameValue_Time(time);
3988       DrawGameValue_Score(score);
3989 #endif
3990     }
3991
3992     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3993     {
3994       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3995       {
3996         /* close exit door after last player */
3997         if ((AllPlayersGone &&
3998              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3999               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4000               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4001             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4002             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4003         {
4004           int element = Feld[ExitX][ExitY];
4005
4006 #if 0
4007           if (element == EL_EM_EXIT_OPEN ||
4008               element == EL_EM_STEEL_EXIT_OPEN)
4009           {
4010             Bang(ExitX, ExitY);
4011           }
4012           else
4013 #endif
4014           {
4015             Feld[ExitX][ExitY] =
4016               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4017                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4018                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4019                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4020                EL_EM_STEEL_EXIT_CLOSING);
4021
4022             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4023           }
4024         }
4025
4026         /* player disappears */
4027         DrawLevelField(ExitX, ExitY);
4028       }
4029
4030       for (i = 0; i < MAX_PLAYERS; i++)
4031       {
4032         struct PlayerInfo *player = &stored_player[i];
4033
4034         if (player->present)
4035         {
4036           RemovePlayer(player);
4037
4038           /* player disappears */
4039           DrawLevelField(player->jx, player->jy);
4040         }
4041       }
4042     }
4043
4044     PlaySound(SND_GAME_WINNING);
4045   }
4046
4047   if (game_over_delay_1 > 0)
4048   {
4049     game_over_delay_1--;
4050
4051     return;
4052   }
4053
4054   if (time != time_final)
4055   {
4056     int time_to_go = ABS(time_final - time);
4057     int time_count_dir = (time < time_final ? +1 : -1);
4058     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4059
4060     time  += time_count_steps * time_count_dir;
4061     score += time_count_steps * level.score[SC_TIME_BONUS];
4062
4063 #if 1
4064     game_control_value[GAME_CONTROL_TIME] = time;
4065     game_control_value[GAME_CONTROL_SCORE] = score;
4066
4067     DisplayGameControlValues();
4068 #else
4069     DrawGameValue_Time(time);
4070     DrawGameValue_Score(score);
4071 #endif
4072
4073     if (time == time_final)
4074       StopSound(SND_GAME_LEVELTIME_BONUS);
4075     else if (setup.sound_loops)
4076       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4077     else
4078       PlaySound(SND_GAME_LEVELTIME_BONUS);
4079
4080     return;
4081   }
4082
4083   local_player->LevelSolved_PanelOff = TRUE;
4084
4085   if (game_over_delay_2 > 0)
4086   {
4087     game_over_delay_2--;
4088
4089     return;
4090   }
4091
4092 #if 1
4093   GameEnd();
4094 #endif
4095 }
4096
4097 void GameEnd()
4098 {
4099   int hi_pos;
4100   boolean raise_level = FALSE;
4101
4102   local_player->LevelSolved_GameEnd = TRUE;
4103
4104   CloseDoor(DOOR_CLOSE_1);
4105
4106   if (local_player->LevelSolved_SaveTape)
4107   {
4108 #if 0
4109     TapeStop();
4110 #endif
4111
4112 #if 1
4113     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4114 #else
4115     SaveTape(tape.level_nr);            /* ask to save tape */
4116 #endif
4117   }
4118
4119   if (level_editor_test_game)
4120   {
4121     game_status = GAME_MODE_MAIN;
4122
4123 #if 1
4124     DrawAndFadeInMainMenu(REDRAW_FIELD);
4125 #else
4126     DrawMainMenu();
4127 #endif
4128
4129     return;
4130   }
4131
4132   if (!local_player->LevelSolved_SaveScore)
4133   {
4134 #if 1
4135     FadeOut(REDRAW_FIELD);
4136 #endif
4137
4138     game_status = GAME_MODE_MAIN;
4139
4140     DrawAndFadeInMainMenu(REDRAW_FIELD);
4141
4142     return;
4143   }
4144
4145   if (level_nr == leveldir_current->handicap_level)
4146   {
4147     leveldir_current->handicap_level++;
4148     SaveLevelSetup_SeriesInfo();
4149   }
4150
4151   if (level_nr < leveldir_current->last_level)
4152     raise_level = TRUE;                 /* advance to next level */
4153
4154   if ((hi_pos = NewHiScore()) >= 0) 
4155   {
4156     game_status = GAME_MODE_SCORES;
4157
4158     DrawHallOfFame(hi_pos);
4159
4160     if (raise_level)
4161     {
4162       level_nr++;
4163       TapeErase();
4164     }
4165   }
4166   else
4167   {
4168 #if 1
4169     FadeOut(REDRAW_FIELD);
4170 #endif
4171
4172     game_status = GAME_MODE_MAIN;
4173
4174     if (raise_level)
4175     {
4176       level_nr++;
4177       TapeErase();
4178     }
4179
4180     DrawAndFadeInMainMenu(REDRAW_FIELD);
4181   }
4182 }
4183
4184 int NewHiScore()
4185 {
4186   int k, l;
4187   int position = -1;
4188
4189   LoadScore(level_nr);
4190
4191   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4192       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4193     return -1;
4194
4195   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4196   {
4197     if (local_player->score_final > highscore[k].Score)
4198     {
4199       /* player has made it to the hall of fame */
4200
4201       if (k < MAX_SCORE_ENTRIES - 1)
4202       {
4203         int m = MAX_SCORE_ENTRIES - 1;
4204
4205 #ifdef ONE_PER_NAME
4206         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4207           if (strEqual(setup.player_name, highscore[l].Name))
4208             m = l;
4209         if (m == k)     /* player's new highscore overwrites his old one */
4210           goto put_into_list;
4211 #endif
4212
4213         for (l = m; l > k; l--)
4214         {
4215           strcpy(highscore[l].Name, highscore[l - 1].Name);
4216           highscore[l].Score = highscore[l - 1].Score;
4217         }
4218       }
4219
4220 #ifdef ONE_PER_NAME
4221       put_into_list:
4222 #endif
4223       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4224       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4225       highscore[k].Score = local_player->score_final; 
4226       position = k;
4227       break;
4228     }
4229
4230 #ifdef ONE_PER_NAME
4231     else if (!strncmp(setup.player_name, highscore[k].Name,
4232                       MAX_PLAYER_NAME_LEN))
4233       break;    /* player already there with a higher score */
4234 #endif
4235
4236   }
4237
4238   if (position >= 0) 
4239     SaveScore(level_nr);
4240
4241   return position;
4242 }
4243
4244 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4245 {
4246   int element = Feld[x][y];
4247   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4248   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4249   int horiz_move = (dx != 0);
4250   int sign = (horiz_move ? dx : dy);
4251   int step = sign * element_info[element].move_stepsize;
4252
4253   /* special values for move stepsize for spring and things on conveyor belt */
4254   if (horiz_move)
4255   {
4256     if (CAN_FALL(element) &&
4257         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4258       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4259     else if (element == EL_SPRING)
4260       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4261   }
4262
4263   return step;
4264 }
4265
4266 inline static int getElementMoveStepsize(int x, int y)
4267 {
4268   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4269 }
4270
4271 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4272 {
4273   if (player->GfxAction != action || player->GfxDir != dir)
4274   {
4275 #if 0
4276     printf("Player frame reset! (%d => %d, %d => %d)\n",
4277            player->GfxAction, action, player->GfxDir, dir);
4278 #endif
4279
4280     player->GfxAction = action;
4281     player->GfxDir = dir;
4282     player->Frame = 0;
4283     player->StepFrame = 0;
4284   }
4285 }
4286
4287 #if USE_GFX_RESET_GFX_ANIMATION
4288 static void ResetGfxFrame(int x, int y, boolean redraw)
4289 {
4290   int element = Feld[x][y];
4291   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4292   int last_gfx_frame = GfxFrame[x][y];
4293
4294   if (graphic_info[graphic].anim_global_sync)
4295     GfxFrame[x][y] = FrameCounter;
4296   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4297     GfxFrame[x][y] = CustomValue[x][y];
4298   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4299     GfxFrame[x][y] = element_info[element].collect_score;
4300   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4301     GfxFrame[x][y] = ChangeDelay[x][y];
4302
4303   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4304     DrawLevelGraphicAnimation(x, y, graphic);
4305 }
4306 #endif
4307
4308 static void ResetGfxAnimation(int x, int y)
4309 {
4310   GfxAction[x][y] = ACTION_DEFAULT;
4311   GfxDir[x][y] = MovDir[x][y];
4312   GfxFrame[x][y] = 0;
4313
4314 #if USE_GFX_RESET_GFX_ANIMATION
4315   ResetGfxFrame(x, y, FALSE);
4316 #endif
4317 }
4318
4319 static void ResetRandomAnimationValue(int x, int y)
4320 {
4321   GfxRandom[x][y] = INIT_GFX_RANDOM();
4322 }
4323
4324 void InitMovingField(int x, int y, int direction)
4325 {
4326   int element = Feld[x][y];
4327   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4328   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4329   int newx = x + dx;
4330   int newy = y + dy;
4331   boolean is_moving_before, is_moving_after;
4332 #if 0
4333   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4334 #endif
4335
4336   /* check if element was/is moving or being moved before/after mode change */
4337 #if 1
4338 #if 1
4339   is_moving_before = (WasJustMoving[x][y] != 0);
4340 #else
4341   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4342   is_moving_before = WasJustMoving[x][y];
4343 #endif
4344 #else
4345   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4346 #endif
4347   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4348
4349   /* reset animation only for moving elements which change direction of moving
4350      or which just started or stopped moving
4351      (else CEs with property "can move" / "not moving" are reset each frame) */
4352 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4353 #if 1
4354   if (is_moving_before != is_moving_after ||
4355       direction != MovDir[x][y])
4356     ResetGfxAnimation(x, y);
4357 #else
4358   if ((is_moving_before || is_moving_after) && !continues_moving)
4359     ResetGfxAnimation(x, y);
4360 #endif
4361 #else
4362   if (!continues_moving)
4363     ResetGfxAnimation(x, y);
4364 #endif
4365
4366   MovDir[x][y] = direction;
4367   GfxDir[x][y] = direction;
4368
4369 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4370   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4371                      direction == MV_DOWN && CAN_FALL(element) ?
4372                      ACTION_FALLING : ACTION_MOVING);
4373 #else
4374   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4375                      ACTION_FALLING : ACTION_MOVING);
4376 #endif
4377
4378   /* this is needed for CEs with property "can move" / "not moving" */
4379
4380   if (is_moving_after)
4381   {
4382     if (Feld[newx][newy] == EL_EMPTY)
4383       Feld[newx][newy] = EL_BLOCKED;
4384
4385     MovDir[newx][newy] = MovDir[x][y];
4386
4387 #if USE_NEW_CUSTOM_VALUE
4388     CustomValue[newx][newy] = CustomValue[x][y];
4389 #endif
4390
4391     GfxFrame[newx][newy] = GfxFrame[x][y];
4392     GfxRandom[newx][newy] = GfxRandom[x][y];
4393     GfxAction[newx][newy] = GfxAction[x][y];
4394     GfxDir[newx][newy] = GfxDir[x][y];
4395   }
4396 }
4397
4398 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4399 {
4400   int direction = MovDir[x][y];
4401   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4402   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4403
4404   *goes_to_x = newx;
4405   *goes_to_y = newy;
4406 }
4407
4408 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4409 {
4410   int oldx = x, oldy = y;
4411   int direction = MovDir[x][y];
4412
4413   if (direction == MV_LEFT)
4414     oldx++;
4415   else if (direction == MV_RIGHT)
4416     oldx--;
4417   else if (direction == MV_UP)
4418     oldy++;
4419   else if (direction == MV_DOWN)
4420     oldy--;
4421
4422   *comes_from_x = oldx;
4423   *comes_from_y = oldy;
4424 }
4425
4426 int MovingOrBlocked2Element(int x, int y)
4427 {
4428   int element = Feld[x][y];
4429
4430   if (element == EL_BLOCKED)
4431   {
4432     int oldx, oldy;
4433
4434     Blocked2Moving(x, y, &oldx, &oldy);
4435     return Feld[oldx][oldy];
4436   }
4437   else
4438     return element;
4439 }
4440
4441 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4442 {
4443   /* like MovingOrBlocked2Element(), but if element is moving
4444      and (x,y) is the field the moving element is just leaving,
4445      return EL_BLOCKED instead of the element value */
4446   int element = Feld[x][y];
4447
4448   if (IS_MOVING(x, y))
4449   {
4450     if (element == EL_BLOCKED)
4451     {
4452       int oldx, oldy;
4453
4454       Blocked2Moving(x, y, &oldx, &oldy);
4455       return Feld[oldx][oldy];
4456     }
4457     else
4458       return EL_BLOCKED;
4459   }
4460   else
4461     return element;
4462 }
4463
4464 static void RemoveField(int x, int y)
4465 {
4466   Feld[x][y] = EL_EMPTY;
4467
4468   MovPos[x][y] = 0;
4469   MovDir[x][y] = 0;
4470   MovDelay[x][y] = 0;
4471
4472 #if USE_NEW_CUSTOM_VALUE
4473   CustomValue[x][y] = 0;
4474 #endif
4475
4476   AmoebaNr[x][y] = 0;
4477   ChangeDelay[x][y] = 0;
4478   ChangePage[x][y] = -1;
4479   Pushed[x][y] = FALSE;
4480
4481 #if 0
4482   ExplodeField[x][y] = EX_TYPE_NONE;
4483 #endif
4484
4485   GfxElement[x][y] = EL_UNDEFINED;
4486   GfxAction[x][y] = ACTION_DEFAULT;
4487   GfxDir[x][y] = MV_NONE;
4488 }
4489
4490 void RemoveMovingField(int x, int y)
4491 {
4492   int oldx = x, oldy = y, newx = x, newy = y;
4493   int element = Feld[x][y];
4494   int next_element = EL_UNDEFINED;
4495
4496   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4497     return;
4498
4499   if (IS_MOVING(x, y))
4500   {
4501     Moving2Blocked(x, y, &newx, &newy);
4502
4503     if (Feld[newx][newy] != EL_BLOCKED)
4504     {
4505       /* element is moving, but target field is not free (blocked), but
4506          already occupied by something different (example: acid pool);
4507          in this case, only remove the moving field, but not the target */
4508
4509       RemoveField(oldx, oldy);
4510
4511       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4512
4513       DrawLevelField(oldx, oldy);
4514
4515       return;
4516     }
4517   }
4518   else if (element == EL_BLOCKED)
4519   {
4520     Blocked2Moving(x, y, &oldx, &oldy);
4521     if (!IS_MOVING(oldx, oldy))
4522       return;
4523   }
4524
4525   if (element == EL_BLOCKED &&
4526       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4527        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4528        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4529        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4530        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4531        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4532     next_element = get_next_element(Feld[oldx][oldy]);
4533
4534   RemoveField(oldx, oldy);
4535   RemoveField(newx, newy);
4536
4537   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4538
4539   if (next_element != EL_UNDEFINED)
4540     Feld[oldx][oldy] = next_element;
4541
4542   DrawLevelField(oldx, oldy);
4543   DrawLevelField(newx, newy);
4544 }
4545
4546 void DrawDynamite(int x, int y)
4547 {
4548   int sx = SCREENX(x), sy = SCREENY(y);
4549   int graphic = el2img(Feld[x][y]);
4550   int frame;
4551
4552   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4553     return;
4554
4555   if (IS_WALKABLE_INSIDE(Back[x][y]))
4556     return;
4557
4558   if (Back[x][y])
4559     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4560   else if (Store[x][y])
4561     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4562
4563   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4564
4565   if (Back[x][y] || Store[x][y])
4566     DrawGraphicThruMask(sx, sy, graphic, frame);
4567   else
4568     DrawGraphic(sx, sy, graphic, frame);
4569 }
4570
4571 void CheckDynamite(int x, int y)
4572 {
4573   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4574   {
4575     MovDelay[x][y]--;
4576
4577     if (MovDelay[x][y] != 0)
4578     {
4579       DrawDynamite(x, y);
4580       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4581
4582       return;
4583     }
4584   }
4585
4586   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4587
4588   Bang(x, y);
4589 }
4590
4591 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4592 {
4593   boolean num_checked_players = 0;
4594   int i;
4595
4596   for (i = 0; i < MAX_PLAYERS; i++)
4597   {
4598     if (stored_player[i].active)
4599     {
4600       int sx = stored_player[i].jx;
4601       int sy = stored_player[i].jy;
4602
4603       if (num_checked_players == 0)
4604       {
4605         *sx1 = *sx2 = sx;
4606         *sy1 = *sy2 = sy;
4607       }
4608       else
4609       {
4610         *sx1 = MIN(*sx1, sx);
4611         *sy1 = MIN(*sy1, sy);
4612         *sx2 = MAX(*sx2, sx);
4613         *sy2 = MAX(*sy2, sy);
4614       }
4615
4616       num_checked_players++;
4617     }
4618   }
4619 }
4620
4621 static boolean checkIfAllPlayersFitToScreen_RND()
4622 {
4623   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4624
4625   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4626
4627   return (sx2 - sx1 < SCR_FIELDX &&
4628           sy2 - sy1 < SCR_FIELDY);
4629 }
4630
4631 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4632 {
4633   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4634
4635   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4636
4637   *sx = (sx1 + sx2) / 2;
4638   *sy = (sy1 + sy2) / 2;
4639 }
4640
4641 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4642                         boolean center_screen, boolean quick_relocation)
4643 {
4644   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4645   boolean no_delay = (tape.warp_forward);
4646   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4647   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4648
4649   if (quick_relocation)
4650   {
4651     int offset = game.scroll_delay_value;
4652
4653     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4654     {
4655       if (!level.shifted_relocation || center_screen)
4656       {
4657         /* quick relocation (without scrolling), with centering of screen */
4658
4659         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4660                     x > SBX_Right + MIDPOSX ? SBX_Right :
4661                     x - MIDPOSX);
4662
4663         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4664                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4665                     y - MIDPOSY);
4666       }
4667       else
4668       {
4669         /* quick relocation (without scrolling), but do not center screen */
4670
4671         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4672                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4673                                old_x - MIDPOSX);
4674
4675         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4676                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4677                                old_y - MIDPOSY);
4678
4679         int offset_x = x + (scroll_x - center_scroll_x);
4680         int offset_y = y + (scroll_y - center_scroll_y);
4681
4682         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4683                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4684                     offset_x - MIDPOSX);
4685
4686         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4687                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4688                     offset_y - MIDPOSY);
4689       }
4690     }
4691     else
4692     {
4693       /* quick relocation (without scrolling), inside visible screen area */
4694
4695       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4696           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4697         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4698
4699       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4700           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4701         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4702
4703       /* don't scroll over playfield boundaries */
4704       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4705         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4706
4707       /* don't scroll over playfield boundaries */
4708       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4709         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4710     }
4711
4712     RedrawPlayfield(TRUE, 0,0,0,0);
4713   }
4714   else
4715   {
4716 #if 1
4717     int scroll_xx, scroll_yy;
4718
4719     if (!level.shifted_relocation || center_screen)
4720     {
4721       /* visible relocation (with scrolling), with centering of screen */
4722
4723       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4724                    x > SBX_Right + MIDPOSX ? SBX_Right :
4725                    x - MIDPOSX);
4726
4727       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4728                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4729                    y - MIDPOSY);
4730     }
4731     else
4732     {
4733       /* visible relocation (with scrolling), but do not center screen */
4734
4735       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4736                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4737                              old_x - MIDPOSX);
4738
4739       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4740                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4741                              old_y - MIDPOSY);
4742
4743       int offset_x = x + (scroll_x - center_scroll_x);
4744       int offset_y = y + (scroll_y - center_scroll_y);
4745
4746       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4747                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4748                    offset_x - MIDPOSX);
4749
4750       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4751                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4752                    offset_y - MIDPOSY);
4753     }
4754
4755 #else
4756
4757     /* visible relocation (with scrolling), with centering of screen */
4758
4759     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4760                      x > SBX_Right + MIDPOSX ? SBX_Right :
4761                      x - MIDPOSX);
4762
4763     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4764                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4765                      y - MIDPOSY);
4766 #endif
4767
4768     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4769
4770     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4771     {
4772       int dx = 0, dy = 0;
4773       int fx = FX, fy = FY;
4774
4775       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4776       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4777
4778       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4779         break;
4780
4781       scroll_x -= dx;
4782       scroll_y -= dy;
4783
4784       fx += dx * TILEX / 2;
4785       fy += dy * TILEY / 2;
4786
4787       ScrollLevel(dx, dy);
4788       DrawAllPlayers();
4789
4790       /* scroll in two steps of half tile size to make things smoother */
4791       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4792       FlushDisplay();
4793       Delay(wait_delay_value);
4794
4795       /* scroll second step to align at full tile size */
4796       BackToFront();
4797       Delay(wait_delay_value);
4798     }
4799
4800     DrawAllPlayers();
4801     BackToFront();
4802     Delay(wait_delay_value);
4803   }
4804 }
4805
4806 void RelocatePlayer(int jx, int jy, int el_player_raw)
4807 {
4808   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4809   int player_nr = GET_PLAYER_NR(el_player);
4810   struct PlayerInfo *player = &stored_player[player_nr];
4811   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4812   boolean no_delay = (tape.warp_forward);
4813   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4814   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4815   int old_jx = player->jx;
4816   int old_jy = player->jy;
4817   int old_element = Feld[old_jx][old_jy];
4818   int element = Feld[jx][jy];
4819   boolean player_relocated = (old_jx != jx || old_jy != jy);
4820
4821   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4822   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4823   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4824   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4825   int leave_side_horiz = move_dir_horiz;
4826   int leave_side_vert  = move_dir_vert;
4827   int enter_side = enter_side_horiz | enter_side_vert;
4828   int leave_side = leave_side_horiz | leave_side_vert;
4829
4830   if (player->GameOver)         /* do not reanimate dead player */
4831     return;
4832
4833   if (!player_relocated)        /* no need to relocate the player */
4834     return;
4835
4836   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4837   {
4838     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4839     DrawLevelField(jx, jy);
4840   }
4841
4842   if (player->present)
4843   {
4844     while (player->MovPos)
4845     {
4846       ScrollPlayer(player, SCROLL_GO_ON);
4847       ScrollScreen(NULL, SCROLL_GO_ON);
4848
4849       AdvanceFrameAndPlayerCounters(player->index_nr);
4850
4851       DrawPlayer(player);
4852
4853       BackToFront();
4854       Delay(wait_delay_value);
4855     }
4856
4857     DrawPlayer(player);         /* needed here only to cleanup last field */
4858     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4859
4860     player->is_moving = FALSE;
4861   }
4862
4863   if (IS_CUSTOM_ELEMENT(old_element))
4864     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4865                                CE_LEFT_BY_PLAYER,
4866                                player->index_bit, leave_side);
4867
4868   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4869                                       CE_PLAYER_LEAVES_X,
4870                                       player->index_bit, leave_side);
4871
4872   Feld[jx][jy] = el_player;
4873   InitPlayerField(jx, jy, el_player, TRUE);
4874
4875   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4876   {
4877     Feld[jx][jy] = element;
4878     InitField(jx, jy, FALSE);
4879   }
4880
4881   /* only visually relocate centered player */
4882   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4883                      FALSE, level.instant_relocation);
4884
4885   TestIfPlayerTouchesBadThing(jx, jy);
4886   TestIfPlayerTouchesCustomElement(jx, jy);
4887
4888   if (IS_CUSTOM_ELEMENT(element))
4889     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4890                                player->index_bit, enter_side);
4891
4892   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4893                                       player->index_bit, enter_side);
4894 }
4895
4896 void Explode(int ex, int ey, int phase, int mode)
4897 {
4898   int x, y;
4899   int last_phase;
4900   int border_element;
4901
4902   /* !!! eliminate this variable !!! */
4903   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4904
4905   if (game.explosions_delayed)
4906   {
4907     ExplodeField[ex][ey] = mode;
4908     return;
4909   }
4910
4911   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4912   {
4913     int center_element = Feld[ex][ey];
4914     int artwork_element, explosion_element;     /* set these values later */
4915
4916 #if 0
4917     /* --- This is only really needed (and now handled) in "Impact()". --- */
4918     /* do not explode moving elements that left the explode field in time */
4919     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4920         center_element == EL_EMPTY &&
4921         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4922       return;
4923 #endif
4924
4925 #if 0
4926     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4927     if (mode == EX_TYPE_NORMAL ||
4928         mode == EX_TYPE_CENTER ||
4929         mode == EX_TYPE_CROSS)
4930       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4931 #endif
4932
4933     /* remove things displayed in background while burning dynamite */
4934     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4935       Back[ex][ey] = 0;
4936
4937     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4938     {
4939       /* put moving element to center field (and let it explode there) */
4940       center_element = MovingOrBlocked2Element(ex, ey);
4941       RemoveMovingField(ex, ey);
4942       Feld[ex][ey] = center_element;
4943     }
4944
4945     /* now "center_element" is finally determined -- set related values now */
4946     artwork_element = center_element;           /* for custom player artwork */
4947     explosion_element = center_element;         /* for custom player artwork */
4948
4949     if (IS_PLAYER(ex, ey))
4950     {
4951       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4952
4953       artwork_element = stored_player[player_nr].artwork_element;
4954
4955       if (level.use_explosion_element[player_nr])
4956       {
4957         explosion_element = level.explosion_element[player_nr];
4958         artwork_element = explosion_element;
4959       }
4960     }
4961
4962 #if 1
4963     if (mode == EX_TYPE_NORMAL ||
4964         mode == EX_TYPE_CENTER ||
4965         mode == EX_TYPE_CROSS)
4966       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4967 #endif
4968
4969     last_phase = element_info[explosion_element].explosion_delay + 1;
4970
4971     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4972     {
4973       int xx = x - ex + 1;
4974       int yy = y - ey + 1;
4975       int element;
4976
4977       if (!IN_LEV_FIELD(x, y) ||
4978           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4979           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4980         continue;
4981
4982       element = Feld[x][y];
4983
4984       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4985       {
4986         element = MovingOrBlocked2Element(x, y);
4987
4988         if (!IS_EXPLOSION_PROOF(element))
4989           RemoveMovingField(x, y);
4990       }
4991
4992       /* indestructible elements can only explode in center (but not flames) */
4993       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4994                                            mode == EX_TYPE_BORDER)) ||
4995           element == EL_FLAMES)
4996         continue;
4997
4998       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4999          behaviour, for example when touching a yamyam that explodes to rocks
5000          with active deadly shield, a rock is created under the player !!! */
5001       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5002 #if 0
5003       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5004           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5005            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5006 #else
5007       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5008 #endif
5009       {
5010         if (IS_ACTIVE_BOMB(element))
5011         {
5012           /* re-activate things under the bomb like gate or penguin */
5013           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5014           Back[x][y] = 0;
5015         }
5016
5017         continue;
5018       }
5019
5020       /* save walkable background elements while explosion on same tile */
5021       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5022           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5023         Back[x][y] = element;
5024
5025       /* ignite explodable elements reached by other explosion */
5026       if (element == EL_EXPLOSION)
5027         element = Store2[x][y];
5028
5029       if (AmoebaNr[x][y] &&
5030           (element == EL_AMOEBA_FULL ||
5031            element == EL_BD_AMOEBA ||
5032            element == EL_AMOEBA_GROWING))
5033       {
5034         AmoebaCnt[AmoebaNr[x][y]]--;
5035         AmoebaCnt2[AmoebaNr[x][y]]--;
5036       }
5037
5038       RemoveField(x, y);
5039
5040       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5041       {
5042         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5043
5044         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5045
5046         if (PLAYERINFO(ex, ey)->use_murphy)
5047           Store[x][y] = EL_EMPTY;
5048       }
5049
5050       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5051          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5052       else if (ELEM_IS_PLAYER(center_element))
5053         Store[x][y] = EL_EMPTY;
5054       else if (center_element == EL_YAMYAM)
5055         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5056       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5057         Store[x][y] = element_info[center_element].content.e[xx][yy];
5058 #if 1
5059       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5060          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5061          otherwise) -- FIX THIS !!! */
5062       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5063         Store[x][y] = element_info[element].content.e[1][1];
5064 #else
5065       else if (!CAN_EXPLODE(element))
5066         Store[x][y] = element_info[element].content.e[1][1];
5067 #endif
5068       else
5069         Store[x][y] = EL_EMPTY;
5070
5071       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5072           center_element == EL_AMOEBA_TO_DIAMOND)
5073         Store2[x][y] = element;
5074
5075       Feld[x][y] = EL_EXPLOSION;
5076       GfxElement[x][y] = artwork_element;
5077
5078       ExplodePhase[x][y] = 1;
5079       ExplodeDelay[x][y] = last_phase;
5080
5081       Stop[x][y] = TRUE;
5082     }
5083
5084     if (center_element == EL_YAMYAM)
5085       game.yamyam_content_nr =
5086         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5087
5088     return;
5089   }
5090
5091   if (Stop[ex][ey])
5092     return;
5093
5094   x = ex;
5095   y = ey;
5096
5097   if (phase == 1)
5098     GfxFrame[x][y] = 0;         /* restart explosion animation */
5099
5100   last_phase = ExplodeDelay[x][y];
5101
5102   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5103
5104 #ifdef DEBUG
5105
5106   /* activate this even in non-DEBUG version until cause for crash in
5107      getGraphicAnimationFrame() (see below) is found and eliminated */
5108
5109 #endif
5110 #if 1
5111
5112 #if 1
5113   /* this can happen if the player leaves an explosion just in time */
5114   if (GfxElement[x][y] == EL_UNDEFINED)
5115     GfxElement[x][y] = EL_EMPTY;
5116 #else
5117   if (GfxElement[x][y] == EL_UNDEFINED)
5118   {
5119     printf("\n\n");
5120     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5121     printf("Explode(): This should never happen!\n");
5122     printf("\n\n");
5123
5124     GfxElement[x][y] = EL_EMPTY;
5125   }
5126 #endif
5127
5128 #endif
5129
5130   border_element = Store2[x][y];
5131   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5132     border_element = StorePlayer[x][y];
5133
5134   if (phase == element_info[border_element].ignition_delay ||
5135       phase == last_phase)
5136   {
5137     boolean border_explosion = FALSE;
5138
5139     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5140         !PLAYER_EXPLOSION_PROTECTED(x, y))
5141     {
5142       KillPlayerUnlessExplosionProtected(x, y);
5143       border_explosion = TRUE;
5144     }
5145     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5146     {
5147       Feld[x][y] = Store2[x][y];
5148       Store2[x][y] = 0;
5149       Bang(x, y);
5150       border_explosion = TRUE;
5151     }
5152     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5153     {
5154       AmoebeUmwandeln(x, y);
5155       Store2[x][y] = 0;
5156       border_explosion = TRUE;
5157     }
5158
5159     /* if an element just explodes due to another explosion (chain-reaction),
5160        do not immediately end the new explosion when it was the last frame of
5161        the explosion (as it would be done in the following "if"-statement!) */
5162     if (border_explosion && phase == last_phase)
5163       return;
5164   }
5165
5166   if (phase == last_phase)
5167   {
5168     int element;
5169
5170     element = Feld[x][y] = Store[x][y];
5171     Store[x][y] = Store2[x][y] = 0;
5172     GfxElement[x][y] = EL_UNDEFINED;
5173
5174     /* player can escape from explosions and might therefore be still alive */
5175     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5176         element <= EL_PLAYER_IS_EXPLODING_4)
5177     {
5178       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5179       int explosion_element = EL_PLAYER_1 + player_nr;
5180       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5181       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5182
5183       if (level.use_explosion_element[player_nr])
5184         explosion_element = level.explosion_element[player_nr];
5185
5186       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5187                     element_info[explosion_element].content.e[xx][yy]);
5188     }
5189
5190     /* restore probably existing indestructible background element */
5191     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5192       element = Feld[x][y] = Back[x][y];
5193     Back[x][y] = 0;
5194
5195     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5196     GfxDir[x][y] = MV_NONE;
5197     ChangeDelay[x][y] = 0;
5198     ChangePage[x][y] = -1;
5199
5200 #if USE_NEW_CUSTOM_VALUE
5201     CustomValue[x][y] = 0;
5202 #endif
5203
5204     InitField_WithBug2(x, y, FALSE);
5205
5206     DrawLevelField(x, y);
5207
5208     TestIfElementTouchesCustomElement(x, y);
5209
5210     if (GFX_CRUMBLED(element))
5211       DrawLevelFieldCrumbledSandNeighbours(x, y);
5212
5213     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5214       StorePlayer[x][y] = 0;
5215
5216     if (ELEM_IS_PLAYER(element))
5217       RelocatePlayer(x, y, element);
5218   }
5219   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5220   {
5221     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5222     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5223
5224     if (phase == delay)
5225       DrawLevelFieldCrumbledSand(x, y);
5226
5227     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5228     {
5229       DrawLevelElement(x, y, Back[x][y]);
5230       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5231     }
5232     else if (IS_WALKABLE_UNDER(Back[x][y]))
5233     {
5234       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5235       DrawLevelElementThruMask(x, y, Back[x][y]);
5236     }
5237     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5238       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5239   }
5240 }
5241
5242 void DynaExplode(int ex, int ey)
5243 {
5244   int i, j;
5245   int dynabomb_element = Feld[ex][ey];
5246   int dynabomb_size = 1;
5247   boolean dynabomb_xl = FALSE;
5248   struct PlayerInfo *player;
5249   static int xy[4][2] =
5250   {
5251     { 0, -1 },
5252     { -1, 0 },
5253     { +1, 0 },
5254     { 0, +1 }
5255   };
5256
5257   if (IS_ACTIVE_BOMB(dynabomb_element))
5258   {
5259     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5260     dynabomb_size = player->dynabomb_size;
5261     dynabomb_xl = player->dynabomb_xl;
5262     player->dynabombs_left++;
5263   }
5264
5265   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5266
5267   for (i = 0; i < NUM_DIRECTIONS; i++)
5268   {
5269     for (j = 1; j <= dynabomb_size; j++)
5270     {
5271       int x = ex + j * xy[i][0];
5272       int y = ey + j * xy[i][1];
5273       int element;
5274
5275       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5276         break;
5277
5278       element = Feld[x][y];
5279
5280       /* do not restart explosions of fields with active bombs */
5281       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5282         continue;
5283
5284       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5285
5286       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5287           !IS_DIGGABLE(element) && !dynabomb_xl)
5288         break;
5289     }
5290   }
5291 }
5292
5293 void Bang(int x, int y)
5294 {
5295   int element = MovingOrBlocked2Element(x, y);
5296   int explosion_type = EX_TYPE_NORMAL;
5297
5298   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5299   {
5300     struct PlayerInfo *player = PLAYERINFO(x, y);
5301
5302     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5303                             player->element_nr);
5304
5305     if (level.use_explosion_element[player->index_nr])
5306     {
5307       int explosion_element = level.explosion_element[player->index_nr];
5308
5309       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5310         explosion_type = EX_TYPE_CROSS;
5311       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5312         explosion_type = EX_TYPE_CENTER;
5313     }
5314   }
5315
5316   switch (element)
5317   {
5318     case EL_BUG:
5319     case EL_SPACESHIP:
5320     case EL_BD_BUTTERFLY:
5321     case EL_BD_FIREFLY:
5322     case EL_YAMYAM:
5323     case EL_DARK_YAMYAM:
5324     case EL_ROBOT:
5325     case EL_PACMAN:
5326     case EL_MOLE:
5327       RaiseScoreElement(element);
5328       break;
5329
5330     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5331     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5332     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5333     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5334     case EL_DYNABOMB_INCREASE_NUMBER:
5335     case EL_DYNABOMB_INCREASE_SIZE:
5336     case EL_DYNABOMB_INCREASE_POWER:
5337       explosion_type = EX_TYPE_DYNA;
5338       break;
5339
5340     case EL_DC_LANDMINE:
5341 #if 0
5342     case EL_EM_EXIT_OPEN:
5343     case EL_EM_STEEL_EXIT_OPEN:
5344 #endif
5345       explosion_type = EX_TYPE_CENTER;
5346       break;
5347
5348     case EL_PENGUIN:
5349     case EL_LAMP:
5350     case EL_LAMP_ACTIVE:
5351     case EL_AMOEBA_TO_DIAMOND:
5352       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5353         explosion_type = EX_TYPE_CENTER;
5354       break;
5355
5356     default:
5357       if (element_info[element].explosion_type == EXPLODES_CROSS)
5358         explosion_type = EX_TYPE_CROSS;
5359       else if (element_info[element].explosion_type == EXPLODES_1X1)
5360         explosion_type = EX_TYPE_CENTER;
5361       break;
5362   }
5363
5364   if (explosion_type == EX_TYPE_DYNA)
5365     DynaExplode(x, y);
5366   else
5367     Explode(x, y, EX_PHASE_START, explosion_type);
5368
5369   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5370 }
5371
5372 void SplashAcid(int x, int y)
5373 {
5374   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5375       (!IN_LEV_FIELD(x - 1, y - 2) ||
5376        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5377     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5378
5379   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5380       (!IN_LEV_FIELD(x + 1, y - 2) ||
5381        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5382     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5383
5384   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5385 }
5386
5387 static void InitBeltMovement()
5388 {
5389   static int belt_base_element[4] =
5390   {
5391     EL_CONVEYOR_BELT_1_LEFT,
5392     EL_CONVEYOR_BELT_2_LEFT,
5393     EL_CONVEYOR_BELT_3_LEFT,
5394     EL_CONVEYOR_BELT_4_LEFT
5395   };
5396   static int belt_base_active_element[4] =
5397   {
5398     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5399     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5400     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5401     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5402   };
5403
5404   int x, y, i, j;
5405
5406   /* set frame order for belt animation graphic according to belt direction */
5407   for (i = 0; i < NUM_BELTS; i++)
5408   {
5409     int belt_nr = i;
5410
5411     for (j = 0; j < NUM_BELT_PARTS; j++)
5412     {
5413       int element = belt_base_active_element[belt_nr] + j;
5414       int graphic = el2img(element);
5415
5416       if (game.belt_dir[i] == MV_LEFT)
5417         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5418       else
5419         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5420     }
5421   }
5422
5423   SCAN_PLAYFIELD(x, y)
5424   {
5425     int element = Feld[x][y];
5426
5427     for (i = 0; i < NUM_BELTS; i++)
5428     {
5429       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5430       {
5431         int e_belt_nr = getBeltNrFromBeltElement(element);
5432         int belt_nr = i;
5433
5434         if (e_belt_nr == belt_nr)
5435         {
5436           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5437
5438           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5439         }
5440       }
5441     }
5442   }
5443 }
5444
5445 static void ToggleBeltSwitch(int x, int y)
5446 {
5447   static int belt_base_element[4] =
5448   {
5449     EL_CONVEYOR_BELT_1_LEFT,
5450     EL_CONVEYOR_BELT_2_LEFT,
5451     EL_CONVEYOR_BELT_3_LEFT,
5452     EL_CONVEYOR_BELT_4_LEFT
5453   };
5454   static int belt_base_active_element[4] =
5455   {
5456     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5457     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5458     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5459     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5460   };
5461   static int belt_base_switch_element[4] =
5462   {
5463     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5464     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5465     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5466     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5467   };
5468   static int belt_move_dir[4] =
5469   {
5470     MV_LEFT,
5471     MV_NONE,
5472     MV_RIGHT,
5473     MV_NONE,
5474   };
5475
5476   int element = Feld[x][y];
5477   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5478   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5479   int belt_dir = belt_move_dir[belt_dir_nr];
5480   int xx, yy, i;
5481
5482   if (!IS_BELT_SWITCH(element))
5483     return;
5484
5485   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5486   game.belt_dir[belt_nr] = belt_dir;
5487
5488   if (belt_dir_nr == 3)
5489     belt_dir_nr = 1;
5490
5491   /* set frame order for belt animation graphic according to belt direction */
5492   for (i = 0; i < NUM_BELT_PARTS; i++)
5493   {
5494     int element = belt_base_active_element[belt_nr] + i;
5495     int graphic = el2img(element);
5496
5497     if (belt_dir == MV_LEFT)
5498       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5499     else
5500       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5501   }
5502
5503   SCAN_PLAYFIELD(xx, yy)
5504   {
5505     int element = Feld[xx][yy];
5506
5507     if (IS_BELT_SWITCH(element))
5508     {
5509       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5510
5511       if (e_belt_nr == belt_nr)
5512       {
5513         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5514         DrawLevelField(xx, yy);
5515       }
5516     }
5517     else if (IS_BELT(element) && belt_dir != MV_NONE)
5518     {
5519       int e_belt_nr = getBeltNrFromBeltElement(element);
5520
5521       if (e_belt_nr == belt_nr)
5522       {
5523         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5524
5525         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5526         DrawLevelField(xx, yy);
5527       }
5528     }
5529     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5530     {
5531       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5532
5533       if (e_belt_nr == belt_nr)
5534       {
5535         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5536
5537         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5538         DrawLevelField(xx, yy);
5539       }
5540     }
5541   }
5542 }
5543
5544 static void ToggleSwitchgateSwitch(int x, int y)
5545 {
5546   int xx, yy;
5547
5548   game.switchgate_pos = !game.switchgate_pos;
5549
5550   SCAN_PLAYFIELD(xx, yy)
5551   {
5552     int element = Feld[xx][yy];
5553
5554 #if !USE_BOTH_SWITCHGATE_SWITCHES
5555     if (element == EL_SWITCHGATE_SWITCH_UP ||
5556         element == EL_SWITCHGATE_SWITCH_DOWN)
5557     {
5558       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5559       DrawLevelField(xx, yy);
5560     }
5561     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5562              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5563     {
5564       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5565       DrawLevelField(xx, yy);
5566     }
5567 #else
5568     if (element == EL_SWITCHGATE_SWITCH_UP)
5569     {
5570       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5571       DrawLevelField(xx, yy);
5572     }
5573     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5574     {
5575       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5576       DrawLevelField(xx, yy);
5577     }
5578     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5579     {
5580       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5581       DrawLevelField(xx, yy);
5582     }
5583     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5584     {
5585       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5586       DrawLevelField(xx, yy);
5587     }
5588 #endif
5589     else if (element == EL_SWITCHGATE_OPEN ||
5590              element == EL_SWITCHGATE_OPENING)
5591     {
5592       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5593
5594       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5595     }
5596     else if (element == EL_SWITCHGATE_CLOSED ||
5597              element == EL_SWITCHGATE_CLOSING)
5598     {
5599       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5600
5601       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5602     }
5603   }
5604 }
5605
5606 static int getInvisibleActiveFromInvisibleElement(int element)
5607 {
5608   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5609           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5610           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5611           element);
5612 }
5613
5614 static int getInvisibleFromInvisibleActiveElement(int element)
5615 {
5616   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5617           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5618           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5619           element);
5620 }
5621
5622 static void RedrawAllLightSwitchesAndInvisibleElements()
5623 {
5624   int x, y;
5625
5626   SCAN_PLAYFIELD(x, y)
5627   {
5628     int element = Feld[x][y];
5629
5630     if (element == EL_LIGHT_SWITCH &&
5631         game.light_time_left > 0)
5632     {
5633       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5634       DrawLevelField(x, y);
5635     }
5636     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5637              game.light_time_left == 0)
5638     {
5639       Feld[x][y] = EL_LIGHT_SWITCH;
5640       DrawLevelField(x, y);
5641     }
5642     else if (element == EL_EMC_DRIPPER &&
5643              game.light_time_left > 0)
5644     {
5645       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5646       DrawLevelField(x, y);
5647     }
5648     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5649              game.light_time_left == 0)
5650     {
5651       Feld[x][y] = EL_EMC_DRIPPER;
5652       DrawLevelField(x, y);
5653     }
5654     else if (element == EL_INVISIBLE_STEELWALL ||
5655              element == EL_INVISIBLE_WALL ||
5656              element == EL_INVISIBLE_SAND)
5657     {
5658       if (game.light_time_left > 0)
5659         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5660
5661       DrawLevelField(x, y);
5662
5663       /* uncrumble neighbour fields, if needed */
5664       if (element == EL_INVISIBLE_SAND)
5665         DrawLevelFieldCrumbledSandNeighbours(x, y);
5666     }
5667     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5668              element == EL_INVISIBLE_WALL_ACTIVE ||
5669              element == EL_INVISIBLE_SAND_ACTIVE)
5670     {
5671       if (game.light_time_left == 0)
5672         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5673
5674       DrawLevelField(x, y);
5675
5676       /* re-crumble neighbour fields, if needed */
5677       if (element == EL_INVISIBLE_SAND)
5678         DrawLevelFieldCrumbledSandNeighbours(x, y);
5679     }
5680   }
5681 }
5682
5683 static void RedrawAllInvisibleElementsForLenses()
5684 {
5685   int x, y;
5686
5687   SCAN_PLAYFIELD(x, y)
5688   {
5689     int element = Feld[x][y];
5690
5691     if (element == EL_EMC_DRIPPER &&
5692         game.lenses_time_left > 0)
5693     {
5694       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5695       DrawLevelField(x, y);
5696     }
5697     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5698              game.lenses_time_left == 0)
5699     {
5700       Feld[x][y] = EL_EMC_DRIPPER;
5701       DrawLevelField(x, y);
5702     }
5703     else if (element == EL_INVISIBLE_STEELWALL ||
5704              element == EL_INVISIBLE_WALL ||
5705              element == EL_INVISIBLE_SAND)
5706     {
5707       if (game.lenses_time_left > 0)
5708         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5709
5710       DrawLevelField(x, y);
5711
5712       /* uncrumble neighbour fields, if needed */
5713       if (element == EL_INVISIBLE_SAND)
5714         DrawLevelFieldCrumbledSandNeighbours(x, y);
5715     }
5716     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5717              element == EL_INVISIBLE_WALL_ACTIVE ||
5718              element == EL_INVISIBLE_SAND_ACTIVE)
5719     {
5720       if (game.lenses_time_left == 0)
5721         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5722
5723       DrawLevelField(x, y);
5724
5725       /* re-crumble neighbour fields, if needed */
5726       if (element == EL_INVISIBLE_SAND)
5727         DrawLevelFieldCrumbledSandNeighbours(x, y);
5728     }
5729   }
5730 }
5731
5732 static void RedrawAllInvisibleElementsForMagnifier()
5733 {
5734   int x, y;
5735
5736   SCAN_PLAYFIELD(x, y)
5737   {
5738     int element = Feld[x][y];
5739
5740     if (element == EL_EMC_FAKE_GRASS &&
5741         game.magnify_time_left > 0)
5742     {
5743       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5744       DrawLevelField(x, y);
5745     }
5746     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5747              game.magnify_time_left == 0)
5748     {
5749       Feld[x][y] = EL_EMC_FAKE_GRASS;
5750       DrawLevelField(x, y);
5751     }
5752     else if (IS_GATE_GRAY(element) &&
5753              game.magnify_time_left > 0)
5754     {
5755       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5756                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5757                     IS_EM_GATE_GRAY(element) ?
5758                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5759                     IS_EMC_GATE_GRAY(element) ?
5760                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5761                     element);
5762       DrawLevelField(x, y);
5763     }
5764     else if (IS_GATE_GRAY_ACTIVE(element) &&
5765              game.magnify_time_left == 0)
5766     {
5767       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5768                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5769                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5770                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5771                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5772                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5773                     element);
5774       DrawLevelField(x, y);
5775     }
5776   }
5777 }
5778
5779 static void ToggleLightSwitch(int x, int y)
5780 {
5781   int element = Feld[x][y];
5782
5783   game.light_time_left =
5784     (element == EL_LIGHT_SWITCH ?
5785      level.time_light * FRAMES_PER_SECOND : 0);
5786
5787   RedrawAllLightSwitchesAndInvisibleElements();
5788 }
5789
5790 static void ActivateTimegateSwitch(int x, int y)
5791 {
5792   int xx, yy;
5793
5794   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5795
5796   SCAN_PLAYFIELD(xx, yy)
5797   {
5798     int element = Feld[xx][yy];
5799
5800     if (element == EL_TIMEGATE_CLOSED ||
5801         element == EL_TIMEGATE_CLOSING)
5802     {
5803       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5804       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5805     }
5806
5807     /*
5808     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5809     {
5810       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5811       DrawLevelField(xx, yy);
5812     }
5813     */
5814
5815   }
5816
5817 #if 1
5818   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5819                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5820 #else
5821   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5822 #endif
5823 }
5824
5825 void Impact(int x, int y)
5826 {
5827   boolean last_line = (y == lev_fieldy - 1);
5828   boolean object_hit = FALSE;
5829   boolean impact = (last_line || object_hit);
5830   int element = Feld[x][y];
5831   int smashed = EL_STEELWALL;
5832
5833   if (!last_line)       /* check if element below was hit */
5834   {
5835     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5836       return;
5837
5838     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5839                                          MovDir[x][y + 1] != MV_DOWN ||
5840                                          MovPos[x][y + 1] <= TILEY / 2));
5841
5842     /* do not smash moving elements that left the smashed field in time */
5843     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5844         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5845       object_hit = FALSE;
5846
5847 #if USE_QUICKSAND_IMPACT_BUGFIX
5848     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5849     {
5850       RemoveMovingField(x, y + 1);
5851       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5852       Feld[x][y + 2] = EL_ROCK;
5853       DrawLevelField(x, y + 2);
5854
5855       object_hit = TRUE;
5856     }
5857
5858     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5859     {
5860       RemoveMovingField(x, y + 1);
5861       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5862       Feld[x][y + 2] = EL_ROCK;
5863       DrawLevelField(x, y + 2);
5864
5865       object_hit = TRUE;
5866     }
5867 #endif
5868
5869     if (object_hit)
5870       smashed = MovingOrBlocked2Element(x, y + 1);
5871
5872     impact = (last_line || object_hit);
5873   }
5874
5875   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5876   {
5877     SplashAcid(x, y + 1);
5878     return;
5879   }
5880
5881   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5882   /* only reset graphic animation if graphic really changes after impact */
5883   if (impact &&
5884       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5885   {
5886     ResetGfxAnimation(x, y);
5887     DrawLevelField(x, y);
5888   }
5889
5890   if (impact && CAN_EXPLODE_IMPACT(element))
5891   {
5892     Bang(x, y);
5893     return;
5894   }
5895   else if (impact && element == EL_PEARL &&
5896            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5897   {
5898     ResetGfxAnimation(x, y);
5899
5900     Feld[x][y] = EL_PEARL_BREAKING;
5901     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5902     return;
5903   }
5904   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5905   {
5906     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5907
5908     return;
5909   }
5910
5911   if (impact && element == EL_AMOEBA_DROP)
5912   {
5913     if (object_hit && IS_PLAYER(x, y + 1))
5914       KillPlayerUnlessEnemyProtected(x, y + 1);
5915     else if (object_hit && smashed == EL_PENGUIN)
5916       Bang(x, y + 1);
5917     else
5918     {
5919       Feld[x][y] = EL_AMOEBA_GROWING;
5920       Store[x][y] = EL_AMOEBA_WET;
5921
5922       ResetRandomAnimationValue(x, y);
5923     }
5924     return;
5925   }
5926
5927   if (object_hit)               /* check which object was hit */
5928   {
5929     if ((CAN_PASS_MAGIC_WALL(element) && 
5930          (smashed == EL_MAGIC_WALL ||
5931           smashed == EL_BD_MAGIC_WALL)) ||
5932         (CAN_PASS_DC_MAGIC_WALL(element) &&
5933          smashed == EL_DC_MAGIC_WALL))
5934     {
5935       int xx, yy;
5936       int activated_magic_wall =
5937         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5938          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5939          EL_DC_MAGIC_WALL_ACTIVE);
5940
5941       /* activate magic wall / mill */
5942       SCAN_PLAYFIELD(xx, yy)
5943       {
5944         if (Feld[xx][yy] == smashed)
5945           Feld[xx][yy] = activated_magic_wall;
5946       }
5947
5948       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5949       game.magic_wall_active = TRUE;
5950
5951       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5952                             SND_MAGIC_WALL_ACTIVATING :
5953                             smashed == EL_BD_MAGIC_WALL ?
5954                             SND_BD_MAGIC_WALL_ACTIVATING :
5955                             SND_DC_MAGIC_WALL_ACTIVATING));
5956     }
5957
5958     if (IS_PLAYER(x, y + 1))
5959     {
5960       if (CAN_SMASH_PLAYER(element))
5961       {
5962         KillPlayerUnlessEnemyProtected(x, y + 1);
5963         return;
5964       }
5965     }
5966     else if (smashed == EL_PENGUIN)
5967     {
5968       if (CAN_SMASH_PLAYER(element))
5969       {
5970         Bang(x, y + 1);
5971         return;
5972       }
5973     }
5974     else if (element == EL_BD_DIAMOND)
5975     {
5976       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5977       {
5978         Bang(x, y + 1);
5979         return;
5980       }
5981     }
5982     else if (((element == EL_SP_INFOTRON ||
5983                element == EL_SP_ZONK) &&
5984               (smashed == EL_SP_SNIKSNAK ||
5985                smashed == EL_SP_ELECTRON ||
5986                smashed == EL_SP_DISK_ORANGE)) ||
5987              (element == EL_SP_INFOTRON &&
5988               smashed == EL_SP_DISK_YELLOW))
5989     {
5990       Bang(x, y + 1);
5991       return;
5992     }
5993     else if (CAN_SMASH_EVERYTHING(element))
5994     {
5995       if (IS_CLASSIC_ENEMY(smashed) ||
5996           CAN_EXPLODE_SMASHED(smashed))
5997       {
5998         Bang(x, y + 1);
5999         return;
6000       }
6001       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6002       {
6003         if (smashed == EL_LAMP ||
6004             smashed == EL_LAMP_ACTIVE)
6005         {
6006           Bang(x, y + 1);
6007           return;
6008         }
6009         else if (smashed == EL_NUT)
6010         {
6011           Feld[x][y + 1] = EL_NUT_BREAKING;
6012           PlayLevelSound(x, y, SND_NUT_BREAKING);
6013           RaiseScoreElement(EL_NUT);
6014           return;
6015         }
6016         else if (smashed == EL_PEARL)
6017         {
6018           ResetGfxAnimation(x, y);
6019
6020           Feld[x][y + 1] = EL_PEARL_BREAKING;
6021           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6022           return;
6023         }
6024         else if (smashed == EL_DIAMOND)
6025         {
6026           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6027           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6028           return;
6029         }
6030         else if (IS_BELT_SWITCH(smashed))
6031         {
6032           ToggleBeltSwitch(x, y + 1);
6033         }
6034         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6035                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6036                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6037                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6038         {
6039           ToggleSwitchgateSwitch(x, y + 1);
6040         }
6041         else if (smashed == EL_LIGHT_SWITCH ||
6042                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6043         {
6044           ToggleLightSwitch(x, y + 1);
6045         }
6046         else
6047         {
6048 #if 0
6049           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6050 #endif
6051
6052           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6053
6054           CheckElementChangeBySide(x, y + 1, smashed, element,
6055                                    CE_SWITCHED, CH_SIDE_TOP);
6056           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6057                                             CH_SIDE_TOP);
6058         }
6059       }
6060       else
6061       {
6062         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6063       }
6064     }
6065   }
6066
6067   /* play sound of magic wall / mill */
6068   if (!last_line &&
6069       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6070        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6071        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6072   {
6073     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6074       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6075     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6076       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6077     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6078       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6079
6080     return;
6081   }
6082
6083   /* play sound of object that hits the ground */
6084   if (last_line || object_hit)
6085     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6086 }
6087
6088 inline static void TurnRoundExt(int x, int y)
6089 {
6090   static struct
6091   {
6092     int dx, dy;
6093   } move_xy[] =
6094   {
6095     {  0,  0 },
6096     { -1,  0 },
6097     { +1,  0 },
6098     {  0,  0 },
6099     {  0, -1 },
6100     {  0,  0 }, { 0, 0 }, { 0, 0 },
6101     {  0, +1 }
6102   };
6103   static struct
6104   {
6105     int left, right, back;
6106   } turn[] =
6107   {
6108     { 0,        0,              0        },
6109     { MV_DOWN,  MV_UP,          MV_RIGHT },
6110     { MV_UP,    MV_DOWN,        MV_LEFT  },
6111     { 0,        0,              0        },
6112     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6113     { 0,        0,              0        },
6114     { 0,        0,              0        },
6115     { 0,        0,              0        },
6116     { MV_RIGHT, MV_LEFT,        MV_UP    }
6117   };
6118
6119   int element = Feld[x][y];
6120   int move_pattern = element_info[element].move_pattern;
6121
6122   int old_move_dir = MovDir[x][y];
6123   int left_dir  = turn[old_move_dir].left;
6124   int right_dir = turn[old_move_dir].right;
6125   int back_dir  = turn[old_move_dir].back;
6126
6127   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6128   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6129   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6130   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6131
6132   int left_x  = x + left_dx,  left_y  = y + left_dy;
6133   int right_x = x + right_dx, right_y = y + right_dy;
6134   int move_x  = x + move_dx,  move_y  = y + move_dy;
6135
6136   int xx, yy;
6137
6138   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6139   {
6140     TestIfBadThingTouchesOtherBadThing(x, y);
6141
6142     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6143       MovDir[x][y] = right_dir;
6144     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6145       MovDir[x][y] = left_dir;
6146
6147     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6148       MovDelay[x][y] = 9;
6149     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6150       MovDelay[x][y] = 1;
6151   }
6152   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6153   {
6154     TestIfBadThingTouchesOtherBadThing(x, y);
6155
6156     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6157       MovDir[x][y] = left_dir;
6158     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6159       MovDir[x][y] = right_dir;
6160
6161     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6162       MovDelay[x][y] = 9;
6163     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6164       MovDelay[x][y] = 1;
6165   }
6166   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6167   {
6168     TestIfBadThingTouchesOtherBadThing(x, y);
6169
6170     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6171       MovDir[x][y] = left_dir;
6172     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6173       MovDir[x][y] = right_dir;
6174
6175     if (MovDir[x][y] != old_move_dir)
6176       MovDelay[x][y] = 9;
6177   }
6178   else if (element == EL_YAMYAM)
6179   {
6180     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6181     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6182
6183     if (can_turn_left && can_turn_right)
6184       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6185     else if (can_turn_left)
6186       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6187     else if (can_turn_right)
6188       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6189     else
6190       MovDir[x][y] = back_dir;
6191
6192     MovDelay[x][y] = 16 + 16 * RND(3);
6193   }
6194   else if (element == EL_DARK_YAMYAM)
6195   {
6196     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6197                                                          left_x, left_y);
6198     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6199                                                          right_x, right_y);
6200
6201     if (can_turn_left && can_turn_right)
6202       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6203     else if (can_turn_left)
6204       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6205     else if (can_turn_right)
6206       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6207     else
6208       MovDir[x][y] = back_dir;
6209
6210     MovDelay[x][y] = 16 + 16 * RND(3);
6211   }
6212   else if (element == EL_PACMAN)
6213   {
6214     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6215     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6216
6217     if (can_turn_left && can_turn_right)
6218       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6219     else if (can_turn_left)
6220       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6221     else if (can_turn_right)
6222       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6223     else
6224       MovDir[x][y] = back_dir;
6225
6226     MovDelay[x][y] = 6 + RND(40);
6227   }
6228   else if (element == EL_PIG)
6229   {
6230     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6231     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6232     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6233     boolean should_turn_left, should_turn_right, should_move_on;
6234     int rnd_value = 24;
6235     int rnd = RND(rnd_value);
6236
6237     should_turn_left = (can_turn_left &&
6238                         (!can_move_on ||
6239                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6240                                                    y + back_dy + left_dy)));
6241     should_turn_right = (can_turn_right &&
6242                          (!can_move_on ||
6243                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6244                                                     y + back_dy + right_dy)));
6245     should_move_on = (can_move_on &&
6246                       (!can_turn_left ||
6247                        !can_turn_right ||
6248                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6249                                                  y + move_dy + left_dy) ||
6250                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6251                                                  y + move_dy + right_dy)));
6252
6253     if (should_turn_left || should_turn_right || should_move_on)
6254     {
6255       if (should_turn_left && should_turn_right && should_move_on)
6256         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6257                         rnd < 2 * rnd_value / 3 ? right_dir :
6258                         old_move_dir);
6259       else if (should_turn_left && should_turn_right)
6260         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6261       else if (should_turn_left && should_move_on)
6262         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6263       else if (should_turn_right && should_move_on)
6264         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6265       else if (should_turn_left)
6266         MovDir[x][y] = left_dir;
6267       else if (should_turn_right)
6268         MovDir[x][y] = right_dir;
6269       else if (should_move_on)
6270         MovDir[x][y] = old_move_dir;
6271     }
6272     else if (can_move_on && rnd > rnd_value / 8)
6273       MovDir[x][y] = old_move_dir;
6274     else if (can_turn_left && can_turn_right)
6275       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6276     else if (can_turn_left && rnd > rnd_value / 8)
6277       MovDir[x][y] = left_dir;
6278     else if (can_turn_right && rnd > rnd_value/8)
6279       MovDir[x][y] = right_dir;
6280     else
6281       MovDir[x][y] = back_dir;
6282
6283     xx = x + move_xy[MovDir[x][y]].dx;
6284     yy = y + move_xy[MovDir[x][y]].dy;
6285
6286     if (!IN_LEV_FIELD(xx, yy) ||
6287         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6288       MovDir[x][y] = old_move_dir;
6289
6290     MovDelay[x][y] = 0;
6291   }
6292   else if (element == EL_DRAGON)
6293   {
6294     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6295     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6296     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6297     int rnd_value = 24;
6298     int rnd = RND(rnd_value);
6299
6300     if (can_move_on && rnd > rnd_value / 8)
6301       MovDir[x][y] = old_move_dir;
6302     else if (can_turn_left && can_turn_right)
6303       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6304     else if (can_turn_left && rnd > rnd_value / 8)
6305       MovDir[x][y] = left_dir;
6306     else if (can_turn_right && rnd > rnd_value / 8)
6307       MovDir[x][y] = right_dir;
6308     else
6309       MovDir[x][y] = back_dir;
6310
6311     xx = x + move_xy[MovDir[x][y]].dx;
6312     yy = y + move_xy[MovDir[x][y]].dy;
6313
6314     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6315       MovDir[x][y] = old_move_dir;
6316
6317     MovDelay[x][y] = 0;
6318   }
6319   else if (element == EL_MOLE)
6320   {
6321     boolean can_move_on =
6322       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6323                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6324                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6325     if (!can_move_on)
6326     {
6327       boolean can_turn_left =
6328         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6329                               IS_AMOEBOID(Feld[left_x][left_y])));
6330
6331       boolean can_turn_right =
6332         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6333                               IS_AMOEBOID(Feld[right_x][right_y])));
6334
6335       if (can_turn_left && can_turn_right)
6336         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6337       else if (can_turn_left)
6338         MovDir[x][y] = left_dir;
6339       else
6340         MovDir[x][y] = right_dir;
6341     }
6342
6343     if (MovDir[x][y] != old_move_dir)
6344       MovDelay[x][y] = 9;
6345   }
6346   else if (element == EL_BALLOON)
6347   {
6348     MovDir[x][y] = game.wind_direction;
6349     MovDelay[x][y] = 0;
6350   }
6351   else if (element == EL_SPRING)
6352   {
6353 #if USE_NEW_SPRING_BUMPER
6354     if (MovDir[x][y] & MV_HORIZONTAL)
6355     {
6356       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6357           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6358       {
6359         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6360         ResetGfxAnimation(move_x, move_y);
6361         DrawLevelField(move_x, move_y);
6362
6363         MovDir[x][y] = back_dir;
6364       }
6365       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6366                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6367         MovDir[x][y] = MV_NONE;
6368     }
6369 #else
6370     if (MovDir[x][y] & MV_HORIZONTAL &&
6371         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6372          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6373       MovDir[x][y] = MV_NONE;
6374 #endif
6375
6376     MovDelay[x][y] = 0;
6377   }
6378   else if (element == EL_ROBOT ||
6379            element == EL_SATELLITE ||
6380            element == EL_PENGUIN ||
6381            element == EL_EMC_ANDROID)
6382   {
6383     int attr_x = -1, attr_y = -1;
6384
6385     if (AllPlayersGone)
6386     {
6387       attr_x = ExitX;
6388       attr_y = ExitY;
6389     }
6390     else
6391     {
6392       int i;
6393
6394       for (i = 0; i < MAX_PLAYERS; i++)
6395       {
6396         struct PlayerInfo *player = &stored_player[i];
6397         int jx = player->jx, jy = player->jy;
6398
6399         if (!player->active)
6400           continue;
6401
6402         if (attr_x == -1 ||
6403             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6404         {
6405           attr_x = jx;
6406           attr_y = jy;
6407         }
6408       }
6409     }
6410
6411     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6412         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6413          game.engine_version < VERSION_IDENT(3,1,0,0)))
6414     {
6415       attr_x = ZX;
6416       attr_y = ZY;
6417     }
6418
6419     if (element == EL_PENGUIN)
6420     {
6421       int i;
6422       static int xy[4][2] =
6423       {
6424         { 0, -1 },
6425         { -1, 0 },
6426         { +1, 0 },
6427         { 0, +1 }
6428       };
6429
6430       for (i = 0; i < NUM_DIRECTIONS; i++)
6431       {
6432         int ex = x + xy[i][0];
6433         int ey = y + xy[i][1];
6434
6435         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6436                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6437                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6438                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6439         {
6440           attr_x = ex;
6441           attr_y = ey;
6442           break;
6443         }
6444       }
6445     }
6446
6447     MovDir[x][y] = MV_NONE;
6448     if (attr_x < x)
6449       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6450     else if (attr_x > x)
6451       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6452     if (attr_y < y)
6453       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6454     else if (attr_y > y)
6455       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6456
6457     if (element == EL_ROBOT)
6458     {
6459       int newx, newy;
6460
6461       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6462         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6463       Moving2Blocked(x, y, &newx, &newy);
6464
6465       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6466         MovDelay[x][y] = 8 + 8 * !RND(3);
6467       else
6468         MovDelay[x][y] = 16;
6469     }
6470     else if (element == EL_PENGUIN)
6471     {
6472       int newx, newy;
6473
6474       MovDelay[x][y] = 1;
6475
6476       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6477       {
6478         boolean first_horiz = RND(2);
6479         int new_move_dir = MovDir[x][y];
6480
6481         MovDir[x][y] =
6482           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6483         Moving2Blocked(x, y, &newx, &newy);
6484
6485         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6486           return;
6487
6488         MovDir[x][y] =
6489           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6490         Moving2Blocked(x, y, &newx, &newy);
6491
6492         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6493           return;
6494
6495         MovDir[x][y] = old_move_dir;
6496         return;
6497       }
6498     }
6499     else if (element == EL_SATELLITE)
6500     {
6501       int newx, newy;
6502
6503       MovDelay[x][y] = 1;
6504
6505       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6506       {
6507         boolean first_horiz = RND(2);
6508         int new_move_dir = MovDir[x][y];
6509
6510         MovDir[x][y] =
6511           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6512         Moving2Blocked(x, y, &newx, &newy);
6513
6514         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6515           return;
6516
6517         MovDir[x][y] =
6518           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6519         Moving2Blocked(x, y, &newx, &newy);
6520
6521         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6522           return;
6523
6524         MovDir[x][y] = old_move_dir;
6525         return;
6526       }
6527     }
6528     else if (element == EL_EMC_ANDROID)
6529     {
6530       static int check_pos[16] =
6531       {
6532         -1,             /*  0 => (invalid)          */
6533         7,              /*  1 => MV_LEFT            */
6534         3,              /*  2 => MV_RIGHT           */
6535         -1,             /*  3 => (invalid)          */
6536         1,              /*  4 =>            MV_UP   */
6537         0,              /*  5 => MV_LEFT  | MV_UP   */
6538         2,              /*  6 => MV_RIGHT | MV_UP   */
6539         -1,             /*  7 => (invalid)          */
6540         5,              /*  8 =>            MV_DOWN */
6541         6,              /*  9 => MV_LEFT  | MV_DOWN */
6542         4,              /* 10 => MV_RIGHT | MV_DOWN */
6543         -1,             /* 11 => (invalid)          */
6544         -1,             /* 12 => (invalid)          */
6545         -1,             /* 13 => (invalid)          */
6546         -1,             /* 14 => (invalid)          */
6547         -1,             /* 15 => (invalid)          */
6548       };
6549       static struct
6550       {
6551         int dx, dy;
6552         int dir;
6553       } check_xy[8] =
6554       {
6555         { -1, -1,       MV_LEFT  | MV_UP   },
6556         {  0, -1,                  MV_UP   },
6557         { +1, -1,       MV_RIGHT | MV_UP   },
6558         { +1,  0,       MV_RIGHT           },
6559         { +1, +1,       MV_RIGHT | MV_DOWN },
6560         {  0, +1,                  MV_DOWN },
6561         { -1, +1,       MV_LEFT  | MV_DOWN },
6562         { -1,  0,       MV_LEFT            },
6563       };
6564       int start_pos, check_order;
6565       boolean can_clone = FALSE;
6566       int i;
6567
6568       /* check if there is any free field around current position */
6569       for (i = 0; i < 8; i++)
6570       {
6571         int newx = x + check_xy[i].dx;
6572         int newy = y + check_xy[i].dy;
6573
6574         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6575         {
6576           can_clone = TRUE;
6577
6578           break;
6579         }
6580       }
6581
6582       if (can_clone)            /* randomly find an element to clone */
6583       {
6584         can_clone = FALSE;
6585
6586         start_pos = check_pos[RND(8)];
6587         check_order = (RND(2) ? -1 : +1);
6588
6589         for (i = 0; i < 8; i++)
6590         {
6591           int pos_raw = start_pos + i * check_order;
6592           int pos = (pos_raw + 8) % 8;
6593           int newx = x + check_xy[pos].dx;
6594           int newy = y + check_xy[pos].dy;
6595
6596           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6597           {
6598             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6599             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6600
6601             Store[x][y] = Feld[newx][newy];
6602
6603             can_clone = TRUE;
6604
6605             break;
6606           }
6607         }
6608       }
6609
6610       if (can_clone)            /* randomly find a direction to move */
6611       {
6612         can_clone = FALSE;
6613
6614         start_pos = check_pos[RND(8)];
6615         check_order = (RND(2) ? -1 : +1);
6616
6617         for (i = 0; i < 8; i++)
6618         {
6619           int pos_raw = start_pos + i * check_order;
6620           int pos = (pos_raw + 8) % 8;
6621           int newx = x + check_xy[pos].dx;
6622           int newy = y + check_xy[pos].dy;
6623           int new_move_dir = check_xy[pos].dir;
6624
6625           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6626           {
6627             MovDir[x][y] = new_move_dir;
6628             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6629
6630             can_clone = TRUE;
6631
6632             break;
6633           }
6634         }
6635       }
6636
6637       if (can_clone)            /* cloning and moving successful */
6638         return;
6639
6640       /* cannot clone -- try to move towards player */
6641
6642       start_pos = check_pos[MovDir[x][y] & 0x0f];
6643       check_order = (RND(2) ? -1 : +1);
6644
6645       for (i = 0; i < 3; i++)
6646       {
6647         /* first check start_pos, then previous/next or (next/previous) pos */
6648         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6649         int pos = (pos_raw + 8) % 8;
6650         int newx = x + check_xy[pos].dx;
6651         int newy = y + check_xy[pos].dy;
6652         int new_move_dir = check_xy[pos].dir;
6653
6654         if (IS_PLAYER(newx, newy))
6655           break;
6656
6657         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6658         {
6659           MovDir[x][y] = new_move_dir;
6660           MovDelay[x][y] = level.android_move_time * 8 + 1;
6661
6662           break;
6663         }
6664       }
6665     }
6666   }
6667   else if (move_pattern == MV_TURNING_LEFT ||
6668            move_pattern == MV_TURNING_RIGHT ||
6669            move_pattern == MV_TURNING_LEFT_RIGHT ||
6670            move_pattern == MV_TURNING_RIGHT_LEFT ||
6671            move_pattern == MV_TURNING_RANDOM ||
6672            move_pattern == MV_ALL_DIRECTIONS)
6673   {
6674     boolean can_turn_left =
6675       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6676     boolean can_turn_right =
6677       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6678
6679     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6680       return;
6681
6682     if (move_pattern == MV_TURNING_LEFT)
6683       MovDir[x][y] = left_dir;
6684     else if (move_pattern == MV_TURNING_RIGHT)
6685       MovDir[x][y] = right_dir;
6686     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6687       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6688     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6689       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6690     else if (move_pattern == MV_TURNING_RANDOM)
6691       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6692                       can_turn_right && !can_turn_left ? right_dir :
6693                       RND(2) ? left_dir : right_dir);
6694     else if (can_turn_left && can_turn_right)
6695       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6696     else if (can_turn_left)
6697       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6698     else if (can_turn_right)
6699       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6700     else
6701       MovDir[x][y] = back_dir;
6702
6703     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6704   }
6705   else if (move_pattern == MV_HORIZONTAL ||
6706            move_pattern == MV_VERTICAL)
6707   {
6708     if (move_pattern & old_move_dir)
6709       MovDir[x][y] = back_dir;
6710     else if (move_pattern == MV_HORIZONTAL)
6711       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6712     else if (move_pattern == MV_VERTICAL)
6713       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6714
6715     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6716   }
6717   else if (move_pattern & MV_ANY_DIRECTION)
6718   {
6719     MovDir[x][y] = move_pattern;
6720     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6721   }
6722   else if (move_pattern & MV_WIND_DIRECTION)
6723   {
6724     MovDir[x][y] = game.wind_direction;
6725     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6726   }
6727   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6728   {
6729     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6730       MovDir[x][y] = left_dir;
6731     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6732       MovDir[x][y] = right_dir;
6733
6734     if (MovDir[x][y] != old_move_dir)
6735       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6736   }
6737   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6738   {
6739     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6740       MovDir[x][y] = right_dir;
6741     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6742       MovDir[x][y] = left_dir;
6743
6744     if (MovDir[x][y] != old_move_dir)
6745       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6746   }
6747   else if (move_pattern == MV_TOWARDS_PLAYER ||
6748            move_pattern == MV_AWAY_FROM_PLAYER)
6749   {
6750     int attr_x = -1, attr_y = -1;
6751     int newx, newy;
6752     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6753
6754     if (AllPlayersGone)
6755     {
6756       attr_x = ExitX;
6757       attr_y = ExitY;
6758     }
6759     else
6760     {
6761       int i;
6762
6763       for (i = 0; i < MAX_PLAYERS; i++)
6764       {
6765         struct PlayerInfo *player = &stored_player[i];
6766         int jx = player->jx, jy = player->jy;
6767
6768         if (!player->active)
6769           continue;
6770
6771         if (attr_x == -1 ||
6772             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6773         {
6774           attr_x = jx;
6775           attr_y = jy;
6776         }
6777       }
6778     }
6779
6780     MovDir[x][y] = MV_NONE;
6781     if (attr_x < x)
6782       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6783     else if (attr_x > x)
6784       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6785     if (attr_y < y)
6786       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6787     else if (attr_y > y)
6788       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6789
6790     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6791
6792     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6793     {
6794       boolean first_horiz = RND(2);
6795       int new_move_dir = MovDir[x][y];
6796
6797       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6798       {
6799         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6800         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6801
6802         return;
6803       }
6804
6805       MovDir[x][y] =
6806         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6807       Moving2Blocked(x, y, &newx, &newy);
6808
6809       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6810         return;
6811
6812       MovDir[x][y] =
6813         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6814       Moving2Blocked(x, y, &newx, &newy);
6815
6816       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6817         return;
6818
6819       MovDir[x][y] = old_move_dir;
6820     }
6821   }
6822   else if (move_pattern == MV_WHEN_PUSHED ||
6823            move_pattern == MV_WHEN_DROPPED)
6824   {
6825     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6826       MovDir[x][y] = MV_NONE;
6827
6828     MovDelay[x][y] = 0;
6829   }
6830   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6831   {
6832     static int test_xy[7][2] =
6833     {
6834       { 0, -1 },
6835       { -1, 0 },
6836       { +1, 0 },
6837       { 0, +1 },
6838       { 0, -1 },
6839       { -1, 0 },
6840       { +1, 0 },
6841     };
6842     static int test_dir[7] =
6843     {
6844       MV_UP,
6845       MV_LEFT,
6846       MV_RIGHT,
6847       MV_DOWN,
6848       MV_UP,
6849       MV_LEFT,
6850       MV_RIGHT,
6851     };
6852     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6853     int move_preference = -1000000;     /* start with very low preference */
6854     int new_move_dir = MV_NONE;
6855     int start_test = RND(4);
6856     int i;
6857
6858     for (i = 0; i < NUM_DIRECTIONS; i++)
6859     {
6860       int move_dir = test_dir[start_test + i];
6861       int move_dir_preference;
6862
6863       xx = x + test_xy[start_test + i][0];
6864       yy = y + test_xy[start_test + i][1];
6865
6866       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6867           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6868       {
6869         new_move_dir = move_dir;
6870
6871         break;
6872       }
6873
6874       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6875         continue;
6876
6877       move_dir_preference = -1 * RunnerVisit[xx][yy];
6878       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6879         move_dir_preference = PlayerVisit[xx][yy];
6880
6881       if (move_dir_preference > move_preference)
6882       {
6883         /* prefer field that has not been visited for the longest time */
6884         move_preference = move_dir_preference;
6885         new_move_dir = move_dir;
6886       }
6887       else if (move_dir_preference == move_preference &&
6888                move_dir == old_move_dir)
6889       {
6890         /* prefer last direction when all directions are preferred equally */
6891         move_preference = move_dir_preference;
6892         new_move_dir = move_dir;
6893       }
6894     }
6895
6896     MovDir[x][y] = new_move_dir;
6897     if (old_move_dir != new_move_dir)
6898       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6899   }
6900 }
6901
6902 static void TurnRound(int x, int y)
6903 {
6904   int direction = MovDir[x][y];
6905
6906   TurnRoundExt(x, y);
6907
6908   GfxDir[x][y] = MovDir[x][y];
6909
6910   if (direction != MovDir[x][y])
6911     GfxFrame[x][y] = 0;
6912
6913   if (MovDelay[x][y])
6914     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6915
6916   ResetGfxFrame(x, y, FALSE);
6917 }
6918
6919 static boolean JustBeingPushed(int x, int y)
6920 {
6921   int i;
6922
6923   for (i = 0; i < MAX_PLAYERS; i++)
6924   {
6925     struct PlayerInfo *player = &stored_player[i];
6926
6927     if (player->active && player->is_pushing && player->MovPos)
6928     {
6929       int next_jx = player->jx + (player->jx - player->last_jx);
6930       int next_jy = player->jy + (player->jy - player->last_jy);
6931
6932       if (x == next_jx && y == next_jy)
6933         return TRUE;
6934     }
6935   }
6936
6937   return FALSE;
6938 }
6939
6940 void StartMoving(int x, int y)
6941 {
6942   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6943   int element = Feld[x][y];
6944
6945   if (Stop[x][y])
6946     return;
6947
6948   if (MovDelay[x][y] == 0)
6949     GfxAction[x][y] = ACTION_DEFAULT;
6950
6951   if (CAN_FALL(element) && y < lev_fieldy - 1)
6952   {
6953     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6954         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6955       if (JustBeingPushed(x, y))
6956         return;
6957
6958     if (element == EL_QUICKSAND_FULL)
6959     {
6960       if (IS_FREE(x, y + 1))
6961       {
6962         InitMovingField(x, y, MV_DOWN);
6963         started_moving = TRUE;
6964
6965         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6966 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6967         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6968           Store[x][y] = EL_ROCK;
6969 #else
6970         Store[x][y] = EL_ROCK;
6971 #endif
6972
6973         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6974       }
6975       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6976       {
6977         if (!MovDelay[x][y])
6978           MovDelay[x][y] = TILEY + 1;
6979
6980         if (MovDelay[x][y])
6981         {
6982           MovDelay[x][y]--;
6983           if (MovDelay[x][y])
6984             return;
6985         }
6986
6987         Feld[x][y] = EL_QUICKSAND_EMPTY;
6988         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6989         Store[x][y + 1] = Store[x][y];
6990         Store[x][y] = 0;
6991
6992         PlayLevelSoundAction(x, y, ACTION_FILLING);
6993       }
6994     }
6995     else if (element == EL_QUICKSAND_FAST_FULL)
6996     {
6997       if (IS_FREE(x, y + 1))
6998       {
6999         InitMovingField(x, y, MV_DOWN);
7000         started_moving = TRUE;
7001
7002         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7003 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7004         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7005           Store[x][y] = EL_ROCK;
7006 #else
7007         Store[x][y] = EL_ROCK;
7008 #endif
7009
7010         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7011       }
7012       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7013       {
7014         if (!MovDelay[x][y])
7015           MovDelay[x][y] = TILEY + 1;
7016
7017         if (MovDelay[x][y])
7018         {
7019           MovDelay[x][y]--;
7020           if (MovDelay[x][y])
7021             return;
7022         }
7023
7024         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7025         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7026         Store[x][y + 1] = Store[x][y];
7027         Store[x][y] = 0;
7028
7029         PlayLevelSoundAction(x, y, ACTION_FILLING);
7030       }
7031     }
7032     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7033              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7034     {
7035       InitMovingField(x, y, MV_DOWN);
7036       started_moving = TRUE;
7037
7038       Feld[x][y] = EL_QUICKSAND_FILLING;
7039       Store[x][y] = element;
7040
7041       PlayLevelSoundAction(x, y, ACTION_FILLING);
7042     }
7043     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7044              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7045     {
7046       InitMovingField(x, y, MV_DOWN);
7047       started_moving = TRUE;
7048
7049       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7050       Store[x][y] = element;
7051
7052       PlayLevelSoundAction(x, y, ACTION_FILLING);
7053     }
7054     else if (element == EL_MAGIC_WALL_FULL)
7055     {
7056       if (IS_FREE(x, y + 1))
7057       {
7058         InitMovingField(x, y, MV_DOWN);
7059         started_moving = TRUE;
7060
7061         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7062         Store[x][y] = EL_CHANGED(Store[x][y]);
7063       }
7064       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7065       {
7066         if (!MovDelay[x][y])
7067           MovDelay[x][y] = TILEY/4 + 1;
7068
7069         if (MovDelay[x][y])
7070         {
7071           MovDelay[x][y]--;
7072           if (MovDelay[x][y])
7073             return;
7074         }
7075
7076         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7077         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7078         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7079         Store[x][y] = 0;
7080       }
7081     }
7082     else if (element == EL_BD_MAGIC_WALL_FULL)
7083     {
7084       if (IS_FREE(x, y + 1))
7085       {
7086         InitMovingField(x, y, MV_DOWN);
7087         started_moving = TRUE;
7088
7089         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7090         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7091       }
7092       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7093       {
7094         if (!MovDelay[x][y])
7095           MovDelay[x][y] = TILEY/4 + 1;
7096
7097         if (MovDelay[x][y])
7098         {
7099           MovDelay[x][y]--;
7100           if (MovDelay[x][y])
7101             return;
7102         }
7103
7104         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7105         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7106         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7107         Store[x][y] = 0;
7108       }
7109     }
7110     else if (element == EL_DC_MAGIC_WALL_FULL)
7111     {
7112       if (IS_FREE(x, y + 1))
7113       {
7114         InitMovingField(x, y, MV_DOWN);
7115         started_moving = TRUE;
7116
7117         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7118         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7119       }
7120       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7121       {
7122         if (!MovDelay[x][y])
7123           MovDelay[x][y] = TILEY/4 + 1;
7124
7125         if (MovDelay[x][y])
7126         {
7127           MovDelay[x][y]--;
7128           if (MovDelay[x][y])
7129             return;
7130         }
7131
7132         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7133         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7134         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7135         Store[x][y] = 0;
7136       }
7137     }
7138     else if ((CAN_PASS_MAGIC_WALL(element) &&
7139               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7140                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7141              (CAN_PASS_DC_MAGIC_WALL(element) &&
7142               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7143
7144     {
7145       InitMovingField(x, y, MV_DOWN);
7146       started_moving = TRUE;
7147
7148       Feld[x][y] =
7149         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7150          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7151          EL_DC_MAGIC_WALL_FILLING);
7152       Store[x][y] = element;
7153     }
7154     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7155     {
7156       SplashAcid(x, y + 1);
7157
7158       InitMovingField(x, y, MV_DOWN);
7159       started_moving = TRUE;
7160
7161       Store[x][y] = EL_ACID;
7162     }
7163     else if (
7164 #if USE_FIX_IMPACT_COLLISION
7165              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7166               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7167 #else
7168              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7169               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7170 #endif
7171              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7172               CAN_FALL(element) && WasJustFalling[x][y] &&
7173               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7174
7175              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7176               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7177               (Feld[x][y + 1] == EL_BLOCKED)))
7178     {
7179       /* this is needed for a special case not covered by calling "Impact()"
7180          from "ContinueMoving()": if an element moves to a tile directly below
7181          another element which was just falling on that tile (which was empty
7182          in the previous frame), the falling element above would just stop
7183          instead of smashing the element below (in previous version, the above
7184          element was just checked for "moving" instead of "falling", resulting
7185          in incorrect smashes caused by horizontal movement of the above
7186          element; also, the case of the player being the element to smash was
7187          simply not covered here... :-/ ) */
7188
7189       CheckCollision[x][y] = 0;
7190       CheckImpact[x][y] = 0;
7191
7192       Impact(x, y);
7193     }
7194     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7195     {
7196       if (MovDir[x][y] == MV_NONE)
7197       {
7198         InitMovingField(x, y, MV_DOWN);
7199         started_moving = TRUE;
7200       }
7201     }
7202     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7203     {
7204       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7205         MovDir[x][y] = MV_DOWN;
7206
7207       InitMovingField(x, y, MV_DOWN);
7208       started_moving = TRUE;
7209     }
7210     else if (element == EL_AMOEBA_DROP)
7211     {
7212       Feld[x][y] = EL_AMOEBA_GROWING;
7213       Store[x][y] = EL_AMOEBA_WET;
7214     }
7215     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7216               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7217              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7218              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7219     {
7220       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7221                                 (IS_FREE(x - 1, y + 1) ||
7222                                  Feld[x - 1][y + 1] == EL_ACID));
7223       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7224                                 (IS_FREE(x + 1, y + 1) ||
7225                                  Feld[x + 1][y + 1] == EL_ACID));
7226       boolean can_fall_any  = (can_fall_left || can_fall_right);
7227       boolean can_fall_both = (can_fall_left && can_fall_right);
7228       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7229
7230 #if USE_NEW_ALL_SLIPPERY
7231       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7232       {
7233         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7234           can_fall_right = FALSE;
7235         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7236           can_fall_left = FALSE;
7237         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7238           can_fall_right = FALSE;
7239         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7240           can_fall_left = FALSE;
7241
7242         can_fall_any  = (can_fall_left || can_fall_right);
7243         can_fall_both = FALSE;
7244       }
7245 #else
7246       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7247       {
7248         if (slippery_type == SLIPPERY_ONLY_LEFT)
7249           can_fall_right = FALSE;
7250         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7251           can_fall_left = FALSE;
7252         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7253           can_fall_right = FALSE;
7254         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7255           can_fall_left = FALSE;
7256
7257         can_fall_any  = (can_fall_left || can_fall_right);
7258         can_fall_both = (can_fall_left && can_fall_right);
7259       }
7260 #endif
7261
7262 #if USE_NEW_ALL_SLIPPERY
7263 #else
7264 #if USE_NEW_SP_SLIPPERY
7265       /* !!! better use the same properties as for custom elements here !!! */
7266       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7267                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7268       {
7269         can_fall_right = FALSE;         /* slip down on left side */
7270         can_fall_both = FALSE;
7271       }
7272 #endif
7273 #endif
7274
7275 #if USE_NEW_ALL_SLIPPERY
7276       if (can_fall_both)
7277       {
7278         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7279           can_fall_right = FALSE;       /* slip down on left side */
7280         else
7281           can_fall_left = !(can_fall_right = RND(2));
7282
7283         can_fall_both = FALSE;
7284       }
7285 #else
7286       if (can_fall_both)
7287       {
7288         if (game.emulation == EMU_BOULDERDASH ||
7289             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7290           can_fall_right = FALSE;       /* slip down on left side */
7291         else
7292           can_fall_left = !(can_fall_right = RND(2));
7293
7294         can_fall_both = FALSE;
7295       }
7296 #endif
7297
7298       if (can_fall_any)
7299       {
7300         /* if not determined otherwise, prefer left side for slipping down */
7301         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7302         started_moving = TRUE;
7303       }
7304     }
7305 #if 0
7306     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7307 #else
7308     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7309 #endif
7310     {
7311       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7312       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7313       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7314       int belt_dir = game.belt_dir[belt_nr];
7315
7316       if ((belt_dir == MV_LEFT  && left_is_free) ||
7317           (belt_dir == MV_RIGHT && right_is_free))
7318       {
7319         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7320
7321         InitMovingField(x, y, belt_dir);
7322         started_moving = TRUE;
7323
7324         Pushed[x][y] = TRUE;
7325         Pushed[nextx][y] = TRUE;
7326
7327         GfxAction[x][y] = ACTION_DEFAULT;
7328       }
7329       else
7330       {
7331         MovDir[x][y] = 0;       /* if element was moving, stop it */
7332       }
7333     }
7334   }
7335
7336   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7337 #if 0
7338   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7339 #else
7340   if (CAN_MOVE(element) && !started_moving)
7341 #endif
7342   {
7343     int move_pattern = element_info[element].move_pattern;
7344     int newx, newy;
7345
7346 #if 0
7347 #if DEBUG
7348     if (MovDir[x][y] == MV_NONE)
7349     {
7350       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7351              x, y, element, element_info[element].token_name);
7352       printf("StartMoving(): This should never happen!\n");
7353     }
7354 #endif
7355 #endif
7356
7357     Moving2Blocked(x, y, &newx, &newy);
7358
7359     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7360       return;
7361
7362     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7363         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7364     {
7365       WasJustMoving[x][y] = 0;
7366       CheckCollision[x][y] = 0;
7367
7368       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7369
7370       if (Feld[x][y] != element)        /* element has changed */
7371         return;
7372     }
7373
7374     if (!MovDelay[x][y])        /* start new movement phase */
7375     {
7376       /* all objects that can change their move direction after each step
7377          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7378
7379       if (element != EL_YAMYAM &&
7380           element != EL_DARK_YAMYAM &&
7381           element != EL_PACMAN &&
7382           !(move_pattern & MV_ANY_DIRECTION) &&
7383           move_pattern != MV_TURNING_LEFT &&
7384           move_pattern != MV_TURNING_RIGHT &&
7385           move_pattern != MV_TURNING_LEFT_RIGHT &&
7386           move_pattern != MV_TURNING_RIGHT_LEFT &&
7387           move_pattern != MV_TURNING_RANDOM)
7388       {
7389         TurnRound(x, y);
7390
7391         if (MovDelay[x][y] && (element == EL_BUG ||
7392                                element == EL_SPACESHIP ||
7393                                element == EL_SP_SNIKSNAK ||
7394                                element == EL_SP_ELECTRON ||
7395                                element == EL_MOLE))
7396           DrawLevelField(x, y);
7397       }
7398     }
7399
7400     if (MovDelay[x][y])         /* wait some time before next movement */
7401     {
7402       MovDelay[x][y]--;
7403
7404       if (element == EL_ROBOT ||
7405           element == EL_YAMYAM ||
7406           element == EL_DARK_YAMYAM)
7407       {
7408         DrawLevelElementAnimationIfNeeded(x, y, element);
7409         PlayLevelSoundAction(x, y, ACTION_WAITING);
7410       }
7411       else if (element == EL_SP_ELECTRON)
7412         DrawLevelElementAnimationIfNeeded(x, y, element);
7413       else if (element == EL_DRAGON)
7414       {
7415         int i;
7416         int dir = MovDir[x][y];
7417         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7418         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7419         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7420                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7421                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7422                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7423         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7424
7425         GfxAction[x][y] = ACTION_ATTACKING;
7426
7427         if (IS_PLAYER(x, y))
7428           DrawPlayerField(x, y);
7429         else
7430           DrawLevelField(x, y);
7431
7432         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7433
7434         for (i = 1; i <= 3; i++)
7435         {
7436           int xx = x + i * dx;
7437           int yy = y + i * dy;
7438           int sx = SCREENX(xx);
7439           int sy = SCREENY(yy);
7440           int flame_graphic = graphic + (i - 1);
7441
7442           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7443             break;
7444
7445           if (MovDelay[x][y])
7446           {
7447             int flamed = MovingOrBlocked2Element(xx, yy);
7448
7449             /* !!! */
7450 #if 0
7451             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7452               Bang(xx, yy);
7453             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7454               RemoveMovingField(xx, yy);
7455             else
7456               RemoveField(xx, yy);
7457 #else
7458             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7459               Bang(xx, yy);
7460             else
7461               RemoveMovingField(xx, yy);
7462 #endif
7463
7464             ChangeDelay[xx][yy] = 0;
7465
7466             Feld[xx][yy] = EL_FLAMES;
7467
7468             if (IN_SCR_FIELD(sx, sy))
7469             {
7470               DrawLevelFieldCrumbledSand(xx, yy);
7471               DrawGraphic(sx, sy, flame_graphic, frame);
7472             }
7473           }
7474           else
7475           {
7476             if (Feld[xx][yy] == EL_FLAMES)
7477               Feld[xx][yy] = EL_EMPTY;
7478             DrawLevelField(xx, yy);
7479           }
7480         }
7481       }
7482
7483       if (MovDelay[x][y])       /* element still has to wait some time */
7484       {
7485         PlayLevelSoundAction(x, y, ACTION_WAITING);
7486
7487         return;
7488       }
7489     }
7490
7491     /* now make next step */
7492
7493     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7494
7495     if (DONT_COLLIDE_WITH(element) &&
7496         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7497         !PLAYER_ENEMY_PROTECTED(newx, newy))
7498     {
7499       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7500
7501       return;
7502     }
7503
7504     else if (CAN_MOVE_INTO_ACID(element) &&
7505              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7506              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7507              (MovDir[x][y] == MV_DOWN ||
7508               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7509     {
7510       SplashAcid(newx, newy);
7511       Store[x][y] = EL_ACID;
7512     }
7513     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7514     {
7515       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7516           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7517           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7518           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7519       {
7520         RemoveField(x, y);
7521         DrawLevelField(x, y);
7522
7523         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7524         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7525           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7526
7527         local_player->friends_still_needed--;
7528         if (!local_player->friends_still_needed &&
7529             !local_player->GameOver && AllPlayersGone)
7530           PlayerWins(local_player);
7531
7532         return;
7533       }
7534       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7535       {
7536         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7537           DrawLevelField(newx, newy);
7538         else
7539           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7540       }
7541       else if (!IS_FREE(newx, newy))
7542       {
7543         GfxAction[x][y] = ACTION_WAITING;
7544
7545         if (IS_PLAYER(x, y))
7546           DrawPlayerField(x, y);
7547         else
7548           DrawLevelField(x, y);
7549
7550         return;
7551       }
7552     }
7553     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7554     {
7555       if (IS_FOOD_PIG(Feld[newx][newy]))
7556       {
7557         if (IS_MOVING(newx, newy))
7558           RemoveMovingField(newx, newy);
7559         else
7560         {
7561           Feld[newx][newy] = EL_EMPTY;
7562           DrawLevelField(newx, newy);
7563         }
7564
7565         PlayLevelSound(x, y, SND_PIG_DIGGING);
7566       }
7567       else if (!IS_FREE(newx, newy))
7568       {
7569         if (IS_PLAYER(x, y))
7570           DrawPlayerField(x, y);
7571         else
7572           DrawLevelField(x, y);
7573
7574         return;
7575       }
7576     }
7577     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7578     {
7579       if (Store[x][y] != EL_EMPTY)
7580       {
7581         boolean can_clone = FALSE;
7582         int xx, yy;
7583
7584         /* check if element to clone is still there */
7585         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7586         {
7587           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7588           {
7589             can_clone = TRUE;
7590
7591             break;
7592           }
7593         }
7594
7595         /* cannot clone or target field not free anymore -- do not clone */
7596         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7597           Store[x][y] = EL_EMPTY;
7598       }
7599
7600       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7601       {
7602         if (IS_MV_DIAGONAL(MovDir[x][y]))
7603         {
7604           int diagonal_move_dir = MovDir[x][y];
7605           int stored = Store[x][y];
7606           int change_delay = 8;
7607           int graphic;
7608
7609           /* android is moving diagonally */
7610
7611           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7612
7613           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7614           GfxElement[x][y] = EL_EMC_ANDROID;
7615           GfxAction[x][y] = ACTION_SHRINKING;
7616           GfxDir[x][y] = diagonal_move_dir;
7617           ChangeDelay[x][y] = change_delay;
7618
7619           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7620                                    GfxDir[x][y]);
7621
7622           DrawLevelGraphicAnimation(x, y, graphic);
7623           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7624
7625           if (Feld[newx][newy] == EL_ACID)
7626           {
7627             SplashAcid(newx, newy);
7628
7629             return;
7630           }
7631
7632           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7633
7634           Store[newx][newy] = EL_EMC_ANDROID;
7635           GfxElement[newx][newy] = EL_EMC_ANDROID;
7636           GfxAction[newx][newy] = ACTION_GROWING;
7637           GfxDir[newx][newy] = diagonal_move_dir;
7638           ChangeDelay[newx][newy] = change_delay;
7639
7640           graphic = el_act_dir2img(GfxElement[newx][newy],
7641                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7642
7643           DrawLevelGraphicAnimation(newx, newy, graphic);
7644           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7645
7646           return;
7647         }
7648         else
7649         {
7650           Feld[newx][newy] = EL_EMPTY;
7651           DrawLevelField(newx, newy);
7652
7653           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7654         }
7655       }
7656       else if (!IS_FREE(newx, newy))
7657       {
7658 #if 0
7659         if (IS_PLAYER(x, y))
7660           DrawPlayerField(x, y);
7661         else
7662           DrawLevelField(x, y);
7663 #endif
7664
7665         return;
7666       }
7667     }
7668     else if (IS_CUSTOM_ELEMENT(element) &&
7669              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7670     {
7671       int new_element = Feld[newx][newy];
7672
7673       if (!IS_FREE(newx, newy))
7674       {
7675         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7676                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7677                       ACTION_BREAKING);
7678
7679         /* no element can dig solid indestructible elements */
7680         if (IS_INDESTRUCTIBLE(new_element) &&
7681             !IS_DIGGABLE(new_element) &&
7682             !IS_COLLECTIBLE(new_element))
7683           return;
7684
7685         if (AmoebaNr[newx][newy] &&
7686             (new_element == EL_AMOEBA_FULL ||
7687              new_element == EL_BD_AMOEBA ||
7688              new_element == EL_AMOEBA_GROWING))
7689         {
7690           AmoebaCnt[AmoebaNr[newx][newy]]--;
7691           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7692         }
7693
7694         if (IS_MOVING(newx, newy))
7695           RemoveMovingField(newx, newy);
7696         else
7697         {
7698           RemoveField(newx, newy);
7699           DrawLevelField(newx, newy);
7700         }
7701
7702         /* if digged element was about to explode, prevent the explosion */
7703         ExplodeField[newx][newy] = EX_TYPE_NONE;
7704
7705         PlayLevelSoundAction(x, y, action);
7706       }
7707
7708       Store[newx][newy] = EL_EMPTY;
7709 #if 1
7710       /* this makes it possible to leave the removed element again */
7711       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7712         Store[newx][newy] = new_element;
7713 #else
7714       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7715       {
7716         int move_leave_element = element_info[element].move_leave_element;
7717
7718         /* this makes it possible to leave the removed element again */
7719         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7720                              new_element : move_leave_element);
7721       }
7722 #endif
7723
7724       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7725       {
7726         RunnerVisit[x][y] = FrameCounter;
7727         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7728       }
7729     }
7730     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7731     {
7732       if (!IS_FREE(newx, newy))
7733       {
7734         if (IS_PLAYER(x, y))
7735           DrawPlayerField(x, y);
7736         else
7737           DrawLevelField(x, y);
7738
7739         return;
7740       }
7741       else
7742       {
7743         boolean wanna_flame = !RND(10);
7744         int dx = newx - x, dy = newy - y;
7745         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7746         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7747         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7748                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7749         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7750                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7751
7752         if ((wanna_flame ||
7753              IS_CLASSIC_ENEMY(element1) ||
7754              IS_CLASSIC_ENEMY(element2)) &&
7755             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7756             element1 != EL_FLAMES && element2 != EL_FLAMES)
7757         {
7758           ResetGfxAnimation(x, y);
7759           GfxAction[x][y] = ACTION_ATTACKING;
7760
7761           if (IS_PLAYER(x, y))
7762             DrawPlayerField(x, y);
7763           else
7764             DrawLevelField(x, y);
7765
7766           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7767
7768           MovDelay[x][y] = 50;
7769
7770           /* !!! */
7771 #if 0
7772           RemoveField(newx, newy);
7773 #endif
7774           Feld[newx][newy] = EL_FLAMES;
7775           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7776           {
7777 #if 0
7778             RemoveField(newx1, newy1);
7779 #endif
7780             Feld[newx1][newy1] = EL_FLAMES;
7781           }
7782           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7783           {
7784 #if 0
7785             RemoveField(newx2, newy2);
7786 #endif
7787             Feld[newx2][newy2] = EL_FLAMES;
7788           }
7789
7790           return;
7791         }
7792       }
7793     }
7794     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7795              Feld[newx][newy] == EL_DIAMOND)
7796     {
7797       if (IS_MOVING(newx, newy))
7798         RemoveMovingField(newx, newy);
7799       else
7800       {
7801         Feld[newx][newy] = EL_EMPTY;
7802         DrawLevelField(newx, newy);
7803       }
7804
7805       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7806     }
7807     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7808              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7809     {
7810       if (AmoebaNr[newx][newy])
7811       {
7812         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7813         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7814             Feld[newx][newy] == EL_BD_AMOEBA)
7815           AmoebaCnt[AmoebaNr[newx][newy]]--;
7816       }
7817
7818 #if 0
7819       /* !!! test !!! */
7820       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7821       {
7822         RemoveMovingField(newx, newy);
7823       }
7824 #else
7825       if (IS_MOVING(newx, newy))
7826       {
7827         RemoveMovingField(newx, newy);
7828       }
7829 #endif
7830       else
7831       {
7832         Feld[newx][newy] = EL_EMPTY;
7833         DrawLevelField(newx, newy);
7834       }
7835
7836       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7837     }
7838     else if ((element == EL_PACMAN || element == EL_MOLE)
7839              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7840     {
7841       if (AmoebaNr[newx][newy])
7842       {
7843         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7844         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7845             Feld[newx][newy] == EL_BD_AMOEBA)
7846           AmoebaCnt[AmoebaNr[newx][newy]]--;
7847       }
7848
7849       if (element == EL_MOLE)
7850       {
7851         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7852         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7853
7854         ResetGfxAnimation(x, y);
7855         GfxAction[x][y] = ACTION_DIGGING;
7856         DrawLevelField(x, y);
7857
7858         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7859
7860         return;                         /* wait for shrinking amoeba */
7861       }
7862       else      /* element == EL_PACMAN */
7863       {
7864         Feld[newx][newy] = EL_EMPTY;
7865         DrawLevelField(newx, newy);
7866         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7867       }
7868     }
7869     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7870              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7871               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7872     {
7873       /* wait for shrinking amoeba to completely disappear */
7874       return;
7875     }
7876     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7877     {
7878       /* object was running against a wall */
7879
7880       TurnRound(x, y);
7881
7882 #if 0
7883       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7884       if (move_pattern & MV_ANY_DIRECTION &&
7885           move_pattern == MovDir[x][y])
7886       {
7887         int blocking_element =
7888           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7889
7890         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7891                                  MovDir[x][y]);
7892
7893         element = Feld[x][y];   /* element might have changed */
7894       }
7895 #endif
7896
7897       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7898         DrawLevelElementAnimation(x, y, element);
7899
7900       if (DONT_TOUCH(element))
7901         TestIfBadThingTouchesPlayer(x, y);
7902
7903       return;
7904     }
7905
7906     InitMovingField(x, y, MovDir[x][y]);
7907
7908     PlayLevelSoundAction(x, y, ACTION_MOVING);
7909   }
7910
7911   if (MovDir[x][y])
7912     ContinueMoving(x, y);
7913 }
7914
7915 void ContinueMoving(int x, int y)
7916 {
7917   int element = Feld[x][y];
7918   struct ElementInfo *ei = &element_info[element];
7919   int direction = MovDir[x][y];
7920   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7921   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7922   int newx = x + dx, newy = y + dy;
7923   int stored = Store[x][y];
7924   int stored_new = Store[newx][newy];
7925   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7926   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7927   boolean last_line = (newy == lev_fieldy - 1);
7928
7929   MovPos[x][y] += getElementMoveStepsize(x, y);
7930
7931   if (pushed_by_player) /* special case: moving object pushed by player */
7932     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7933
7934   if (ABS(MovPos[x][y]) < TILEX)
7935   {
7936 #if 0
7937     int ee = Feld[x][y];
7938     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7939     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7940
7941     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7942            x, y, ABS(MovPos[x][y]),
7943            ee, gg, ff,
7944            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7945 #endif
7946
7947     DrawLevelField(x, y);
7948
7949     return;     /* element is still moving */
7950   }
7951
7952   /* element reached destination field */
7953
7954   Feld[x][y] = EL_EMPTY;
7955   Feld[newx][newy] = element;
7956   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7957
7958   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7959   {
7960     element = Feld[newx][newy] = EL_ACID;
7961   }
7962   else if (element == EL_MOLE)
7963   {
7964     Feld[x][y] = EL_SAND;
7965
7966     DrawLevelFieldCrumbledSandNeighbours(x, y);
7967   }
7968   else if (element == EL_QUICKSAND_FILLING)
7969   {
7970     element = Feld[newx][newy] = get_next_element(element);
7971     Store[newx][newy] = Store[x][y];
7972   }
7973   else if (element == EL_QUICKSAND_EMPTYING)
7974   {
7975     Feld[x][y] = get_next_element(element);
7976     element = Feld[newx][newy] = Store[x][y];
7977   }
7978   else if (element == EL_QUICKSAND_FAST_FILLING)
7979   {
7980     element = Feld[newx][newy] = get_next_element(element);
7981     Store[newx][newy] = Store[x][y];
7982   }
7983   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7984   {
7985     Feld[x][y] = get_next_element(element);
7986     element = Feld[newx][newy] = Store[x][y];
7987   }
7988   else if (element == EL_MAGIC_WALL_FILLING)
7989   {
7990     element = Feld[newx][newy] = get_next_element(element);
7991     if (!game.magic_wall_active)
7992       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7993     Store[newx][newy] = Store[x][y];
7994   }
7995   else if (element == EL_MAGIC_WALL_EMPTYING)
7996   {
7997     Feld[x][y] = get_next_element(element);
7998     if (!game.magic_wall_active)
7999       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8000     element = Feld[newx][newy] = Store[x][y];
8001
8002 #if USE_NEW_CUSTOM_VALUE
8003     InitField(newx, newy, FALSE);
8004 #endif
8005   }
8006   else if (element == EL_BD_MAGIC_WALL_FILLING)
8007   {
8008     element = Feld[newx][newy] = get_next_element(element);
8009     if (!game.magic_wall_active)
8010       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8011     Store[newx][newy] = Store[x][y];
8012   }
8013   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8014   {
8015     Feld[x][y] = get_next_element(element);
8016     if (!game.magic_wall_active)
8017       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8018     element = Feld[newx][newy] = Store[x][y];
8019
8020 #if USE_NEW_CUSTOM_VALUE
8021     InitField(newx, newy, FALSE);
8022 #endif
8023   }
8024   else if (element == EL_DC_MAGIC_WALL_FILLING)
8025   {
8026     element = Feld[newx][newy] = get_next_element(element);
8027     if (!game.magic_wall_active)
8028       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8029     Store[newx][newy] = Store[x][y];
8030   }
8031   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8032   {
8033     Feld[x][y] = get_next_element(element);
8034     if (!game.magic_wall_active)
8035       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8036     element = Feld[newx][newy] = Store[x][y];
8037
8038 #if USE_NEW_CUSTOM_VALUE
8039     InitField(newx, newy, FALSE);
8040 #endif
8041   }
8042   else if (element == EL_AMOEBA_DROPPING)
8043   {
8044     Feld[x][y] = get_next_element(element);
8045     element = Feld[newx][newy] = Store[x][y];
8046   }
8047   else if (element == EL_SOKOBAN_OBJECT)
8048   {
8049     if (Back[x][y])
8050       Feld[x][y] = Back[x][y];
8051
8052     if (Back[newx][newy])
8053       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8054
8055     Back[x][y] = Back[newx][newy] = 0;
8056   }
8057
8058   Store[x][y] = EL_EMPTY;
8059   MovPos[x][y] = 0;
8060   MovDir[x][y] = 0;
8061   MovDelay[x][y] = 0;
8062
8063   MovDelay[newx][newy] = 0;
8064
8065   if (CAN_CHANGE_OR_HAS_ACTION(element))
8066   {
8067     /* copy element change control values to new field */
8068     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8069     ChangePage[newx][newy]  = ChangePage[x][y];
8070     ChangeCount[newx][newy] = ChangeCount[x][y];
8071     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8072   }
8073
8074 #if USE_NEW_CUSTOM_VALUE
8075     CustomValue[newx][newy] = CustomValue[x][y];
8076 #endif
8077
8078   ChangeDelay[x][y] = 0;
8079   ChangePage[x][y] = -1;
8080   ChangeCount[x][y] = 0;
8081   ChangeEvent[x][y] = -1;
8082
8083 #if USE_NEW_CUSTOM_VALUE
8084   CustomValue[x][y] = 0;
8085 #endif
8086
8087   /* copy animation control values to new field */
8088   GfxFrame[newx][newy]  = GfxFrame[x][y];
8089   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8090   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8091   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8092
8093   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8094
8095   /* some elements can leave other elements behind after moving */
8096 #if 1
8097   if (ei->move_leave_element != EL_EMPTY &&
8098       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8099       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8100 #else
8101   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8102       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8103       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8104 #endif
8105   {
8106     int move_leave_element = ei->move_leave_element;
8107
8108 #if 1
8109 #if 1
8110     /* this makes it possible to leave the removed element again */
8111     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8112       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8113 #else
8114     /* this makes it possible to leave the removed element again */
8115     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8116       move_leave_element = stored;
8117 #endif
8118 #else
8119     /* this makes it possible to leave the removed element again */
8120     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8121         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8122       move_leave_element = stored;
8123 #endif
8124
8125     Feld[x][y] = move_leave_element;
8126
8127     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8128       MovDir[x][y] = direction;
8129
8130     InitField(x, y, FALSE);
8131
8132     if (GFX_CRUMBLED(Feld[x][y]))
8133       DrawLevelFieldCrumbledSandNeighbours(x, y);
8134
8135     if (ELEM_IS_PLAYER(move_leave_element))
8136       RelocatePlayer(x, y, move_leave_element);
8137   }
8138
8139   /* do this after checking for left-behind element */
8140   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8141
8142   if (!CAN_MOVE(element) ||
8143       (CAN_FALL(element) && direction == MV_DOWN &&
8144        (element == EL_SPRING ||
8145         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8146         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8147     GfxDir[x][y] = MovDir[newx][newy] = 0;
8148
8149   DrawLevelField(x, y);
8150   DrawLevelField(newx, newy);
8151
8152   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8153
8154   /* prevent pushed element from moving on in pushed direction */
8155   if (pushed_by_player && CAN_MOVE(element) &&
8156       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8157       !(element_info[element].move_pattern & direction))
8158     TurnRound(newx, newy);
8159
8160   /* prevent elements on conveyor belt from moving on in last direction */
8161   if (pushed_by_conveyor && CAN_FALL(element) &&
8162       direction & MV_HORIZONTAL)
8163     MovDir[newx][newy] = 0;
8164
8165   if (!pushed_by_player)
8166   {
8167     int nextx = newx + dx, nexty = newy + dy;
8168     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8169
8170     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8171
8172     if (CAN_FALL(element) && direction == MV_DOWN)
8173       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8174
8175     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8176       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8177
8178 #if USE_FIX_IMPACT_COLLISION
8179     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8180       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8181 #endif
8182   }
8183
8184   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8185   {
8186     TestIfBadThingTouchesPlayer(newx, newy);
8187     TestIfBadThingTouchesFriend(newx, newy);
8188
8189     if (!IS_CUSTOM_ELEMENT(element))
8190       TestIfBadThingTouchesOtherBadThing(newx, newy);
8191   }
8192   else if (element == EL_PENGUIN)
8193     TestIfFriendTouchesBadThing(newx, newy);
8194
8195   /* give the player one last chance (one more frame) to move away */
8196   if (CAN_FALL(element) && direction == MV_DOWN &&
8197       (last_line || (!IS_FREE(x, newy + 1) &&
8198                      (!IS_PLAYER(x, newy + 1) ||
8199                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8200     Impact(x, newy);
8201
8202   if (pushed_by_player && !game.use_change_when_pushing_bug)
8203   {
8204     int push_side = MV_DIR_OPPOSITE(direction);
8205     struct PlayerInfo *player = PLAYERINFO(x, y);
8206
8207     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8208                                player->index_bit, push_side);
8209     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8210                                         player->index_bit, push_side);
8211   }
8212
8213   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8214     MovDelay[newx][newy] = 1;
8215
8216   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8217
8218   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8219
8220 #if 0
8221   if (ChangePage[newx][newy] != -1)             /* delayed change */
8222   {
8223     int page = ChangePage[newx][newy];
8224     struct ElementChangeInfo *change = &ei->change_page[page];
8225
8226     ChangePage[newx][newy] = -1;
8227
8228     if (change->can_change)
8229     {
8230       if (ChangeElement(newx, newy, element, page))
8231       {
8232         if (change->post_change_function)
8233           change->post_change_function(newx, newy);
8234       }
8235     }
8236
8237     if (change->has_action)
8238       ExecuteCustomElementAction(newx, newy, element, page);
8239   }
8240 #endif
8241
8242   TestIfElementHitsCustomElement(newx, newy, direction);
8243   TestIfPlayerTouchesCustomElement(newx, newy);
8244   TestIfElementTouchesCustomElement(newx, newy);
8245
8246   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8247       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8248     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8249                              MV_DIR_OPPOSITE(direction));
8250 }
8251
8252 int AmoebeNachbarNr(int ax, int ay)
8253 {
8254   int i;
8255   int element = Feld[ax][ay];
8256   int group_nr = 0;
8257   static int xy[4][2] =
8258   {
8259     { 0, -1 },
8260     { -1, 0 },
8261     { +1, 0 },
8262     { 0, +1 }
8263   };
8264
8265   for (i = 0; i < NUM_DIRECTIONS; i++)
8266   {
8267     int x = ax + xy[i][0];
8268     int y = ay + xy[i][1];
8269
8270     if (!IN_LEV_FIELD(x, y))
8271       continue;
8272
8273     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8274       group_nr = AmoebaNr[x][y];
8275   }
8276
8277   return group_nr;
8278 }
8279
8280 void AmoebenVereinigen(int ax, int ay)
8281 {
8282   int i, x, y, xx, yy;
8283   int new_group_nr = AmoebaNr[ax][ay];
8284   static int xy[4][2] =
8285   {
8286     { 0, -1 },
8287     { -1, 0 },
8288     { +1, 0 },
8289     { 0, +1 }
8290   };
8291
8292   if (new_group_nr == 0)
8293     return;
8294
8295   for (i = 0; i < NUM_DIRECTIONS; i++)
8296   {
8297     x = ax + xy[i][0];
8298     y = ay + xy[i][1];
8299
8300     if (!IN_LEV_FIELD(x, y))
8301       continue;
8302
8303     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8304          Feld[x][y] == EL_BD_AMOEBA ||
8305          Feld[x][y] == EL_AMOEBA_DEAD) &&
8306         AmoebaNr[x][y] != new_group_nr)
8307     {
8308       int old_group_nr = AmoebaNr[x][y];
8309
8310       if (old_group_nr == 0)
8311         return;
8312
8313       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8314       AmoebaCnt[old_group_nr] = 0;
8315       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8316       AmoebaCnt2[old_group_nr] = 0;
8317
8318       SCAN_PLAYFIELD(xx, yy)
8319       {
8320         if (AmoebaNr[xx][yy] == old_group_nr)
8321           AmoebaNr[xx][yy] = new_group_nr;
8322       }
8323     }
8324   }
8325 }
8326
8327 void AmoebeUmwandeln(int ax, int ay)
8328 {
8329   int i, x, y;
8330
8331   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8332   {
8333     int group_nr = AmoebaNr[ax][ay];
8334
8335 #ifdef DEBUG
8336     if (group_nr == 0)
8337     {
8338       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8339       printf("AmoebeUmwandeln(): This should never happen!\n");
8340       return;
8341     }
8342 #endif
8343
8344     SCAN_PLAYFIELD(x, y)
8345     {
8346       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8347       {
8348         AmoebaNr[x][y] = 0;
8349         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8350       }
8351     }
8352
8353     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8354                             SND_AMOEBA_TURNING_TO_GEM :
8355                             SND_AMOEBA_TURNING_TO_ROCK));
8356     Bang(ax, ay);
8357   }
8358   else
8359   {
8360     static int xy[4][2] =
8361     {
8362       { 0, -1 },
8363       { -1, 0 },
8364       { +1, 0 },
8365       { 0, +1 }
8366     };
8367
8368     for (i = 0; i < NUM_DIRECTIONS; i++)
8369     {
8370       x = ax + xy[i][0];
8371       y = ay + xy[i][1];
8372
8373       if (!IN_LEV_FIELD(x, y))
8374         continue;
8375
8376       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8377       {
8378         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8379                               SND_AMOEBA_TURNING_TO_GEM :
8380                               SND_AMOEBA_TURNING_TO_ROCK));
8381         Bang(x, y);
8382       }
8383     }
8384   }
8385 }
8386
8387 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8388 {
8389   int x, y;
8390   int group_nr = AmoebaNr[ax][ay];
8391   boolean done = FALSE;
8392
8393 #ifdef DEBUG
8394   if (group_nr == 0)
8395   {
8396     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8397     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8398     return;
8399   }
8400 #endif
8401
8402   SCAN_PLAYFIELD(x, y)
8403   {
8404     if (AmoebaNr[x][y] == group_nr &&
8405         (Feld[x][y] == EL_AMOEBA_DEAD ||
8406          Feld[x][y] == EL_BD_AMOEBA ||
8407          Feld[x][y] == EL_AMOEBA_GROWING))
8408     {
8409       AmoebaNr[x][y] = 0;
8410       Feld[x][y] = new_element;
8411       InitField(x, y, FALSE);
8412       DrawLevelField(x, y);
8413       done = TRUE;
8414     }
8415   }
8416
8417   if (done)
8418     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8419                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8420                             SND_BD_AMOEBA_TURNING_TO_GEM));
8421 }
8422
8423 void AmoebeWaechst(int x, int y)
8424 {
8425   static unsigned long sound_delay = 0;
8426   static unsigned long sound_delay_value = 0;
8427
8428   if (!MovDelay[x][y])          /* start new growing cycle */
8429   {
8430     MovDelay[x][y] = 7;
8431
8432     if (DelayReached(&sound_delay, sound_delay_value))
8433     {
8434       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8435       sound_delay_value = 30;
8436     }
8437   }
8438
8439   if (MovDelay[x][y])           /* wait some time before growing bigger */
8440   {
8441     MovDelay[x][y]--;
8442     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8443     {
8444       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8445                                            6 - MovDelay[x][y]);
8446
8447       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8448     }
8449
8450     if (!MovDelay[x][y])
8451     {
8452       Feld[x][y] = Store[x][y];
8453       Store[x][y] = 0;
8454       DrawLevelField(x, y);
8455     }
8456   }
8457 }
8458
8459 void AmoebaDisappearing(int x, int y)
8460 {
8461   static unsigned long sound_delay = 0;
8462   static unsigned long sound_delay_value = 0;
8463
8464   if (!MovDelay[x][y])          /* start new shrinking cycle */
8465   {
8466     MovDelay[x][y] = 7;
8467
8468     if (DelayReached(&sound_delay, sound_delay_value))
8469       sound_delay_value = 30;
8470   }
8471
8472   if (MovDelay[x][y])           /* wait some time before shrinking */
8473   {
8474     MovDelay[x][y]--;
8475     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8476     {
8477       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8478                                            6 - MovDelay[x][y]);
8479
8480       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8481     }
8482
8483     if (!MovDelay[x][y])
8484     {
8485       Feld[x][y] = EL_EMPTY;
8486       DrawLevelField(x, y);
8487
8488       /* don't let mole enter this field in this cycle;
8489          (give priority to objects falling to this field from above) */
8490       Stop[x][y] = TRUE;
8491     }
8492   }
8493 }
8494
8495 void AmoebeAbleger(int ax, int ay)
8496 {
8497   int i;
8498   int element = Feld[ax][ay];
8499   int graphic = el2img(element);
8500   int newax = ax, neway = ay;
8501   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8502   static int xy[4][2] =
8503   {
8504     { 0, -1 },
8505     { -1, 0 },
8506     { +1, 0 },
8507     { 0, +1 }
8508   };
8509
8510   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8511   {
8512     Feld[ax][ay] = EL_AMOEBA_DEAD;
8513     DrawLevelField(ax, ay);
8514     return;
8515   }
8516
8517   if (IS_ANIMATED(graphic))
8518     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8519
8520   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8521     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8522
8523   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8524   {
8525     MovDelay[ax][ay]--;
8526     if (MovDelay[ax][ay])
8527       return;
8528   }
8529
8530   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8531   {
8532     int start = RND(4);
8533     int x = ax + xy[start][0];
8534     int y = ay + xy[start][1];
8535
8536     if (!IN_LEV_FIELD(x, y))
8537       return;
8538
8539     if (IS_FREE(x, y) ||
8540         CAN_GROW_INTO(Feld[x][y]) ||
8541         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8542         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8543     {
8544       newax = x;
8545       neway = y;
8546     }
8547
8548     if (newax == ax && neway == ay)
8549       return;
8550   }
8551   else                          /* normal or "filled" (BD style) amoeba */
8552   {
8553     int start = RND(4);
8554     boolean waiting_for_player = FALSE;
8555
8556     for (i = 0; i < NUM_DIRECTIONS; i++)
8557     {
8558       int j = (start + i) % 4;
8559       int x = ax + xy[j][0];
8560       int y = ay + xy[j][1];
8561
8562       if (!IN_LEV_FIELD(x, y))
8563         continue;
8564
8565       if (IS_FREE(x, y) ||
8566           CAN_GROW_INTO(Feld[x][y]) ||
8567           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8568           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8569       {
8570         newax = x;
8571         neway = y;
8572         break;
8573       }
8574       else if (IS_PLAYER(x, y))
8575         waiting_for_player = TRUE;
8576     }
8577
8578     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8579     {
8580       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8581       {
8582         Feld[ax][ay] = EL_AMOEBA_DEAD;
8583         DrawLevelField(ax, ay);
8584         AmoebaCnt[AmoebaNr[ax][ay]]--;
8585
8586         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8587         {
8588           if (element == EL_AMOEBA_FULL)
8589             AmoebeUmwandeln(ax, ay);
8590           else if (element == EL_BD_AMOEBA)
8591             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8592         }
8593       }
8594       return;
8595     }
8596     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8597     {
8598       /* amoeba gets larger by growing in some direction */
8599
8600       int new_group_nr = AmoebaNr[ax][ay];
8601
8602 #ifdef DEBUG
8603   if (new_group_nr == 0)
8604   {
8605     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8606     printf("AmoebeAbleger(): This should never happen!\n");
8607     return;
8608   }
8609 #endif
8610
8611       AmoebaNr[newax][neway] = new_group_nr;
8612       AmoebaCnt[new_group_nr]++;
8613       AmoebaCnt2[new_group_nr]++;
8614
8615       /* if amoeba touches other amoeba(s) after growing, unify them */
8616       AmoebenVereinigen(newax, neway);
8617
8618       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8619       {
8620         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8621         return;
8622       }
8623     }
8624   }
8625
8626   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8627       (neway == lev_fieldy - 1 && newax != ax))
8628   {
8629     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8630     Store[newax][neway] = element;
8631   }
8632   else if (neway == ay || element == EL_EMC_DRIPPER)
8633   {
8634     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8635
8636     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8637   }
8638   else
8639   {
8640     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8641     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8642     Store[ax][ay] = EL_AMOEBA_DROP;
8643     ContinueMoving(ax, ay);
8644     return;
8645   }
8646
8647   DrawLevelField(newax, neway);
8648 }
8649
8650 void Life(int ax, int ay)
8651 {
8652   int x1, y1, x2, y2;
8653   int life_time = 40;
8654   int element = Feld[ax][ay];
8655   int graphic = el2img(element);
8656   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8657                          level.biomaze);
8658   boolean changed = FALSE;
8659
8660   if (IS_ANIMATED(graphic))
8661     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8662
8663   if (Stop[ax][ay])
8664     return;
8665
8666   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8667     MovDelay[ax][ay] = life_time;
8668
8669   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8670   {
8671     MovDelay[ax][ay]--;
8672     if (MovDelay[ax][ay])
8673       return;
8674   }
8675
8676   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8677   {
8678     int xx = ax+x1, yy = ay+y1;
8679     int nachbarn = 0;
8680
8681     if (!IN_LEV_FIELD(xx, yy))
8682       continue;
8683
8684     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8685     {
8686       int x = xx+x2, y = yy+y2;
8687
8688       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8689         continue;
8690
8691       if (((Feld[x][y] == element ||
8692             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8693            !Stop[x][y]) ||
8694           (IS_FREE(x, y) && Stop[x][y]))
8695         nachbarn++;
8696     }
8697
8698     if (xx == ax && yy == ay)           /* field in the middle */
8699     {
8700       if (nachbarn < life_parameter[0] ||
8701           nachbarn > life_parameter[1])
8702       {
8703         Feld[xx][yy] = EL_EMPTY;
8704         if (!Stop[xx][yy])
8705           DrawLevelField(xx, yy);
8706         Stop[xx][yy] = TRUE;
8707         changed = TRUE;
8708       }
8709     }
8710     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8711     {                                   /* free border field */
8712       if (nachbarn >= life_parameter[2] &&
8713           nachbarn <= life_parameter[3])
8714       {
8715         Feld[xx][yy] = element;
8716         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8717         if (!Stop[xx][yy])
8718           DrawLevelField(xx, yy);
8719         Stop[xx][yy] = TRUE;
8720         changed = TRUE;
8721       }
8722     }
8723   }
8724
8725   if (changed)
8726     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8727                    SND_GAME_OF_LIFE_GROWING);
8728 }
8729
8730 static void InitRobotWheel(int x, int y)
8731 {
8732   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8733 }
8734
8735 static void RunRobotWheel(int x, int y)
8736 {
8737   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8738 }
8739
8740 static void StopRobotWheel(int x, int y)
8741 {
8742   if (ZX == x && ZY == y)
8743     ZX = ZY = -1;
8744 }
8745
8746 static void InitTimegateWheel(int x, int y)
8747 {
8748   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8749 }
8750
8751 static void RunTimegateWheel(int x, int y)
8752 {
8753   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8754 }
8755
8756 static void InitMagicBallDelay(int x, int y)
8757 {
8758 #if 1
8759   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8760 #else
8761   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8762 #endif
8763 }
8764
8765 static void ActivateMagicBall(int bx, int by)
8766 {
8767   int x, y;
8768
8769   if (level.ball_random)
8770   {
8771     int pos_border = RND(8);    /* select one of the eight border elements */
8772     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8773     int xx = pos_content % 3;
8774     int yy = pos_content / 3;
8775
8776     x = bx - 1 + xx;
8777     y = by - 1 + yy;
8778
8779     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8780       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8781   }
8782   else
8783   {
8784     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8785     {
8786       int xx = x - bx + 1;
8787       int yy = y - by + 1;
8788
8789       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8790         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8791     }
8792   }
8793
8794   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8795 }
8796
8797 void CheckExit(int x, int y)
8798 {
8799   if (local_player->gems_still_needed > 0 ||
8800       local_player->sokobanfields_still_needed > 0 ||
8801       local_player->lights_still_needed > 0)
8802   {
8803     int element = Feld[x][y];
8804     int graphic = el2img(element);
8805
8806     if (IS_ANIMATED(graphic))
8807       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8808
8809     return;
8810   }
8811
8812   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8813     return;
8814
8815   Feld[x][y] = EL_EXIT_OPENING;
8816
8817   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8818 }
8819
8820 void CheckExitEM(int x, int y)
8821 {
8822   if (local_player->gems_still_needed > 0 ||
8823       local_player->sokobanfields_still_needed > 0 ||
8824       local_player->lights_still_needed > 0)
8825   {
8826     int element = Feld[x][y];
8827     int graphic = el2img(element);
8828
8829     if (IS_ANIMATED(graphic))
8830       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8831
8832     return;
8833   }
8834
8835   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8836     return;
8837
8838   Feld[x][y] = EL_EM_EXIT_OPENING;
8839
8840   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8841 }
8842
8843 void CheckExitSteel(int x, int y)
8844 {
8845   if (local_player->gems_still_needed > 0 ||
8846       local_player->sokobanfields_still_needed > 0 ||
8847       local_player->lights_still_needed > 0)
8848   {
8849     int element = Feld[x][y];
8850     int graphic = el2img(element);
8851
8852     if (IS_ANIMATED(graphic))
8853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8854
8855     return;
8856   }
8857
8858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8859     return;
8860
8861   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8862
8863   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8864 }
8865
8866 void CheckExitSteelEM(int x, int y)
8867 {
8868   if (local_player->gems_still_needed > 0 ||
8869       local_player->sokobanfields_still_needed > 0 ||
8870       local_player->lights_still_needed > 0)
8871   {
8872     int element = Feld[x][y];
8873     int graphic = el2img(element);
8874
8875     if (IS_ANIMATED(graphic))
8876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8877
8878     return;
8879   }
8880
8881   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8882     return;
8883
8884   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8885
8886   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8887 }
8888
8889 void CheckExitSP(int x, int y)
8890 {
8891   if (local_player->gems_still_needed > 0)
8892   {
8893     int element = Feld[x][y];
8894     int graphic = el2img(element);
8895
8896     if (IS_ANIMATED(graphic))
8897       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8898
8899     return;
8900   }
8901
8902   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8903     return;
8904
8905   Feld[x][y] = EL_SP_EXIT_OPENING;
8906
8907   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8908 }
8909
8910 static void CloseAllOpenTimegates()
8911 {
8912   int x, y;
8913
8914   SCAN_PLAYFIELD(x, y)
8915   {
8916     int element = Feld[x][y];
8917
8918     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8919     {
8920       Feld[x][y] = EL_TIMEGATE_CLOSING;
8921
8922       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8923     }
8924   }
8925 }
8926
8927 void DrawTwinkleOnField(int x, int y)
8928 {
8929   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8930     return;
8931
8932   if (Feld[x][y] == EL_BD_DIAMOND)
8933     return;
8934
8935   if (MovDelay[x][y] == 0)      /* next animation frame */
8936     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8937
8938   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8939   {
8940     MovDelay[x][y]--;
8941
8942     if (setup.direct_draw && MovDelay[x][y])
8943       SetDrawtoField(DRAW_BUFFERED);
8944
8945     DrawLevelElementAnimation(x, y, Feld[x][y]);
8946
8947     if (MovDelay[x][y] != 0)
8948     {
8949       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8950                                            10 - MovDelay[x][y]);
8951
8952       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8953
8954       if (setup.direct_draw)
8955       {
8956         int dest_x, dest_y;
8957
8958         dest_x = FX + SCREENX(x) * TILEX;
8959         dest_y = FY + SCREENY(y) * TILEY;
8960
8961         BlitBitmap(drawto_field, window,
8962                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8963         SetDrawtoField(DRAW_DIRECT);
8964       }
8965     }
8966   }
8967 }
8968
8969 void MauerWaechst(int x, int y)
8970 {
8971   int delay = 6;
8972
8973   if (!MovDelay[x][y])          /* next animation frame */
8974     MovDelay[x][y] = 3 * delay;
8975
8976   if (MovDelay[x][y])           /* wait some time before next frame */
8977   {
8978     MovDelay[x][y]--;
8979
8980     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8981     {
8982       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8983       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8984
8985       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8986     }
8987
8988     if (!MovDelay[x][y])
8989     {
8990       if (MovDir[x][y] == MV_LEFT)
8991       {
8992         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8993           DrawLevelField(x - 1, y);
8994       }
8995       else if (MovDir[x][y] == MV_RIGHT)
8996       {
8997         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8998           DrawLevelField(x + 1, y);
8999       }
9000       else if (MovDir[x][y] == MV_UP)
9001       {
9002         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9003           DrawLevelField(x, y - 1);
9004       }
9005       else
9006       {
9007         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9008           DrawLevelField(x, y + 1);
9009       }
9010
9011       Feld[x][y] = Store[x][y];
9012       Store[x][y] = 0;
9013       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9014       DrawLevelField(x, y);
9015     }
9016   }
9017 }
9018
9019 void MauerAbleger(int ax, int ay)
9020 {
9021   int element = Feld[ax][ay];
9022   int graphic = el2img(element);
9023   boolean oben_frei = FALSE, unten_frei = FALSE;
9024   boolean links_frei = FALSE, rechts_frei = FALSE;
9025   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9026   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9027   boolean new_wall = FALSE;
9028
9029   if (IS_ANIMATED(graphic))
9030     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9031
9032   if (!MovDelay[ax][ay])        /* start building new wall */
9033     MovDelay[ax][ay] = 6;
9034
9035   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9036   {
9037     MovDelay[ax][ay]--;
9038     if (MovDelay[ax][ay])
9039       return;
9040   }
9041
9042   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9043     oben_frei = TRUE;
9044   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9045     unten_frei = TRUE;
9046   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9047     links_frei = TRUE;
9048   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9049     rechts_frei = TRUE;
9050
9051   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9052       element == EL_EXPANDABLE_WALL_ANY)
9053   {
9054     if (oben_frei)
9055     {
9056       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9057       Store[ax][ay-1] = element;
9058       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9059       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9060         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9061                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9062       new_wall = TRUE;
9063     }
9064     if (unten_frei)
9065     {
9066       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9067       Store[ax][ay+1] = element;
9068       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9069       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9070         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9071                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9072       new_wall = TRUE;
9073     }
9074   }
9075
9076   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9077       element == EL_EXPANDABLE_WALL_ANY ||
9078       element == EL_EXPANDABLE_WALL ||
9079       element == EL_BD_EXPANDABLE_WALL)
9080   {
9081     if (links_frei)
9082     {
9083       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9084       Store[ax-1][ay] = element;
9085       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9086       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9087         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9088                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9089       new_wall = TRUE;
9090     }
9091
9092     if (rechts_frei)
9093     {
9094       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9095       Store[ax+1][ay] = element;
9096       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9097       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9098         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9099                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9100       new_wall = TRUE;
9101     }
9102   }
9103
9104   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9105     DrawLevelField(ax, ay);
9106
9107   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9108     oben_massiv = TRUE;
9109   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9110     unten_massiv = TRUE;
9111   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9112     links_massiv = TRUE;
9113   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9114     rechts_massiv = TRUE;
9115
9116   if (((oben_massiv && unten_massiv) ||
9117        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9118        element == EL_EXPANDABLE_WALL) &&
9119       ((links_massiv && rechts_massiv) ||
9120        element == EL_EXPANDABLE_WALL_VERTICAL))
9121     Feld[ax][ay] = EL_WALL;
9122
9123   if (new_wall)
9124     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9125 }
9126
9127 void MauerAblegerStahl(int ax, int ay)
9128 {
9129   int element = Feld[ax][ay];
9130   int graphic = el2img(element);
9131   boolean oben_frei = FALSE, unten_frei = FALSE;
9132   boolean links_frei = FALSE, rechts_frei = FALSE;
9133   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9134   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9135   boolean new_wall = FALSE;
9136
9137   if (IS_ANIMATED(graphic))
9138     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9139
9140   if (!MovDelay[ax][ay])        /* start building new wall */
9141     MovDelay[ax][ay] = 6;
9142
9143   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9144   {
9145     MovDelay[ax][ay]--;
9146     if (MovDelay[ax][ay])
9147       return;
9148   }
9149
9150   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9151     oben_frei = TRUE;
9152   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9153     unten_frei = TRUE;
9154   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9155     links_frei = TRUE;
9156   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9157     rechts_frei = TRUE;
9158
9159   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9160       element == EL_EXPANDABLE_STEELWALL_ANY)
9161   {
9162     if (oben_frei)
9163     {
9164       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9165       Store[ax][ay-1] = element;
9166       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9167       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9168         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9169                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9170       new_wall = TRUE;
9171     }
9172     if (unten_frei)
9173     {
9174       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9175       Store[ax][ay+1] = element;
9176       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9177       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9178         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9179                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9180       new_wall = TRUE;
9181     }
9182   }
9183
9184   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9185       element == EL_EXPANDABLE_STEELWALL_ANY)
9186   {
9187     if (links_frei)
9188     {
9189       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9190       Store[ax-1][ay] = element;
9191       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9192       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9193         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9194                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9195       new_wall = TRUE;
9196     }
9197
9198     if (rechts_frei)
9199     {
9200       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9201       Store[ax+1][ay] = element;
9202       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9203       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9204         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9205                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9206       new_wall = TRUE;
9207     }
9208   }
9209
9210   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9211     oben_massiv = TRUE;
9212   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9213     unten_massiv = TRUE;
9214   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9215     links_massiv = TRUE;
9216   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9217     rechts_massiv = TRUE;
9218
9219   if (((oben_massiv && unten_massiv) ||
9220        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9221       ((links_massiv && rechts_massiv) ||
9222        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9223     Feld[ax][ay] = EL_WALL;
9224
9225   if (new_wall)
9226     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9227 }
9228
9229 void CheckForDragon(int x, int y)
9230 {
9231   int i, j;
9232   boolean dragon_found = FALSE;
9233   static int xy[4][2] =
9234   {
9235     { 0, -1 },
9236     { -1, 0 },
9237     { +1, 0 },
9238     { 0, +1 }
9239   };
9240
9241   for (i = 0; i < NUM_DIRECTIONS; i++)
9242   {
9243     for (j = 0; j < 4; j++)
9244     {
9245       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9246
9247       if (IN_LEV_FIELD(xx, yy) &&
9248           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9249       {
9250         if (Feld[xx][yy] == EL_DRAGON)
9251           dragon_found = TRUE;
9252       }
9253       else
9254         break;
9255     }
9256   }
9257
9258   if (!dragon_found)
9259   {
9260     for (i = 0; i < NUM_DIRECTIONS; i++)
9261     {
9262       for (j = 0; j < 3; j++)
9263       {
9264         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9265   
9266         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9267         {
9268           Feld[xx][yy] = EL_EMPTY;
9269           DrawLevelField(xx, yy);
9270         }
9271         else
9272           break;
9273       }
9274     }
9275   }
9276 }
9277
9278 static void InitBuggyBase(int x, int y)
9279 {
9280   int element = Feld[x][y];
9281   int activating_delay = FRAMES_PER_SECOND / 4;
9282
9283   ChangeDelay[x][y] =
9284     (element == EL_SP_BUGGY_BASE ?
9285      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9286      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9287      activating_delay :
9288      element == EL_SP_BUGGY_BASE_ACTIVE ?
9289      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9290 }
9291
9292 static void WarnBuggyBase(int x, int y)
9293 {
9294   int i;
9295   static int xy[4][2] =
9296   {
9297     { 0, -1 },
9298     { -1, 0 },
9299     { +1, 0 },
9300     { 0, +1 }
9301   };
9302
9303   for (i = 0; i < NUM_DIRECTIONS; i++)
9304   {
9305     int xx = x + xy[i][0];
9306     int yy = y + xy[i][1];
9307
9308     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9309     {
9310       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9311
9312       break;
9313     }
9314   }
9315 }
9316
9317 static void InitTrap(int x, int y)
9318 {
9319   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9320 }
9321
9322 static void ActivateTrap(int x, int y)
9323 {
9324   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9325 }
9326
9327 static void ChangeActiveTrap(int x, int y)
9328 {
9329   int graphic = IMG_TRAP_ACTIVE;
9330
9331   /* if new animation frame was drawn, correct crumbled sand border */
9332   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9333     DrawLevelFieldCrumbledSand(x, y);
9334 }
9335
9336 static int getSpecialActionElement(int element, int number, int base_element)
9337 {
9338   return (element != EL_EMPTY ? element :
9339           number != -1 ? base_element + number - 1 :
9340           EL_EMPTY);
9341 }
9342
9343 static int getModifiedActionNumber(int value_old, int operator, int operand,
9344                                    int value_min, int value_max)
9345 {
9346   int value_new = (operator == CA_MODE_SET      ? operand :
9347                    operator == CA_MODE_ADD      ? value_old + operand :
9348                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9349                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9350                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9351                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9352                    value_old);
9353
9354   return (value_new < value_min ? value_min :
9355           value_new > value_max ? value_max :
9356           value_new);
9357 }
9358
9359 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9360 {
9361   struct ElementInfo *ei = &element_info[element];
9362   struct ElementChangeInfo *change = &ei->change_page[page];
9363   int target_element = change->target_element;
9364   int action_type = change->action_type;
9365   int action_mode = change->action_mode;
9366   int action_arg = change->action_arg;
9367   int i;
9368
9369   if (!change->has_action)
9370     return;
9371
9372   /* ---------- determine action paramater values -------------------------- */
9373
9374   int level_time_value =
9375     (level.time > 0 ? TimeLeft :
9376      TimePlayed);
9377
9378   int action_arg_element =
9379     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9380      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9381      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9382      EL_EMPTY);
9383
9384   int action_arg_direction =
9385     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9386      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9387      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9388      change->actual_trigger_side :
9389      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9390      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9391      MV_NONE);
9392
9393   int action_arg_number_min =
9394     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9395      CA_ARG_MIN);
9396
9397   int action_arg_number_max =
9398     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9399      action_type == CA_SET_LEVEL_GEMS ? 999 :
9400      action_type == CA_SET_LEVEL_TIME ? 9999 :
9401      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9402      action_type == CA_SET_CE_VALUE ? 9999 :
9403      action_type == CA_SET_CE_SCORE ? 9999 :
9404      CA_ARG_MAX);
9405
9406   int action_arg_number_reset =
9407     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9408      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9409      action_type == CA_SET_LEVEL_TIME ? level.time :
9410      action_type == CA_SET_LEVEL_SCORE ? 0 :
9411 #if USE_NEW_CUSTOM_VALUE
9412      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9413 #else
9414      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9415 #endif
9416      action_type == CA_SET_CE_SCORE ? 0 :
9417      0);
9418
9419   int action_arg_number =
9420     (action_arg <= CA_ARG_MAX ? action_arg :
9421      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9422      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9423      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9424      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9425      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9426      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9427 #if USE_NEW_CUSTOM_VALUE
9428      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9429 #else
9430      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9431 #endif
9432      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9433      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9434      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9435      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9436      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9437      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9438      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9439      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9440      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9441      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9442      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9443      -1);
9444
9445   int action_arg_number_old =
9446     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9447      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9448      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9449      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9450      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9451      0);
9452
9453   int action_arg_number_new =
9454     getModifiedActionNumber(action_arg_number_old,
9455                             action_mode, action_arg_number,
9456                             action_arg_number_min, action_arg_number_max);
9457
9458   int trigger_player_bits =
9459     (change->actual_trigger_player >= EL_PLAYER_1 &&
9460      change->actual_trigger_player <= EL_PLAYER_4 ?
9461      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9462      PLAYER_BITS_ANY);
9463
9464   int action_arg_player_bits =
9465     (action_arg >= CA_ARG_PLAYER_1 &&
9466      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9467      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9468      PLAYER_BITS_ANY);
9469
9470   /* ---------- execute action  -------------------------------------------- */
9471
9472   switch (action_type)
9473   {
9474     case CA_NO_ACTION:
9475     {
9476       return;
9477     }
9478
9479     /* ---------- level actions  ------------------------------------------- */
9480
9481     case CA_RESTART_LEVEL:
9482     {
9483       game.restart_level = TRUE;
9484
9485       break;
9486     }
9487
9488     case CA_SHOW_ENVELOPE:
9489     {
9490       int element = getSpecialActionElement(action_arg_element,
9491                                             action_arg_number, EL_ENVELOPE_1);
9492
9493       if (IS_ENVELOPE(element))
9494         local_player->show_envelope = element;
9495
9496       break;
9497     }
9498
9499     case CA_SET_LEVEL_TIME:
9500     {
9501       if (level.time > 0)       /* only modify limited time value */
9502       {
9503         TimeLeft = action_arg_number_new;
9504
9505 #if 1
9506         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9507
9508         DisplayGameControlValues();
9509 #else
9510         DrawGameValue_Time(TimeLeft);
9511 #endif
9512
9513         if (!TimeLeft && setup.time_limit)
9514           for (i = 0; i < MAX_PLAYERS; i++)
9515             KillPlayer(&stored_player[i]);
9516       }
9517
9518       break;
9519     }
9520
9521     case CA_SET_LEVEL_SCORE:
9522     {
9523       local_player->score = action_arg_number_new;
9524
9525 #if 1
9526       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9527
9528       DisplayGameControlValues();
9529 #else
9530       DrawGameValue_Score(local_player->score);
9531 #endif
9532
9533       break;
9534     }
9535
9536     case CA_SET_LEVEL_GEMS:
9537     {
9538       local_player->gems_still_needed = action_arg_number_new;
9539
9540 #if 1
9541       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9542
9543       DisplayGameControlValues();
9544 #else
9545       DrawGameValue_Emeralds(local_player->gems_still_needed);
9546 #endif
9547
9548       break;
9549     }
9550
9551 #if !USE_PLAYER_GRAVITY
9552     case CA_SET_LEVEL_GRAVITY:
9553     {
9554       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9555                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9556                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9557                       game.gravity);
9558       break;
9559     }
9560 #endif
9561
9562     case CA_SET_LEVEL_WIND:
9563     {
9564       game.wind_direction = action_arg_direction;
9565
9566       break;
9567     }
9568
9569     /* ---------- player actions  ------------------------------------------ */
9570
9571     case CA_MOVE_PLAYER:
9572     {
9573       /* automatically move to the next field in specified direction */
9574       for (i = 0; i < MAX_PLAYERS; i++)
9575         if (trigger_player_bits & (1 << i))
9576           stored_player[i].programmed_action = action_arg_direction;
9577
9578       break;
9579     }
9580
9581     case CA_EXIT_PLAYER:
9582     {
9583       for (i = 0; i < MAX_PLAYERS; i++)
9584         if (action_arg_player_bits & (1 << i))
9585           PlayerWins(&stored_player[i]);
9586
9587       break;
9588     }
9589
9590     case CA_KILL_PLAYER:
9591     {
9592       for (i = 0; i < MAX_PLAYERS; i++)
9593         if (action_arg_player_bits & (1 << i))
9594           KillPlayer(&stored_player[i]);
9595
9596       break;
9597     }
9598
9599     case CA_SET_PLAYER_KEYS:
9600     {
9601       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9602       int element = getSpecialActionElement(action_arg_element,
9603                                             action_arg_number, EL_KEY_1);
9604
9605       if (IS_KEY(element))
9606       {
9607         for (i = 0; i < MAX_PLAYERS; i++)
9608         {
9609           if (trigger_player_bits & (1 << i))
9610           {
9611             stored_player[i].key[KEY_NR(element)] = key_state;
9612
9613             DrawGameDoorValues();
9614           }
9615         }
9616       }
9617
9618       break;
9619     }
9620
9621     case CA_SET_PLAYER_SPEED:
9622     {
9623       for (i = 0; i < MAX_PLAYERS; i++)
9624       {
9625         if (trigger_player_bits & (1 << i))
9626         {
9627           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9628
9629           if (action_arg == CA_ARG_SPEED_FASTER &&
9630               stored_player[i].cannot_move)
9631           {
9632             action_arg_number = STEPSIZE_VERY_SLOW;
9633           }
9634           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9635                    action_arg == CA_ARG_SPEED_FASTER)
9636           {
9637             action_arg_number = 2;
9638             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9639                            CA_MODE_MULTIPLY);
9640           }
9641           else if (action_arg == CA_ARG_NUMBER_RESET)
9642           {
9643             action_arg_number = level.initial_player_stepsize[i];
9644           }
9645
9646           move_stepsize =
9647             getModifiedActionNumber(move_stepsize,
9648                                     action_mode,
9649                                     action_arg_number,
9650                                     action_arg_number_min,
9651                                     action_arg_number_max);
9652
9653           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9654         }
9655       }
9656
9657       break;
9658     }
9659
9660     case CA_SET_PLAYER_SHIELD:
9661     {
9662       for (i = 0; i < MAX_PLAYERS; i++)
9663       {
9664         if (trigger_player_bits & (1 << i))
9665         {
9666           if (action_arg == CA_ARG_SHIELD_OFF)
9667           {
9668             stored_player[i].shield_normal_time_left = 0;
9669             stored_player[i].shield_deadly_time_left = 0;
9670           }
9671           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9672           {
9673             stored_player[i].shield_normal_time_left = 999999;
9674           }
9675           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9676           {
9677             stored_player[i].shield_normal_time_left = 999999;
9678             stored_player[i].shield_deadly_time_left = 999999;
9679           }
9680         }
9681       }
9682
9683       break;
9684     }
9685
9686 #if USE_PLAYER_GRAVITY
9687     case CA_SET_PLAYER_GRAVITY:
9688     {
9689       for (i = 0; i < MAX_PLAYERS; i++)
9690       {
9691         if (trigger_player_bits & (1 << i))
9692         {
9693           stored_player[i].gravity =
9694             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9695              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9696              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9697              stored_player[i].gravity);
9698         }
9699       }
9700
9701       break;
9702     }
9703 #endif
9704
9705     case CA_SET_PLAYER_ARTWORK:
9706     {
9707       for (i = 0; i < MAX_PLAYERS; i++)
9708       {
9709         if (trigger_player_bits & (1 << i))
9710         {
9711           int artwork_element = action_arg_element;
9712
9713           if (action_arg == CA_ARG_ELEMENT_RESET)
9714             artwork_element =
9715               (level.use_artwork_element[i] ? level.artwork_element[i] :
9716                stored_player[i].element_nr);
9717
9718 #if USE_GFX_RESET_PLAYER_ARTWORK
9719           if (stored_player[i].artwork_element != artwork_element)
9720             stored_player[i].Frame = 0;
9721 #endif
9722
9723           stored_player[i].artwork_element = artwork_element;
9724
9725           SetPlayerWaiting(&stored_player[i], FALSE);
9726
9727           /* set number of special actions for bored and sleeping animation */
9728           stored_player[i].num_special_action_bored =
9729             get_num_special_action(artwork_element,
9730                                    ACTION_BORING_1, ACTION_BORING_LAST);
9731           stored_player[i].num_special_action_sleeping =
9732             get_num_special_action(artwork_element,
9733                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9734         }
9735       }
9736
9737       break;
9738     }
9739
9740     /* ---------- CE actions  ---------------------------------------------- */
9741
9742     case CA_SET_CE_VALUE:
9743     {
9744 #if USE_NEW_CUSTOM_VALUE
9745       int last_ce_value = CustomValue[x][y];
9746
9747       CustomValue[x][y] = action_arg_number_new;
9748
9749       if (CustomValue[x][y] != last_ce_value)
9750       {
9751         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9752         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9753
9754         if (CustomValue[x][y] == 0)
9755         {
9756           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9757           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9758         }
9759       }
9760 #endif
9761
9762       break;
9763     }
9764
9765     case CA_SET_CE_SCORE:
9766     {
9767 #if USE_NEW_CUSTOM_VALUE
9768       int last_ce_score = ei->collect_score;
9769
9770       ei->collect_score = action_arg_number_new;
9771
9772       if (ei->collect_score != last_ce_score)
9773       {
9774         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9775         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9776
9777         if (ei->collect_score == 0)
9778         {
9779           int xx, yy;
9780
9781           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9782           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9783
9784           /*
9785             This is a very special case that seems to be a mixture between
9786             CheckElementChange() and CheckTriggeredElementChange(): while
9787             the first one only affects single elements that are triggered
9788             directly, the second one affects multiple elements in the playfield
9789             that are triggered indirectly by another element. This is a third
9790             case: Changing the CE score always affects multiple identical CEs,
9791             so every affected CE must be checked, not only the single CE for
9792             which the CE score was changed in the first place (as every instance
9793             of that CE shares the same CE score, and therefore also can change)!
9794           */
9795           SCAN_PLAYFIELD(xx, yy)
9796           {
9797             if (Feld[xx][yy] == element)
9798               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9799                                  CE_SCORE_GETS_ZERO);
9800           }
9801         }
9802       }
9803 #endif
9804
9805       break;
9806     }
9807
9808     /* ---------- engine actions  ------------------------------------------ */
9809
9810     case CA_SET_ENGINE_SCAN_MODE:
9811     {
9812       InitPlayfieldScanMode(action_arg);
9813
9814       break;
9815     }
9816
9817     default:
9818       break;
9819   }
9820 }
9821
9822 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9823 {
9824   int old_element = Feld[x][y];
9825   int new_element = GetElementFromGroupElement(element);
9826   int previous_move_direction = MovDir[x][y];
9827 #if USE_NEW_CUSTOM_VALUE
9828   int last_ce_value = CustomValue[x][y];
9829 #endif
9830   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9831   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9832   boolean add_player_onto_element = (new_element_is_player &&
9833 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9834                                      /* this breaks SnakeBite when a snake is
9835                                         halfway through a door that closes */
9836                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9837                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9838 #endif
9839                                      IS_WALKABLE(old_element));
9840
9841 #if 0
9842   /* check if element under the player changes from accessible to unaccessible
9843      (needed for special case of dropping element which then changes) */
9844   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9845       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9846   {
9847     Bang(x, y);
9848
9849     return;
9850   }
9851 #endif
9852
9853   if (!add_player_onto_element)
9854   {
9855     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9856       RemoveMovingField(x, y);
9857     else
9858       RemoveField(x, y);
9859
9860     Feld[x][y] = new_element;
9861
9862 #if !USE_GFX_RESET_GFX_ANIMATION
9863     ResetGfxAnimation(x, y);
9864     ResetRandomAnimationValue(x, y);
9865 #endif
9866
9867     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9868       MovDir[x][y] = previous_move_direction;
9869
9870 #if USE_NEW_CUSTOM_VALUE
9871     if (element_info[new_element].use_last_ce_value)
9872       CustomValue[x][y] = last_ce_value;
9873 #endif
9874
9875     InitField_WithBug1(x, y, FALSE);
9876
9877     new_element = Feld[x][y];   /* element may have changed */
9878
9879 #if USE_GFX_RESET_GFX_ANIMATION
9880     ResetGfxAnimation(x, y);
9881     ResetRandomAnimationValue(x, y);
9882 #endif
9883
9884     DrawLevelField(x, y);
9885
9886     if (GFX_CRUMBLED(new_element))
9887       DrawLevelFieldCrumbledSandNeighbours(x, y);
9888   }
9889
9890 #if 1
9891   /* check if element under the player changes from accessible to unaccessible
9892      (needed for special case of dropping element which then changes) */
9893   /* (must be checked after creating new element for walkable group elements) */
9894 #if USE_FIX_KILLED_BY_NON_WALKABLE
9895   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9896       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9897   {
9898     Bang(x, y);
9899
9900     return;
9901   }
9902 #else
9903   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9904       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9905   {
9906     Bang(x, y);
9907
9908     return;
9909   }
9910 #endif
9911 #endif
9912
9913   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9914   if (new_element_is_player)
9915     RelocatePlayer(x, y, new_element);
9916
9917   if (is_change)
9918     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9919
9920   TestIfBadThingTouchesPlayer(x, y);
9921   TestIfPlayerTouchesCustomElement(x, y);
9922   TestIfElementTouchesCustomElement(x, y);
9923 }
9924
9925 static void CreateField(int x, int y, int element)
9926 {
9927   CreateFieldExt(x, y, element, FALSE);
9928 }
9929
9930 static void CreateElementFromChange(int x, int y, int element)
9931 {
9932   element = GET_VALID_RUNTIME_ELEMENT(element);
9933
9934 #if USE_STOP_CHANGED_ELEMENTS
9935   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9936   {
9937     int old_element = Feld[x][y];
9938
9939     /* prevent changed element from moving in same engine frame
9940        unless both old and new element can either fall or move */
9941     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9942         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9943       Stop[x][y] = TRUE;
9944   }
9945 #endif
9946
9947   CreateFieldExt(x, y, element, TRUE);
9948 }
9949
9950 static boolean ChangeElement(int x, int y, int element, int page)
9951 {
9952   struct ElementInfo *ei = &element_info[element];
9953   struct ElementChangeInfo *change = &ei->change_page[page];
9954   int ce_value = CustomValue[x][y];
9955   int ce_score = ei->collect_score;
9956   int target_element;
9957   int old_element = Feld[x][y];
9958
9959   /* always use default change event to prevent running into a loop */
9960   if (ChangeEvent[x][y] == -1)
9961     ChangeEvent[x][y] = CE_DELAY;
9962
9963   if (ChangeEvent[x][y] == CE_DELAY)
9964   {
9965     /* reset actual trigger element, trigger player and action element */
9966     change->actual_trigger_element = EL_EMPTY;
9967     change->actual_trigger_player = EL_PLAYER_1;
9968     change->actual_trigger_side = CH_SIDE_NONE;
9969     change->actual_trigger_ce_value = 0;
9970     change->actual_trigger_ce_score = 0;
9971   }
9972
9973   /* do not change elements more than a specified maximum number of changes */
9974   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9975     return FALSE;
9976
9977   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9978
9979   if (change->explode)
9980   {
9981     Bang(x, y);
9982
9983     return TRUE;
9984   }
9985
9986   if (change->use_target_content)
9987   {
9988     boolean complete_replace = TRUE;
9989     boolean can_replace[3][3];
9990     int xx, yy;
9991
9992     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9993     {
9994       boolean is_empty;
9995       boolean is_walkable;
9996       boolean is_diggable;
9997       boolean is_collectible;
9998       boolean is_removable;
9999       boolean is_destructible;
10000       int ex = x + xx - 1;
10001       int ey = y + yy - 1;
10002       int content_element = change->target_content.e[xx][yy];
10003       int e;
10004
10005       can_replace[xx][yy] = TRUE;
10006
10007       if (ex == x && ey == y)   /* do not check changing element itself */
10008         continue;
10009
10010       if (content_element == EL_EMPTY_SPACE)
10011       {
10012         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10013
10014         continue;
10015       }
10016
10017       if (!IN_LEV_FIELD(ex, ey))
10018       {
10019         can_replace[xx][yy] = FALSE;
10020         complete_replace = FALSE;
10021
10022         continue;
10023       }
10024
10025       e = Feld[ex][ey];
10026
10027       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10028         e = MovingOrBlocked2Element(ex, ey);
10029
10030       is_empty = (IS_FREE(ex, ey) ||
10031                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10032
10033       is_walkable     = (is_empty || IS_WALKABLE(e));
10034       is_diggable     = (is_empty || IS_DIGGABLE(e));
10035       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10036       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10037       is_removable    = (is_diggable || is_collectible);
10038
10039       can_replace[xx][yy] =
10040         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10041           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10042           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10043           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10044           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10045           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10046          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10047
10048       if (!can_replace[xx][yy])
10049         complete_replace = FALSE;
10050     }
10051
10052     if (!change->only_if_complete || complete_replace)
10053     {
10054       boolean something_has_changed = FALSE;
10055
10056       if (change->only_if_complete && change->use_random_replace &&
10057           RND(100) < change->random_percentage)
10058         return FALSE;
10059
10060       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10061       {
10062         int ex = x + xx - 1;
10063         int ey = y + yy - 1;
10064         int content_element;
10065
10066         if (can_replace[xx][yy] && (!change->use_random_replace ||
10067                                     RND(100) < change->random_percentage))
10068         {
10069           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10070             RemoveMovingField(ex, ey);
10071
10072           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10073
10074           content_element = change->target_content.e[xx][yy];
10075           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10076                                               ce_value, ce_score);
10077
10078           CreateElementFromChange(ex, ey, target_element);
10079
10080           something_has_changed = TRUE;
10081
10082           /* for symmetry reasons, freeze newly created border elements */
10083           if (ex != x || ey != y)
10084             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10085         }
10086       }
10087
10088       if (something_has_changed)
10089       {
10090         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10091         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10092       }
10093     }
10094   }
10095   else
10096   {
10097     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10098                                         ce_value, ce_score);
10099
10100     if (element == EL_DIAGONAL_GROWING ||
10101         element == EL_DIAGONAL_SHRINKING)
10102     {
10103       target_element = Store[x][y];
10104
10105       Store[x][y] = EL_EMPTY;
10106     }
10107
10108     CreateElementFromChange(x, y, target_element);
10109
10110     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10111     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10112   }
10113
10114   /* this uses direct change before indirect change */
10115   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10116
10117   return TRUE;
10118 }
10119
10120 #if USE_NEW_DELAYED_ACTION
10121
10122 static void HandleElementChange(int x, int y, int page)
10123 {
10124   int element = MovingOrBlocked2Element(x, y);
10125   struct ElementInfo *ei = &element_info[element];
10126   struct ElementChangeInfo *change = &ei->change_page[page];
10127
10128 #ifdef DEBUG
10129   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10130       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10131   {
10132     printf("\n\n");
10133     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10134            x, y, element, element_info[element].token_name);
10135     printf("HandleElementChange(): This should never happen!\n");
10136     printf("\n\n");
10137   }
10138 #endif
10139
10140   /* this can happen with classic bombs on walkable, changing elements */
10141   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10142   {
10143 #if 0
10144     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10145       ChangeDelay[x][y] = 0;
10146 #endif
10147
10148     return;
10149   }
10150
10151   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10152   {
10153     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10154
10155     if (change->can_change)
10156     {
10157 #if 1
10158       /* !!! not clear why graphic animation should be reset at all here !!! */
10159       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10160 #if USE_GFX_RESET_WHEN_NOT_MOVING
10161       /* when a custom element is about to change (for example by change delay),
10162          do not reset graphic animation when the custom element is moving */
10163       if (!IS_MOVING(x, y))
10164 #endif
10165       {
10166         ResetGfxAnimation(x, y);
10167         ResetRandomAnimationValue(x, y);
10168       }
10169 #endif
10170
10171       if (change->pre_change_function)
10172         change->pre_change_function(x, y);
10173     }
10174   }
10175
10176   ChangeDelay[x][y]--;
10177
10178   if (ChangeDelay[x][y] != 0)           /* continue element change */
10179   {
10180     if (change->can_change)
10181     {
10182       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10183
10184       if (IS_ANIMATED(graphic))
10185         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10186
10187       if (change->change_function)
10188         change->change_function(x, y);
10189     }
10190   }
10191   else                                  /* finish element change */
10192   {
10193     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10194     {
10195       page = ChangePage[x][y];
10196       ChangePage[x][y] = -1;
10197
10198       change = &ei->change_page[page];
10199     }
10200
10201     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10202     {
10203       ChangeDelay[x][y] = 1;            /* try change after next move step */
10204       ChangePage[x][y] = page;          /* remember page to use for change */
10205
10206       return;
10207     }
10208
10209     if (change->can_change)
10210     {
10211       if (ChangeElement(x, y, element, page))
10212       {
10213         if (change->post_change_function)
10214           change->post_change_function(x, y);
10215       }
10216     }
10217
10218     if (change->has_action)
10219       ExecuteCustomElementAction(x, y, element, page);
10220   }
10221 }
10222
10223 #else
10224
10225 static void HandleElementChange(int x, int y, int page)
10226 {
10227   int element = MovingOrBlocked2Element(x, y);
10228   struct ElementInfo *ei = &element_info[element];
10229   struct ElementChangeInfo *change = &ei->change_page[page];
10230
10231 #ifdef DEBUG
10232   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10233   {
10234     printf("\n\n");
10235     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10236            x, y, element, element_info[element].token_name);
10237     printf("HandleElementChange(): This should never happen!\n");
10238     printf("\n\n");
10239   }
10240 #endif
10241
10242   /* this can happen with classic bombs on walkable, changing elements */
10243   if (!CAN_CHANGE(element))
10244   {
10245 #if 0
10246     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10247       ChangeDelay[x][y] = 0;
10248 #endif
10249
10250     return;
10251   }
10252
10253   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10254   {
10255     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10256
10257     ResetGfxAnimation(x, y);
10258     ResetRandomAnimationValue(x, y);
10259
10260     if (change->pre_change_function)
10261       change->pre_change_function(x, y);
10262   }
10263
10264   ChangeDelay[x][y]--;
10265
10266   if (ChangeDelay[x][y] != 0)           /* continue element change */
10267   {
10268     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10269
10270     if (IS_ANIMATED(graphic))
10271       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10272
10273     if (change->change_function)
10274       change->change_function(x, y);
10275   }
10276   else                                  /* finish element change */
10277   {
10278     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10279     {
10280       page = ChangePage[x][y];
10281       ChangePage[x][y] = -1;
10282
10283       change = &ei->change_page[page];
10284     }
10285
10286     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10287     {
10288       ChangeDelay[x][y] = 1;            /* try change after next move step */
10289       ChangePage[x][y] = page;          /* remember page to use for change */
10290
10291       return;
10292     }
10293
10294     if (ChangeElement(x, y, element, page))
10295     {
10296       if (change->post_change_function)
10297         change->post_change_function(x, y);
10298     }
10299   }
10300 }
10301
10302 #endif
10303
10304 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10305                                               int trigger_element,
10306                                               int trigger_event,
10307                                               int trigger_player,
10308                                               int trigger_side,
10309                                               int trigger_page)
10310 {
10311   boolean change_done_any = FALSE;
10312   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10313   int i;
10314
10315   if (!(trigger_events[trigger_element][trigger_event]))
10316     return FALSE;
10317
10318 #if 0
10319   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10320          trigger_event, recursion_loop_depth, recursion_loop_detected,
10321          recursion_loop_element, EL_NAME(recursion_loop_element));
10322 #endif
10323
10324   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10325
10326   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10327   {
10328     int element = EL_CUSTOM_START + i;
10329     boolean change_done = FALSE;
10330     int p;
10331
10332     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10333         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10334       continue;
10335
10336     for (p = 0; p < element_info[element].num_change_pages; p++)
10337     {
10338       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10339
10340       if (change->can_change_or_has_action &&
10341           change->has_event[trigger_event] &&
10342           change->trigger_side & trigger_side &&
10343           change->trigger_player & trigger_player &&
10344           change->trigger_page & trigger_page_bits &&
10345           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10346       {
10347         change->actual_trigger_element = trigger_element;
10348         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10349         change->actual_trigger_side = trigger_side;
10350         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10351         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10352
10353         if ((change->can_change && !change_done) || change->has_action)
10354         {
10355           int x, y;
10356
10357           SCAN_PLAYFIELD(x, y)
10358           {
10359             if (Feld[x][y] == element)
10360             {
10361               if (change->can_change && !change_done)
10362               {
10363                 ChangeDelay[x][y] = 1;
10364                 ChangeEvent[x][y] = trigger_event;
10365
10366                 HandleElementChange(x, y, p);
10367               }
10368 #if USE_NEW_DELAYED_ACTION
10369               else if (change->has_action)
10370               {
10371                 ExecuteCustomElementAction(x, y, element, p);
10372                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10373               }
10374 #else
10375               if (change->has_action)
10376               {
10377                 ExecuteCustomElementAction(x, y, element, p);
10378                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10379               }
10380 #endif
10381             }
10382           }
10383
10384           if (change->can_change)
10385           {
10386             change_done = TRUE;
10387             change_done_any = TRUE;
10388           }
10389         }
10390       }
10391     }
10392   }
10393
10394   RECURSION_LOOP_DETECTION_END();
10395
10396   return change_done_any;
10397 }
10398
10399 static boolean CheckElementChangeExt(int x, int y,
10400                                      int element,
10401                                      int trigger_element,
10402                                      int trigger_event,
10403                                      int trigger_player,
10404                                      int trigger_side)
10405 {
10406   boolean change_done = FALSE;
10407   int p;
10408
10409   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10411     return FALSE;
10412
10413   if (Feld[x][y] == EL_BLOCKED)
10414   {
10415     Blocked2Moving(x, y, &x, &y);
10416     element = Feld[x][y];
10417   }
10418
10419 #if 0
10420   /* check if element has already changed */
10421   if (Feld[x][y] != element)
10422     return FALSE;
10423 #else
10424   /* check if element has already changed or is about to change after moving */
10425   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10426        Feld[x][y] != element) ||
10427
10428       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10429        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10430         ChangePage[x][y] != -1)))
10431     return FALSE;
10432 #endif
10433
10434 #if 0
10435   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10436          trigger_event, recursion_loop_depth, recursion_loop_detected,
10437          recursion_loop_element, EL_NAME(recursion_loop_element));
10438 #endif
10439
10440   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10441
10442   for (p = 0; p < element_info[element].num_change_pages; p++)
10443   {
10444     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10445
10446     /* check trigger element for all events where the element that is checked
10447        for changing interacts with a directly adjacent element -- this is
10448        different to element changes that affect other elements to change on the
10449        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10450     boolean check_trigger_element =
10451       (trigger_event == CE_TOUCHING_X ||
10452        trigger_event == CE_HITTING_X ||
10453        trigger_event == CE_HIT_BY_X ||
10454 #if 1
10455        /* this one was forgotten until 3.2.3 */
10456        trigger_event == CE_DIGGING_X);
10457 #endif
10458
10459     if (change->can_change_or_has_action &&
10460         change->has_event[trigger_event] &&
10461         change->trigger_side & trigger_side &&
10462         change->trigger_player & trigger_player &&
10463         (!check_trigger_element ||
10464          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10465     {
10466       change->actual_trigger_element = trigger_element;
10467       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10468       change->actual_trigger_side = trigger_side;
10469       change->actual_trigger_ce_value = CustomValue[x][y];
10470       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10471
10472       /* special case: trigger element not at (x,y) position for some events */
10473       if (check_trigger_element)
10474       {
10475         static struct
10476         {
10477           int dx, dy;
10478         } move_xy[] =
10479           {
10480             {  0,  0 },
10481             { -1,  0 },
10482             { +1,  0 },
10483             {  0,  0 },
10484             {  0, -1 },
10485             {  0,  0 }, { 0, 0 }, { 0, 0 },
10486             {  0, +1 }
10487           };
10488
10489         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10490         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10491
10492         change->actual_trigger_ce_value = CustomValue[xx][yy];
10493         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10494       }
10495
10496       if (change->can_change && !change_done)
10497       {
10498         ChangeDelay[x][y] = 1;
10499         ChangeEvent[x][y] = trigger_event;
10500
10501         HandleElementChange(x, y, p);
10502
10503         change_done = TRUE;
10504       }
10505 #if USE_NEW_DELAYED_ACTION
10506       else if (change->has_action)
10507       {
10508         ExecuteCustomElementAction(x, y, element, p);
10509         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10510       }
10511 #else
10512       if (change->has_action)
10513       {
10514         ExecuteCustomElementAction(x, y, element, p);
10515         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10516       }
10517 #endif
10518     }
10519   }
10520
10521   RECURSION_LOOP_DETECTION_END();
10522
10523   return change_done;
10524 }
10525
10526 static void PlayPlayerSound(struct PlayerInfo *player)
10527 {
10528   int jx = player->jx, jy = player->jy;
10529   int sound_element = player->artwork_element;
10530   int last_action = player->last_action_waiting;
10531   int action = player->action_waiting;
10532
10533   if (player->is_waiting)
10534   {
10535     if (action != last_action)
10536       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10537     else
10538       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10539   }
10540   else
10541   {
10542     if (action != last_action)
10543       StopSound(element_info[sound_element].sound[last_action]);
10544
10545     if (last_action == ACTION_SLEEPING)
10546       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10547   }
10548 }
10549
10550 static void PlayAllPlayersSound()
10551 {
10552   int i;
10553
10554   for (i = 0; i < MAX_PLAYERS; i++)
10555     if (stored_player[i].active)
10556       PlayPlayerSound(&stored_player[i]);
10557 }
10558
10559 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10560 {
10561   boolean last_waiting = player->is_waiting;
10562   int move_dir = player->MovDir;
10563
10564   player->dir_waiting = move_dir;
10565   player->last_action_waiting = player->action_waiting;
10566
10567   if (is_waiting)
10568   {
10569     if (!last_waiting)          /* not waiting -> waiting */
10570     {
10571       player->is_waiting = TRUE;
10572
10573       player->frame_counter_bored =
10574         FrameCounter +
10575         game.player_boring_delay_fixed +
10576         GetSimpleRandom(game.player_boring_delay_random);
10577       player->frame_counter_sleeping =
10578         FrameCounter +
10579         game.player_sleeping_delay_fixed +
10580         GetSimpleRandom(game.player_sleeping_delay_random);
10581
10582       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10583     }
10584
10585     if (game.player_sleeping_delay_fixed +
10586         game.player_sleeping_delay_random > 0 &&
10587         player->anim_delay_counter == 0 &&
10588         player->post_delay_counter == 0 &&
10589         FrameCounter >= player->frame_counter_sleeping)
10590       player->is_sleeping = TRUE;
10591     else if (game.player_boring_delay_fixed +
10592              game.player_boring_delay_random > 0 &&
10593              FrameCounter >= player->frame_counter_bored)
10594       player->is_bored = TRUE;
10595
10596     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10597                               player->is_bored ? ACTION_BORING :
10598                               ACTION_WAITING);
10599
10600     if (player->is_sleeping && player->use_murphy)
10601     {
10602       /* special case for sleeping Murphy when leaning against non-free tile */
10603
10604       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10605           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10606            !IS_MOVING(player->jx - 1, player->jy)))
10607         move_dir = MV_LEFT;
10608       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10609                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10610                 !IS_MOVING(player->jx + 1, player->jy)))
10611         move_dir = MV_RIGHT;
10612       else
10613         player->is_sleeping = FALSE;
10614
10615       player->dir_waiting = move_dir;
10616     }
10617
10618     if (player->is_sleeping)
10619     {
10620       if (player->num_special_action_sleeping > 0)
10621       {
10622         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10623         {
10624           int last_special_action = player->special_action_sleeping;
10625           int num_special_action = player->num_special_action_sleeping;
10626           int special_action =
10627             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10628              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10629              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10630              last_special_action + 1 : ACTION_SLEEPING);
10631           int special_graphic =
10632             el_act_dir2img(player->artwork_element, special_action, move_dir);
10633
10634           player->anim_delay_counter =
10635             graphic_info[special_graphic].anim_delay_fixed +
10636             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10637           player->post_delay_counter =
10638             graphic_info[special_graphic].post_delay_fixed +
10639             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10640
10641           player->special_action_sleeping = special_action;
10642         }
10643
10644         if (player->anim_delay_counter > 0)
10645         {
10646           player->action_waiting = player->special_action_sleeping;
10647           player->anim_delay_counter--;
10648         }
10649         else if (player->post_delay_counter > 0)
10650         {
10651           player->post_delay_counter--;
10652         }
10653       }
10654     }
10655     else if (player->is_bored)
10656     {
10657       if (player->num_special_action_bored > 0)
10658       {
10659         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10660         {
10661           int special_action =
10662             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10663           int special_graphic =
10664             el_act_dir2img(player->artwork_element, special_action, move_dir);
10665
10666           player->anim_delay_counter =
10667             graphic_info[special_graphic].anim_delay_fixed +
10668             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10669           player->post_delay_counter =
10670             graphic_info[special_graphic].post_delay_fixed +
10671             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10672
10673           player->special_action_bored = special_action;
10674         }
10675
10676         if (player->anim_delay_counter > 0)
10677         {
10678           player->action_waiting = player->special_action_bored;
10679           player->anim_delay_counter--;
10680         }
10681         else if (player->post_delay_counter > 0)
10682         {
10683           player->post_delay_counter--;
10684         }
10685       }
10686     }
10687   }
10688   else if (last_waiting)        /* waiting -> not waiting */
10689   {
10690     player->is_waiting = FALSE;
10691     player->is_bored = FALSE;
10692     player->is_sleeping = FALSE;
10693
10694     player->frame_counter_bored = -1;
10695     player->frame_counter_sleeping = -1;
10696
10697     player->anim_delay_counter = 0;
10698     player->post_delay_counter = 0;
10699
10700     player->dir_waiting = player->MovDir;
10701     player->action_waiting = ACTION_DEFAULT;
10702
10703     player->special_action_bored = ACTION_DEFAULT;
10704     player->special_action_sleeping = ACTION_DEFAULT;
10705   }
10706 }
10707
10708 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10709 {
10710   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10711   int left      = player_action & JOY_LEFT;
10712   int right     = player_action & JOY_RIGHT;
10713   int up        = player_action & JOY_UP;
10714   int down      = player_action & JOY_DOWN;
10715   int button1   = player_action & JOY_BUTTON_1;
10716   int button2   = player_action & JOY_BUTTON_2;
10717   int dx        = (left ? -1 : right ? 1 : 0);
10718   int dy        = (up   ? -1 : down  ? 1 : 0);
10719
10720   if (!player->active || tape.pausing)
10721     return 0;
10722
10723   if (player_action)
10724   {
10725     if (button1)
10726       snapped = SnapField(player, dx, dy);
10727     else
10728     {
10729       if (button2)
10730         dropped = DropElement(player);
10731
10732       moved = MovePlayer(player, dx, dy);
10733     }
10734
10735     if (tape.single_step && tape.recording && !tape.pausing)
10736     {
10737       if (button1 || (dropped && !moved))
10738       {
10739         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10740         SnapField(player, 0, 0);                /* stop snapping */
10741       }
10742     }
10743
10744     SetPlayerWaiting(player, FALSE);
10745
10746     return player_action;
10747   }
10748   else
10749   {
10750     /* no actions for this player (no input at player's configured device) */
10751
10752     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10753     SnapField(player, 0, 0);
10754     CheckGravityMovementWhenNotMoving(player);
10755
10756     if (player->MovPos == 0)
10757       SetPlayerWaiting(player, TRUE);
10758
10759     if (player->MovPos == 0)    /* needed for tape.playing */
10760       player->is_moving = FALSE;
10761
10762     player->is_dropping = FALSE;
10763     player->is_dropping_pressed = FALSE;
10764     player->drop_pressed_delay = 0;
10765
10766     return 0;
10767   }
10768 }
10769
10770 static void CheckLevelTime()
10771 {
10772   int i;
10773
10774   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10775   {
10776     if (level.native_em_level->lev->home == 0)  /* all players at home */
10777     {
10778       PlayerWins(local_player);
10779
10780       AllPlayersGone = TRUE;
10781
10782       level.native_em_level->lev->home = -1;
10783     }
10784
10785     if (level.native_em_level->ply[0]->alive == 0 &&
10786         level.native_em_level->ply[1]->alive == 0 &&
10787         level.native_em_level->ply[2]->alive == 0 &&
10788         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10789       AllPlayersGone = TRUE;
10790   }
10791
10792   if (TimeFrames >= FRAMES_PER_SECOND)
10793   {
10794     TimeFrames = 0;
10795     TapeTime++;
10796
10797     for (i = 0; i < MAX_PLAYERS; i++)
10798     {
10799       struct PlayerInfo *player = &stored_player[i];
10800
10801       if (SHIELD_ON(player))
10802       {
10803         player->shield_normal_time_left--;
10804
10805         if (player->shield_deadly_time_left > 0)
10806           player->shield_deadly_time_left--;
10807       }
10808     }
10809
10810     if (!local_player->LevelSolved && !level.use_step_counter)
10811     {
10812       TimePlayed++;
10813
10814       if (TimeLeft > 0)
10815       {
10816         TimeLeft--;
10817
10818         if (TimeLeft <= 10 && setup.time_limit)
10819           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10820
10821 #if 1
10822         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10823
10824         DisplayGameControlValues();
10825 #else
10826         DrawGameValue_Time(TimeLeft);
10827 #endif
10828
10829         if (!TimeLeft && setup.time_limit)
10830         {
10831           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10832             level.native_em_level->lev->killed_out_of_time = TRUE;
10833           else
10834             for (i = 0; i < MAX_PLAYERS; i++)
10835               KillPlayer(&stored_player[i]);
10836         }
10837       }
10838 #if 1
10839       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10840       {
10841         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10842
10843         DisplayGameControlValues();
10844       }
10845 #else
10846       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10847         DrawGameValue_Time(TimePlayed);
10848 #endif
10849
10850       level.native_em_level->lev->time =
10851         (level.time == 0 ? TimePlayed : TimeLeft);
10852     }
10853
10854     if (tape.recording || tape.playing)
10855       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10856   }
10857
10858   DrawGameDoorValues();
10859 }
10860
10861 void AdvanceFrameAndPlayerCounters(int player_nr)
10862 {
10863   int i;
10864
10865   /* advance frame counters (global frame counter and time frame counter) */
10866   FrameCounter++;
10867   TimeFrames++;
10868
10869   /* advance player counters (counters for move delay, move animation etc.) */
10870   for (i = 0; i < MAX_PLAYERS; i++)
10871   {
10872     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10873     int move_delay_value = stored_player[i].move_delay_value;
10874     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10875
10876     if (!advance_player_counters)       /* not all players may be affected */
10877       continue;
10878
10879 #if USE_NEW_PLAYER_ANIM
10880     if (move_frames == 0)       /* less than one move per game frame */
10881     {
10882       int stepsize = TILEX / move_delay_value;
10883       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10884       int count = (stored_player[i].is_moving ?
10885                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10886
10887       if (count % delay == 0)
10888         move_frames = 1;
10889     }
10890 #endif
10891
10892     stored_player[i].Frame += move_frames;
10893
10894     if (stored_player[i].MovPos != 0)
10895       stored_player[i].StepFrame += move_frames;
10896
10897     if (stored_player[i].move_delay > 0)
10898       stored_player[i].move_delay--;
10899
10900     /* due to bugs in previous versions, counter must count up, not down */
10901     if (stored_player[i].push_delay != -1)
10902       stored_player[i].push_delay++;
10903
10904     if (stored_player[i].drop_delay > 0)
10905       stored_player[i].drop_delay--;
10906
10907     if (stored_player[i].is_dropping_pressed)
10908       stored_player[i].drop_pressed_delay++;
10909   }
10910 }
10911
10912 void StartGameActions(boolean init_network_game, boolean record_tape,
10913                       long random_seed)
10914 {
10915   unsigned long new_random_seed = InitRND(random_seed);
10916
10917   if (record_tape)
10918     TapeStartRecording(new_random_seed);
10919
10920 #if defined(NETWORK_AVALIABLE)
10921   if (init_network_game)
10922   {
10923     SendToServer_StartPlaying();
10924
10925     return;
10926   }
10927 #endif
10928
10929   InitGame();
10930 }
10931
10932 void GameActions()
10933 {
10934   static unsigned long game_frame_delay = 0;
10935   unsigned long game_frame_delay_value;
10936   byte *recorded_player_action;
10937   byte summarized_player_action = 0;
10938   byte tape_action[MAX_PLAYERS];
10939   int i;
10940
10941   /* detect endless loops, caused by custom element programming */
10942   if (recursion_loop_detected && recursion_loop_depth == 0)
10943   {
10944     char *message = getStringCat3("Internal Error ! Element ",
10945                                   EL_NAME(recursion_loop_element),
10946                                   " caused endless loop ! Quit the game ?");
10947
10948     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10949           EL_NAME(recursion_loop_element));
10950
10951     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10952
10953     recursion_loop_detected = FALSE;    /* if game should be continued */
10954
10955     free(message);
10956
10957     return;
10958   }
10959
10960   if (game.restart_level)
10961     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10962
10963   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10964   {
10965     if (level.native_em_level->lev->home == 0)  /* all players at home */
10966     {
10967       PlayerWins(local_player);
10968
10969       AllPlayersGone = TRUE;
10970
10971       level.native_em_level->lev->home = -1;
10972     }
10973
10974     if (level.native_em_level->ply[0]->alive == 0 &&
10975         level.native_em_level->ply[1]->alive == 0 &&
10976         level.native_em_level->ply[2]->alive == 0 &&
10977         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10978       AllPlayersGone = TRUE;
10979   }
10980
10981   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10982     GameWon();
10983
10984   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10985     TapeStop();
10986
10987   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10988     return;
10989
10990   game_frame_delay_value =
10991     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10992
10993   if (tape.playing && tape.warp_forward && !tape.pausing)
10994     game_frame_delay_value = 0;
10995
10996   /* ---------- main game synchronization point ---------- */
10997
10998   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10999
11000   if (network_playing && !network_player_action_received)
11001   {
11002     /* try to get network player actions in time */
11003
11004 #if defined(NETWORK_AVALIABLE)
11005     /* last chance to get network player actions without main loop delay */
11006     HandleNetworking();
11007 #endif
11008
11009     /* game was quit by network peer */
11010     if (game_status != GAME_MODE_PLAYING)
11011       return;
11012
11013     if (!network_player_action_received)
11014       return;           /* failed to get network player actions in time */
11015
11016     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11017   }
11018
11019   if (tape.pausing)
11020     return;
11021
11022   /* at this point we know that we really continue executing the game */
11023
11024   network_player_action_received = FALSE;
11025
11026   /* when playing tape, read previously recorded player input from tape data */
11027   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11028
11029 #if 1
11030   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11031   if (tape.pausing)
11032     return;
11033 #endif
11034
11035   if (tape.set_centered_player)
11036   {
11037     game.centered_player_nr_next = tape.centered_player_nr_next;
11038     game.set_centered_player = TRUE;
11039   }
11040
11041   for (i = 0; i < MAX_PLAYERS; i++)
11042   {
11043     summarized_player_action |= stored_player[i].action;
11044
11045     if (!network_playing)
11046       stored_player[i].effective_action = stored_player[i].action;
11047   }
11048
11049 #if defined(NETWORK_AVALIABLE)
11050   if (network_playing)
11051     SendToServer_MovePlayer(summarized_player_action);
11052 #endif
11053
11054   if (!options.network && !setup.team_mode)
11055     local_player->effective_action = summarized_player_action;
11056
11057   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11058   {
11059     for (i = 0; i < MAX_PLAYERS; i++)
11060       stored_player[i].effective_action =
11061         (i == game.centered_player_nr ? summarized_player_action : 0);
11062   }
11063
11064   if (recorded_player_action != NULL)
11065     for (i = 0; i < MAX_PLAYERS; i++)
11066       stored_player[i].effective_action = recorded_player_action[i];
11067
11068   for (i = 0; i < MAX_PLAYERS; i++)
11069   {
11070     tape_action[i] = stored_player[i].effective_action;
11071
11072     /* (this can only happen in the R'n'D game engine) */
11073     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11074       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11075   }
11076
11077   /* only record actions from input devices, but not programmed actions */
11078   if (tape.recording)
11079     TapeRecordAction(tape_action);
11080
11081   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11082   {
11083     GameActions_EM_Main();
11084   }
11085   else
11086   {
11087     GameActions_RND();
11088   }
11089 }
11090
11091 void GameActions_EM_Main()
11092 {
11093   byte effective_action[MAX_PLAYERS];
11094   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11095   int i;
11096
11097   for (i = 0; i < MAX_PLAYERS; i++)
11098     effective_action[i] = stored_player[i].effective_action;
11099
11100   GameActions_EM(effective_action, warp_mode);
11101
11102   CheckLevelTime();
11103
11104   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11105 }
11106
11107 void GameActions_RND()
11108 {
11109   int magic_wall_x = 0, magic_wall_y = 0;
11110   int i, x, y, element, graphic;
11111
11112   InitPlayfieldScanModeVars();
11113
11114 #if USE_ONE_MORE_CHANGE_PER_FRAME
11115   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11116   {
11117     SCAN_PLAYFIELD(x, y)
11118     {
11119       ChangeCount[x][y] = 0;
11120       ChangeEvent[x][y] = -1;
11121     }
11122   }
11123 #endif
11124
11125   if (game.set_centered_player)
11126   {
11127     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11128
11129     /* switching to "all players" only possible if all players fit to screen */
11130     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11131     {
11132       game.centered_player_nr_next = game.centered_player_nr;
11133       game.set_centered_player = FALSE;
11134     }
11135
11136     /* do not switch focus to non-existing (or non-active) player */
11137     if (game.centered_player_nr_next >= 0 &&
11138         !stored_player[game.centered_player_nr_next].active)
11139     {
11140       game.centered_player_nr_next = game.centered_player_nr;
11141       game.set_centered_player = FALSE;
11142     }
11143   }
11144
11145   if (game.set_centered_player &&
11146       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11147   {
11148     int sx, sy;
11149
11150     if (game.centered_player_nr_next == -1)
11151     {
11152       setScreenCenteredToAllPlayers(&sx, &sy);
11153     }
11154     else
11155     {
11156       sx = stored_player[game.centered_player_nr_next].jx;
11157       sy = stored_player[game.centered_player_nr_next].jy;
11158     }
11159
11160     game.centered_player_nr = game.centered_player_nr_next;
11161     game.set_centered_player = FALSE;
11162
11163     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11164     DrawGameDoorValues();
11165   }
11166
11167   for (i = 0; i < MAX_PLAYERS; i++)
11168   {
11169     int actual_player_action = stored_player[i].effective_action;
11170
11171 #if 1
11172     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11173        - rnd_equinox_tetrachloride 048
11174        - rnd_equinox_tetrachloride_ii 096
11175        - rnd_emanuel_schmieg 002
11176        - doctor_sloan_ww 001, 020
11177     */
11178     if (stored_player[i].MovPos == 0)
11179       CheckGravityMovement(&stored_player[i]);
11180 #endif
11181
11182     /* overwrite programmed action with tape action */
11183     if (stored_player[i].programmed_action)
11184       actual_player_action = stored_player[i].programmed_action;
11185
11186     PlayerActions(&stored_player[i], actual_player_action);
11187
11188     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11189   }
11190
11191   ScrollScreen(NULL, SCROLL_GO_ON);
11192
11193   /* for backwards compatibility, the following code emulates a fixed bug that
11194      occured when pushing elements (causing elements that just made their last
11195      pushing step to already (if possible) make their first falling step in the
11196      same game frame, which is bad); this code is also needed to use the famous
11197      "spring push bug" which is used in older levels and might be wanted to be
11198      used also in newer levels, but in this case the buggy pushing code is only
11199      affecting the "spring" element and no other elements */
11200
11201   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11202   {
11203     for (i = 0; i < MAX_PLAYERS; i++)
11204     {
11205       struct PlayerInfo *player = &stored_player[i];
11206       int x = player->jx;
11207       int y = player->jy;
11208
11209       if (player->active && player->is_pushing && player->is_moving &&
11210           IS_MOVING(x, y) &&
11211           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11212            Feld[x][y] == EL_SPRING))
11213       {
11214         ContinueMoving(x, y);
11215
11216         /* continue moving after pushing (this is actually a bug) */
11217         if (!IS_MOVING(x, y))
11218           Stop[x][y] = FALSE;
11219       }
11220     }
11221   }
11222
11223 #if 0
11224   debug_print_timestamp(0, "start main loop profiling");
11225 #endif
11226
11227   SCAN_PLAYFIELD(x, y)
11228   {
11229     ChangeCount[x][y] = 0;
11230     ChangeEvent[x][y] = -1;
11231
11232     /* this must be handled before main playfield loop */
11233     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11234     {
11235       MovDelay[x][y]--;
11236       if (MovDelay[x][y] <= 0)
11237         RemoveField(x, y);
11238     }
11239
11240 #if USE_NEW_SNAP_DELAY
11241     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11242     {
11243       MovDelay[x][y]--;
11244       if (MovDelay[x][y] <= 0)
11245       {
11246         RemoveField(x, y);
11247         DrawLevelField(x, y);
11248
11249         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11250       }
11251     }
11252 #endif
11253
11254 #if DEBUG
11255     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11256     {
11257       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11258       printf("GameActions(): This should never happen!\n");
11259
11260       ChangePage[x][y] = -1;
11261     }
11262 #endif
11263
11264     Stop[x][y] = FALSE;
11265     if (WasJustMoving[x][y] > 0)
11266       WasJustMoving[x][y]--;
11267     if (WasJustFalling[x][y] > 0)
11268       WasJustFalling[x][y]--;
11269     if (CheckCollision[x][y] > 0)
11270       CheckCollision[x][y]--;
11271     if (CheckImpact[x][y] > 0)
11272       CheckImpact[x][y]--;
11273
11274     GfxFrame[x][y]++;
11275
11276     /* reset finished pushing action (not done in ContinueMoving() to allow
11277        continuous pushing animation for elements with zero push delay) */
11278     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11279     {
11280       ResetGfxAnimation(x, y);
11281       DrawLevelField(x, y);
11282     }
11283
11284 #if DEBUG
11285     if (IS_BLOCKED(x, y))
11286     {
11287       int oldx, oldy;
11288
11289       Blocked2Moving(x, y, &oldx, &oldy);
11290       if (!IS_MOVING(oldx, oldy))
11291       {
11292         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11293         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11294         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11295         printf("GameActions(): This should never happen!\n");
11296       }
11297     }
11298 #endif
11299   }
11300
11301 #if 0
11302   debug_print_timestamp(0, "- time for pre-main loop:");
11303 #endif
11304
11305 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11306   SCAN_PLAYFIELD(x, y)
11307   {
11308     element = Feld[x][y];
11309     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11310
11311 #if 1
11312     {
11313 #if 1
11314       int element2 = element;
11315       int graphic2 = graphic;
11316 #else
11317       int element2 = Feld[x][y];
11318       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11319 #endif
11320       int last_gfx_frame = GfxFrame[x][y];
11321
11322       if (graphic_info[graphic2].anim_global_sync)
11323         GfxFrame[x][y] = FrameCounter;
11324       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11325         GfxFrame[x][y] = CustomValue[x][y];
11326       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11327         GfxFrame[x][y] = element_info[element2].collect_score;
11328       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11329         GfxFrame[x][y] = ChangeDelay[x][y];
11330
11331       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11332         DrawLevelGraphicAnimation(x, y, graphic2);
11333     }
11334 #else
11335     ResetGfxFrame(x, y, TRUE);
11336 #endif
11337
11338 #if 1
11339     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11340         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11341       ResetRandomAnimationValue(x, y);
11342 #endif
11343
11344 #if 1
11345     SetRandomAnimationValue(x, y);
11346 #endif
11347
11348 #if 1
11349     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11350 #endif
11351   }
11352 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11353
11354 #if 0
11355   debug_print_timestamp(0, "- time for TEST loop:     -->");
11356 #endif
11357
11358   SCAN_PLAYFIELD(x, y)
11359   {
11360     element = Feld[x][y];
11361     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11362
11363     ResetGfxFrame(x, y, TRUE);
11364
11365     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11366         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11367       ResetRandomAnimationValue(x, y);
11368
11369     SetRandomAnimationValue(x, y);
11370
11371     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11372
11373     if (IS_INACTIVE(element))
11374     {
11375       if (IS_ANIMATED(graphic))
11376         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11377
11378       continue;
11379     }
11380
11381     /* this may take place after moving, so 'element' may have changed */
11382     if (IS_CHANGING(x, y) &&
11383         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11384     {
11385       int page = element_info[element].event_page_nr[CE_DELAY];
11386
11387 #if 1
11388       HandleElementChange(x, y, page);
11389 #else
11390       if (CAN_CHANGE(element))
11391         HandleElementChange(x, y, page);
11392
11393       if (HAS_ACTION(element))
11394         ExecuteCustomElementAction(x, y, element, page);
11395 #endif
11396
11397       element = Feld[x][y];
11398       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11399     }
11400
11401 #if 0   // ---------------------------------------------------------------------
11402
11403     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11404     {
11405       StartMoving(x, y);
11406
11407       element = Feld[x][y];
11408       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11409
11410       if (IS_ANIMATED(graphic) &&
11411           !IS_MOVING(x, y) &&
11412           !Stop[x][y])
11413         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11414
11415       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11416         DrawTwinkleOnField(x, y);
11417     }
11418     else if (IS_MOVING(x, y))
11419       ContinueMoving(x, y);
11420     else
11421     {
11422       switch (element)
11423       {
11424         case EL_ACID:
11425         case EL_EXIT_OPEN:
11426         case EL_EM_EXIT_OPEN:
11427         case EL_SP_EXIT_OPEN:
11428         case EL_STEEL_EXIT_OPEN:
11429         case EL_EM_STEEL_EXIT_OPEN:
11430         case EL_SP_TERMINAL:
11431         case EL_SP_TERMINAL_ACTIVE:
11432         case EL_EXTRA_TIME:
11433         case EL_SHIELD_NORMAL:
11434         case EL_SHIELD_DEADLY:
11435           if (IS_ANIMATED(graphic))
11436             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11437           break;
11438
11439         case EL_DYNAMITE_ACTIVE:
11440         case EL_EM_DYNAMITE_ACTIVE:
11441         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11442         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11443         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11444         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11445         case EL_SP_DISK_RED_ACTIVE:
11446           CheckDynamite(x, y);
11447           break;
11448
11449         case EL_AMOEBA_GROWING:
11450           AmoebeWaechst(x, y);
11451           break;
11452
11453         case EL_AMOEBA_SHRINKING:
11454           AmoebaDisappearing(x, y);
11455           break;
11456
11457 #if !USE_NEW_AMOEBA_CODE
11458         case EL_AMOEBA_WET:
11459         case EL_AMOEBA_DRY:
11460         case EL_AMOEBA_FULL:
11461         case EL_BD_AMOEBA:
11462         case EL_EMC_DRIPPER:
11463           AmoebeAbleger(x, y);
11464           break;
11465 #endif
11466
11467         case EL_GAME_OF_LIFE:
11468         case EL_BIOMAZE:
11469           Life(x, y);
11470           break;
11471
11472         case EL_EXIT_CLOSED:
11473           CheckExit(x, y);
11474           break;
11475
11476         case EL_EM_EXIT_CLOSED:
11477           CheckExitEM(x, y);
11478           break;
11479
11480         case EL_STEEL_EXIT_CLOSED:
11481           CheckExitSteel(x, y);
11482           break;
11483
11484         case EL_EM_STEEL_EXIT_CLOSED:
11485           CheckExitSteelEM(x, y);
11486           break;
11487
11488         case EL_SP_EXIT_CLOSED:
11489           CheckExitSP(x, y);
11490           break;
11491
11492         case EL_EXPANDABLE_WALL_GROWING:
11493         case EL_EXPANDABLE_STEELWALL_GROWING:
11494           MauerWaechst(x, y);
11495           break;
11496
11497         case EL_EXPANDABLE_WALL:
11498         case EL_EXPANDABLE_WALL_HORIZONTAL:
11499         case EL_EXPANDABLE_WALL_VERTICAL:
11500         case EL_EXPANDABLE_WALL_ANY:
11501         case EL_BD_EXPANDABLE_WALL:
11502           MauerAbleger(x, y);
11503           break;
11504
11505         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11506         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11507         case EL_EXPANDABLE_STEELWALL_ANY:
11508           MauerAblegerStahl(x, y);
11509           break;
11510
11511         case EL_FLAMES:
11512           CheckForDragon(x, y);
11513           break;
11514
11515         case EL_EXPLOSION:
11516           break;
11517
11518         case EL_ELEMENT_SNAPPING:
11519         case EL_DIAGONAL_SHRINKING:
11520         case EL_DIAGONAL_GROWING:
11521         {
11522           graphic =
11523             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11524
11525           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11526           break;
11527         }
11528
11529         default:
11530           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11531             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11532           break;
11533       }
11534     }
11535
11536 #else   // ---------------------------------------------------------------------
11537
11538     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11539     {
11540       StartMoving(x, y);
11541
11542       element = Feld[x][y];
11543       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11544
11545       if (IS_ANIMATED(graphic) &&
11546           !IS_MOVING(x, y) &&
11547           !Stop[x][y])
11548         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11549
11550       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11551         DrawTwinkleOnField(x, y);
11552     }
11553     else if ((element == EL_ACID ||
11554               element == EL_EXIT_OPEN ||
11555               element == EL_EM_EXIT_OPEN ||
11556               element == EL_SP_EXIT_OPEN ||
11557               element == EL_STEEL_EXIT_OPEN ||
11558               element == EL_EM_STEEL_EXIT_OPEN ||
11559               element == EL_SP_TERMINAL ||
11560               element == EL_SP_TERMINAL_ACTIVE ||
11561               element == EL_EXTRA_TIME ||
11562               element == EL_SHIELD_NORMAL ||
11563               element == EL_SHIELD_DEADLY) &&
11564              IS_ANIMATED(graphic))
11565       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11566     else if (IS_MOVING(x, y))
11567       ContinueMoving(x, y);
11568     else if (IS_ACTIVE_BOMB(element))
11569       CheckDynamite(x, y);
11570     else if (element == EL_AMOEBA_GROWING)
11571       AmoebeWaechst(x, y);
11572     else if (element == EL_AMOEBA_SHRINKING)
11573       AmoebaDisappearing(x, y);
11574
11575 #if !USE_NEW_AMOEBA_CODE
11576     else if (IS_AMOEBALIVE(element))
11577       AmoebeAbleger(x, y);
11578 #endif
11579
11580     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11581       Life(x, y);
11582     else if (element == EL_EXIT_CLOSED)
11583       CheckExit(x, y);
11584     else if (element == EL_EM_EXIT_CLOSED)
11585       CheckExitEM(x, y);
11586     else if (element == EL_STEEL_EXIT_CLOSED)
11587       CheckExitSteel(x, y);
11588     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11589       CheckExitSteelEM(x, y);
11590     else if (element == EL_SP_EXIT_CLOSED)
11591       CheckExitSP(x, y);
11592     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11593              element == EL_EXPANDABLE_STEELWALL_GROWING)
11594       MauerWaechst(x, y);
11595     else if (element == EL_EXPANDABLE_WALL ||
11596              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11597              element == EL_EXPANDABLE_WALL_VERTICAL ||
11598              element == EL_EXPANDABLE_WALL_ANY ||
11599              element == EL_BD_EXPANDABLE_WALL)
11600       MauerAbleger(x, y);
11601     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11602              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11603              element == EL_EXPANDABLE_STEELWALL_ANY)
11604       MauerAblegerStahl(x, y);
11605     else if (element == EL_FLAMES)
11606       CheckForDragon(x, y);
11607     else if (element == EL_EXPLOSION)
11608       ; /* drawing of correct explosion animation is handled separately */
11609     else if (element == EL_ELEMENT_SNAPPING ||
11610              element == EL_DIAGONAL_SHRINKING ||
11611              element == EL_DIAGONAL_GROWING)
11612     {
11613       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11614
11615       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11616     }
11617     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11618       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11619
11620 #endif  // ---------------------------------------------------------------------
11621
11622     if (IS_BELT_ACTIVE(element))
11623       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11624
11625     if (game.magic_wall_active)
11626     {
11627       int jx = local_player->jx, jy = local_player->jy;
11628
11629       /* play the element sound at the position nearest to the player */
11630       if ((element == EL_MAGIC_WALL_FULL ||
11631            element == EL_MAGIC_WALL_ACTIVE ||
11632            element == EL_MAGIC_WALL_EMPTYING ||
11633            element == EL_BD_MAGIC_WALL_FULL ||
11634            element == EL_BD_MAGIC_WALL_ACTIVE ||
11635            element == EL_BD_MAGIC_WALL_EMPTYING ||
11636            element == EL_DC_MAGIC_WALL_FULL ||
11637            element == EL_DC_MAGIC_WALL_ACTIVE ||
11638            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11639           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11640       {
11641         magic_wall_x = x;
11642         magic_wall_y = y;
11643       }
11644     }
11645   }
11646
11647 #if 0
11648   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11649 #endif
11650
11651 #if USE_NEW_AMOEBA_CODE
11652   /* new experimental amoeba growth stuff */
11653   if (!(FrameCounter % 8))
11654   {
11655     static unsigned long random = 1684108901;
11656
11657     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11658     {
11659       x = RND(lev_fieldx);
11660       y = RND(lev_fieldy);
11661       element = Feld[x][y];
11662
11663       if (!IS_PLAYER(x,y) &&
11664           (element == EL_EMPTY ||
11665            CAN_GROW_INTO(element) ||
11666            element == EL_QUICKSAND_EMPTY ||
11667            element == EL_QUICKSAND_FAST_EMPTY ||
11668            element == EL_ACID_SPLASH_LEFT ||
11669            element == EL_ACID_SPLASH_RIGHT))
11670       {
11671         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11672             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11673             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11674             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11675           Feld[x][y] = EL_AMOEBA_DROP;
11676       }
11677
11678       random = random * 129 + 1;
11679     }
11680   }
11681 #endif
11682
11683 #if 0
11684   if (game.explosions_delayed)
11685 #endif
11686   {
11687     game.explosions_delayed = FALSE;
11688
11689     SCAN_PLAYFIELD(x, y)
11690     {
11691       element = Feld[x][y];
11692
11693       if (ExplodeField[x][y])
11694         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11695       else if (element == EL_EXPLOSION)
11696         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11697
11698       ExplodeField[x][y] = EX_TYPE_NONE;
11699     }
11700
11701     game.explosions_delayed = TRUE;
11702   }
11703
11704   if (game.magic_wall_active)
11705   {
11706     if (!(game.magic_wall_time_left % 4))
11707     {
11708       int element = Feld[magic_wall_x][magic_wall_y];
11709
11710       if (element == EL_BD_MAGIC_WALL_FULL ||
11711           element == EL_BD_MAGIC_WALL_ACTIVE ||
11712           element == EL_BD_MAGIC_WALL_EMPTYING)
11713         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11714       else if (element == EL_DC_MAGIC_WALL_FULL ||
11715                element == EL_DC_MAGIC_WALL_ACTIVE ||
11716                element == EL_DC_MAGIC_WALL_EMPTYING)
11717         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11718       else
11719         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11720     }
11721
11722     if (game.magic_wall_time_left > 0)
11723     {
11724       game.magic_wall_time_left--;
11725       if (!game.magic_wall_time_left)
11726       {
11727         SCAN_PLAYFIELD(x, y)
11728         {
11729           element = Feld[x][y];
11730
11731           if (element == EL_MAGIC_WALL_ACTIVE ||
11732               element == EL_MAGIC_WALL_FULL)
11733           {
11734             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11735             DrawLevelField(x, y);
11736           }
11737           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11738                    element == EL_BD_MAGIC_WALL_FULL)
11739           {
11740             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11741             DrawLevelField(x, y);
11742           }
11743           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11744                    element == EL_DC_MAGIC_WALL_FULL)
11745           {
11746             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11747             DrawLevelField(x, y);
11748           }
11749         }
11750
11751         game.magic_wall_active = FALSE;
11752       }
11753     }
11754   }
11755
11756   if (game.light_time_left > 0)
11757   {
11758     game.light_time_left--;
11759
11760     if (game.light_time_left == 0)
11761       RedrawAllLightSwitchesAndInvisibleElements();
11762   }
11763
11764   if (game.timegate_time_left > 0)
11765   {
11766     game.timegate_time_left--;
11767
11768     if (game.timegate_time_left == 0)
11769       CloseAllOpenTimegates();
11770   }
11771
11772   if (game.lenses_time_left > 0)
11773   {
11774     game.lenses_time_left--;
11775
11776     if (game.lenses_time_left == 0)
11777       RedrawAllInvisibleElementsForLenses();
11778   }
11779
11780   if (game.magnify_time_left > 0)
11781   {
11782     game.magnify_time_left--;
11783
11784     if (game.magnify_time_left == 0)
11785       RedrawAllInvisibleElementsForMagnifier();
11786   }
11787
11788   for (i = 0; i < MAX_PLAYERS; i++)
11789   {
11790     struct PlayerInfo *player = &stored_player[i];
11791
11792     if (SHIELD_ON(player))
11793     {
11794       if (player->shield_deadly_time_left)
11795         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11796       else if (player->shield_normal_time_left)
11797         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11798     }
11799   }
11800
11801   CheckLevelTime();
11802
11803   DrawAllPlayers();
11804   PlayAllPlayersSound();
11805
11806   if (options.debug)                    /* calculate frames per second */
11807   {
11808     static unsigned long fps_counter = 0;
11809     static int fps_frames = 0;
11810     unsigned long fps_delay_ms = Counter() - fps_counter;
11811
11812     fps_frames++;
11813
11814     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11815     {
11816       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11817
11818       fps_frames = 0;
11819       fps_counter = Counter();
11820     }
11821
11822     redraw_mask |= REDRAW_FPS;
11823   }
11824
11825   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11826
11827   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11828   {
11829     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11830
11831     local_player->show_envelope = 0;
11832   }
11833
11834 #if 0
11835   debug_print_timestamp(0, "stop main loop profiling ");
11836   printf("----------------------------------------------------------\n");
11837 #endif
11838
11839   /* use random number generator in every frame to make it less predictable */
11840   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11841     RND(1);
11842 }
11843
11844 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11845 {
11846   int min_x = x, min_y = y, max_x = x, max_y = y;
11847   int i;
11848
11849   for (i = 0; i < MAX_PLAYERS; i++)
11850   {
11851     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11852
11853     if (!stored_player[i].active || &stored_player[i] == player)
11854       continue;
11855
11856     min_x = MIN(min_x, jx);
11857     min_y = MIN(min_y, jy);
11858     max_x = MAX(max_x, jx);
11859     max_y = MAX(max_y, jy);
11860   }
11861
11862   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11863 }
11864
11865 static boolean AllPlayersInVisibleScreen()
11866 {
11867   int i;
11868
11869   for (i = 0; i < MAX_PLAYERS; i++)
11870   {
11871     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11872
11873     if (!stored_player[i].active)
11874       continue;
11875
11876     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11877       return FALSE;
11878   }
11879
11880   return TRUE;
11881 }
11882
11883 void ScrollLevel(int dx, int dy)
11884 {
11885 #if 1
11886   static Bitmap *bitmap_db_field2 = NULL;
11887   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11888   int x, y;
11889 #else
11890   int i, x, y;
11891 #endif
11892
11893 #if 0
11894   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11895   /* only horizontal XOR vertical scroll direction allowed */
11896   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11897     return;
11898 #endif
11899
11900 #if 1
11901   if (bitmap_db_field2 == NULL)
11902     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11903
11904   /* needed when blitting directly to same bitmap -- should not be needed with
11905      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11906   BlitBitmap(drawto_field, bitmap_db_field2,
11907              FX + TILEX * (dx == -1) - softscroll_offset,
11908              FY + TILEY * (dy == -1) - softscroll_offset,
11909              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11910              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11911              FX + TILEX * (dx == 1) - softscroll_offset,
11912              FY + TILEY * (dy == 1) - softscroll_offset);
11913   BlitBitmap(bitmap_db_field2, drawto_field,
11914              FX + TILEX * (dx == 1) - softscroll_offset,
11915              FY + TILEY * (dy == 1) - softscroll_offset,
11916              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11917              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11918              FX + TILEX * (dx == 1) - softscroll_offset,
11919              FY + TILEY * (dy == 1) - softscroll_offset);
11920
11921 #else
11922
11923 #if 1
11924   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11925   int xsize = (BX2 - BX1 + 1);
11926   int ysize = (BY2 - BY1 + 1);
11927   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11928   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11929   int step  = (start < end ? +1 : -1);
11930
11931   for (i = start; i != end; i += step)
11932   {
11933     BlitBitmap(drawto_field, drawto_field,
11934                FX + TILEX * (dx != 0 ? i + step : 0),
11935                FY + TILEY * (dy != 0 ? i + step : 0),
11936                TILEX * (dx != 0 ? 1 : xsize),
11937                TILEY * (dy != 0 ? 1 : ysize),
11938                FX + TILEX * (dx != 0 ? i : 0),
11939                FY + TILEY * (dy != 0 ? i : 0));
11940   }
11941
11942 #else
11943
11944   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11945
11946   BlitBitmap(drawto_field, drawto_field,
11947              FX + TILEX * (dx == -1) - softscroll_offset,
11948              FY + TILEY * (dy == -1) - softscroll_offset,
11949              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11950              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11951              FX + TILEX * (dx == 1) - softscroll_offset,
11952              FY + TILEY * (dy == 1) - softscroll_offset);
11953 #endif
11954 #endif
11955
11956   if (dx != 0)
11957   {
11958     x = (dx == 1 ? BX1 : BX2);
11959     for (y = BY1; y <= BY2; y++)
11960       DrawScreenField(x, y);
11961   }
11962
11963   if (dy != 0)
11964   {
11965     y = (dy == 1 ? BY1 : BY2);
11966     for (x = BX1; x <= BX2; x++)
11967       DrawScreenField(x, y);
11968   }
11969
11970   redraw_mask |= REDRAW_FIELD;
11971 }
11972
11973 static boolean canFallDown(struct PlayerInfo *player)
11974 {
11975   int jx = player->jx, jy = player->jy;
11976
11977   return (IN_LEV_FIELD(jx, jy + 1) &&
11978           (IS_FREE(jx, jy + 1) ||
11979            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11980           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11981           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11982 }
11983
11984 static boolean canPassField(int x, int y, int move_dir)
11985 {
11986   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11987   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11988   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11989   int nextx = x + dx;
11990   int nexty = y + dy;
11991   int element = Feld[x][y];
11992
11993   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11994           !CAN_MOVE(element) &&
11995           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11996           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11997           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11998 }
11999
12000 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12001 {
12002   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12003   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12004   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12005   int newx = x + dx;
12006   int newy = y + dy;
12007
12008   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12009           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12010           (IS_DIGGABLE(Feld[newx][newy]) ||
12011            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12012            canPassField(newx, newy, move_dir)));
12013 }
12014
12015 static void CheckGravityMovement(struct PlayerInfo *player)
12016 {
12017 #if USE_PLAYER_GRAVITY
12018   if (player->gravity && !player->programmed_action)
12019 #else
12020   if (game.gravity && !player->programmed_action)
12021 #endif
12022   {
12023     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12024     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12025     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12026     int jx = player->jx, jy = player->jy;
12027     boolean player_is_moving_to_valid_field =
12028       (!player_is_snapping &&
12029        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12030         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12031     boolean player_can_fall_down = canFallDown(player);
12032
12033     if (player_can_fall_down &&
12034         !player_is_moving_to_valid_field)
12035       player->programmed_action = MV_DOWN;
12036   }
12037 }
12038
12039 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12040 {
12041   return CheckGravityMovement(player);
12042
12043 #if USE_PLAYER_GRAVITY
12044   if (player->gravity && !player->programmed_action)
12045 #else
12046   if (game.gravity && !player->programmed_action)
12047 #endif
12048   {
12049     int jx = player->jx, jy = player->jy;
12050     boolean field_under_player_is_free =
12051       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12052     boolean player_is_standing_on_valid_field =
12053       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12054        (IS_WALKABLE(Feld[jx][jy]) &&
12055         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12056
12057     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12058       player->programmed_action = MV_DOWN;
12059   }
12060 }
12061
12062 /*
12063   MovePlayerOneStep()
12064   -----------------------------------------------------------------------------
12065   dx, dy:               direction (non-diagonal) to try to move the player to
12066   real_dx, real_dy:     direction as read from input device (can be diagonal)
12067 */
12068
12069 boolean MovePlayerOneStep(struct PlayerInfo *player,
12070                           int dx, int dy, int real_dx, int real_dy)
12071 {
12072   int jx = player->jx, jy = player->jy;
12073   int new_jx = jx + dx, new_jy = jy + dy;
12074 #if !USE_FIXED_DONT_RUN_INTO
12075   int element;
12076 #endif
12077   int can_move;
12078   boolean player_can_move = !player->cannot_move;
12079
12080   if (!player->active || (!dx && !dy))
12081     return MP_NO_ACTION;
12082
12083   player->MovDir = (dx < 0 ? MV_LEFT :
12084                     dx > 0 ? MV_RIGHT :
12085                     dy < 0 ? MV_UP :
12086                     dy > 0 ? MV_DOWN :  MV_NONE);
12087
12088   if (!IN_LEV_FIELD(new_jx, new_jy))
12089     return MP_NO_ACTION;
12090
12091   if (!player_can_move)
12092   {
12093     if (player->MovPos == 0)
12094     {
12095       player->is_moving = FALSE;
12096       player->is_digging = FALSE;
12097       player->is_collecting = FALSE;
12098       player->is_snapping = FALSE;
12099       player->is_pushing = FALSE;
12100     }
12101   }
12102
12103 #if 1
12104   if (!options.network && game.centered_player_nr == -1 &&
12105       !AllPlayersInSight(player, new_jx, new_jy))
12106     return MP_NO_ACTION;
12107 #else
12108   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12109     return MP_NO_ACTION;
12110 #endif
12111
12112 #if !USE_FIXED_DONT_RUN_INTO
12113   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12114
12115   /* (moved to DigField()) */
12116   if (player_can_move && DONT_RUN_INTO(element))
12117   {
12118     if (element == EL_ACID && dx == 0 && dy == 1)
12119     {
12120       SplashAcid(new_jx, new_jy);
12121       Feld[jx][jy] = EL_PLAYER_1;
12122       InitMovingField(jx, jy, MV_DOWN);
12123       Store[jx][jy] = EL_ACID;
12124       ContinueMoving(jx, jy);
12125       BuryPlayer(player);
12126     }
12127     else
12128       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12129
12130     return MP_MOVING;
12131   }
12132 #endif
12133
12134   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12135   if (can_move != MP_MOVING)
12136     return can_move;
12137
12138   /* check if DigField() has caused relocation of the player */
12139   if (player->jx != jx || player->jy != jy)
12140     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12141
12142   StorePlayer[jx][jy] = 0;
12143   player->last_jx = jx;
12144   player->last_jy = jy;
12145   player->jx = new_jx;
12146   player->jy = new_jy;
12147   StorePlayer[new_jx][new_jy] = player->element_nr;
12148
12149   if (player->move_delay_value_next != -1)
12150   {
12151     player->move_delay_value = player->move_delay_value_next;
12152     player->move_delay_value_next = -1;
12153   }
12154
12155   player->MovPos =
12156     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12157
12158   player->step_counter++;
12159
12160   PlayerVisit[jx][jy] = FrameCounter;
12161
12162 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12163   player->is_moving = TRUE;
12164 #endif
12165
12166 #if 1
12167   /* should better be called in MovePlayer(), but this breaks some tapes */
12168   ScrollPlayer(player, SCROLL_INIT);
12169 #endif
12170
12171   return MP_MOVING;
12172 }
12173
12174 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12175 {
12176   int jx = player->jx, jy = player->jy;
12177   int old_jx = jx, old_jy = jy;
12178   int moved = MP_NO_ACTION;
12179
12180   if (!player->active)
12181     return FALSE;
12182
12183   if (!dx && !dy)
12184   {
12185     if (player->MovPos == 0)
12186     {
12187       player->is_moving = FALSE;
12188       player->is_digging = FALSE;
12189       player->is_collecting = FALSE;
12190       player->is_snapping = FALSE;
12191       player->is_pushing = FALSE;
12192     }
12193
12194     return FALSE;
12195   }
12196
12197   if (player->move_delay > 0)
12198     return FALSE;
12199
12200   player->move_delay = -1;              /* set to "uninitialized" value */
12201
12202   /* store if player is automatically moved to next field */
12203   player->is_auto_moving = (player->programmed_action != MV_NONE);
12204
12205   /* remove the last programmed player action */
12206   player->programmed_action = 0;
12207
12208   if (player->MovPos)
12209   {
12210     /* should only happen if pre-1.2 tape recordings are played */
12211     /* this is only for backward compatibility */
12212
12213     int original_move_delay_value = player->move_delay_value;
12214
12215 #if DEBUG
12216     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12217            tape.counter);
12218 #endif
12219
12220     /* scroll remaining steps with finest movement resolution */
12221     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12222
12223     while (player->MovPos)
12224     {
12225       ScrollPlayer(player, SCROLL_GO_ON);
12226       ScrollScreen(NULL, SCROLL_GO_ON);
12227
12228       AdvanceFrameAndPlayerCounters(player->index_nr);
12229
12230       DrawAllPlayers();
12231       BackToFront();
12232     }
12233
12234     player->move_delay_value = original_move_delay_value;
12235   }
12236
12237   player->is_active = FALSE;
12238
12239   if (player->last_move_dir & MV_HORIZONTAL)
12240   {
12241     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12242       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12243   }
12244   else
12245   {
12246     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12247       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12248   }
12249
12250 #if USE_FIXED_BORDER_RUNNING_GFX
12251   if (!moved && !player->is_active)
12252   {
12253     player->is_moving = FALSE;
12254     player->is_digging = FALSE;
12255     player->is_collecting = FALSE;
12256     player->is_snapping = FALSE;
12257     player->is_pushing = FALSE;
12258   }
12259 #endif
12260
12261   jx = player->jx;
12262   jy = player->jy;
12263
12264 #if 1
12265   if (moved & MP_MOVING && !ScreenMovPos &&
12266       (player->index_nr == game.centered_player_nr ||
12267        game.centered_player_nr == -1))
12268 #else
12269   if (moved & MP_MOVING && !ScreenMovPos &&
12270       (player == local_player || !options.network))
12271 #endif
12272   {
12273     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12274     int offset = game.scroll_delay_value;
12275
12276     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12277     {
12278       /* actual player has left the screen -- scroll in that direction */
12279       if (jx != old_jx)         /* player has moved horizontally */
12280         scroll_x += (jx - old_jx);
12281       else                      /* player has moved vertically */
12282         scroll_y += (jy - old_jy);
12283     }
12284     else
12285     {
12286       if (jx != old_jx)         /* player has moved horizontally */
12287       {
12288         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12289             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12290           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12291
12292         /* don't scroll over playfield boundaries */
12293         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12294           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12295
12296         /* don't scroll more than one field at a time */
12297         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12298
12299         /* don't scroll against the player's moving direction */
12300         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12301             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12302           scroll_x = old_scroll_x;
12303       }
12304       else                      /* player has moved vertically */
12305       {
12306         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12307             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12308           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12309
12310         /* don't scroll over playfield boundaries */
12311         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12312           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12313
12314         /* don't scroll more than one field at a time */
12315         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12316
12317         /* don't scroll against the player's moving direction */
12318         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12319             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12320           scroll_y = old_scroll_y;
12321       }
12322     }
12323
12324     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12325     {
12326 #if 1
12327       if (!options.network && game.centered_player_nr == -1 &&
12328           !AllPlayersInVisibleScreen())
12329       {
12330         scroll_x = old_scroll_x;
12331         scroll_y = old_scroll_y;
12332       }
12333       else
12334 #else
12335       if (!options.network && !AllPlayersInVisibleScreen())
12336       {
12337         scroll_x = old_scroll_x;
12338         scroll_y = old_scroll_y;
12339       }
12340       else
12341 #endif
12342       {
12343         ScrollScreen(player, SCROLL_INIT);
12344         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12345       }
12346     }
12347   }
12348
12349   player->StepFrame = 0;
12350
12351   if (moved & MP_MOVING)
12352   {
12353     if (old_jx != jx && old_jy == jy)
12354       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12355     else if (old_jx == jx && old_jy != jy)
12356       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12357
12358     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12359
12360     player->last_move_dir = player->MovDir;
12361     player->is_moving = TRUE;
12362     player->is_snapping = FALSE;
12363     player->is_switching = FALSE;
12364     player->is_dropping = FALSE;
12365     player->is_dropping_pressed = FALSE;
12366     player->drop_pressed_delay = 0;
12367
12368 #if 0
12369     /* should better be called here than above, but this breaks some tapes */
12370     ScrollPlayer(player, SCROLL_INIT);
12371 #endif
12372   }
12373   else
12374   {
12375     CheckGravityMovementWhenNotMoving(player);
12376
12377     player->is_moving = FALSE;
12378
12379     /* at this point, the player is allowed to move, but cannot move right now
12380        (e.g. because of something blocking the way) -- ensure that the player
12381        is also allowed to move in the next frame (in old versions before 3.1.1,
12382        the player was forced to wait again for eight frames before next try) */
12383
12384     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12385       player->move_delay = 0;   /* allow direct movement in the next frame */
12386   }
12387
12388   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12389     player->move_delay = player->move_delay_value;
12390
12391   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12392   {
12393     TestIfPlayerTouchesBadThing(jx, jy);
12394     TestIfPlayerTouchesCustomElement(jx, jy);
12395   }
12396
12397   if (!player->active)
12398     RemovePlayer(player);
12399
12400   return moved;
12401 }
12402
12403 void ScrollPlayer(struct PlayerInfo *player, int mode)
12404 {
12405   int jx = player->jx, jy = player->jy;
12406   int last_jx = player->last_jx, last_jy = player->last_jy;
12407   int move_stepsize = TILEX / player->move_delay_value;
12408
12409 #if USE_NEW_PLAYER_SPEED
12410   if (!player->active)
12411     return;
12412
12413   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12414     return;
12415 #else
12416   if (!player->active || player->MovPos == 0)
12417     return;
12418 #endif
12419
12420   if (mode == SCROLL_INIT)
12421   {
12422     player->actual_frame_counter = FrameCounter;
12423     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12424
12425     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12426         Feld[last_jx][last_jy] == EL_EMPTY)
12427     {
12428       int last_field_block_delay = 0;   /* start with no blocking at all */
12429       int block_delay_adjustment = player->block_delay_adjustment;
12430
12431       /* if player blocks last field, add delay for exactly one move */
12432       if (player->block_last_field)
12433       {
12434         last_field_block_delay += player->move_delay_value;
12435
12436         /* when blocking enabled, prevent moving up despite gravity */
12437 #if USE_PLAYER_GRAVITY
12438         if (player->gravity && player->MovDir == MV_UP)
12439           block_delay_adjustment = -1;
12440 #else
12441         if (game.gravity && player->MovDir == MV_UP)
12442           block_delay_adjustment = -1;
12443 #endif
12444       }
12445
12446       /* add block delay adjustment (also possible when not blocking) */
12447       last_field_block_delay += block_delay_adjustment;
12448
12449       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12450       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12451     }
12452
12453 #if USE_NEW_PLAYER_SPEED
12454     if (player->MovPos != 0)    /* player has not yet reached destination */
12455       return;
12456 #else
12457     return;
12458 #endif
12459   }
12460   else if (!FrameReached(&player->actual_frame_counter, 1))
12461     return;
12462
12463 #if USE_NEW_PLAYER_SPEED
12464   if (player->MovPos != 0)
12465   {
12466     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12467     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12468
12469     /* before DrawPlayer() to draw correct player graphic for this case */
12470     if (player->MovPos == 0)
12471       CheckGravityMovement(player);
12472   }
12473 #else
12474   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12475   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12476
12477   /* before DrawPlayer() to draw correct player graphic for this case */
12478   if (player->MovPos == 0)
12479     CheckGravityMovement(player);
12480 #endif
12481
12482   if (player->MovPos == 0)      /* player reached destination field */
12483   {
12484     if (player->move_delay_reset_counter > 0)
12485     {
12486       player->move_delay_reset_counter--;
12487
12488       if (player->move_delay_reset_counter == 0)
12489       {
12490         /* continue with normal speed after quickly moving through gate */
12491         HALVE_PLAYER_SPEED(player);
12492
12493         /* be able to make the next move without delay */
12494         player->move_delay = 0;
12495       }
12496     }
12497
12498     player->last_jx = jx;
12499     player->last_jy = jy;
12500
12501     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12502         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12503         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12504         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12505         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12506         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12507     {
12508       DrawPlayer(player);       /* needed here only to cleanup last field */
12509       RemovePlayer(player);
12510
12511       if (local_player->friends_still_needed == 0 ||
12512           IS_SP_ELEMENT(Feld[jx][jy]))
12513         PlayerWins(player);
12514     }
12515
12516     /* this breaks one level: "machine", level 000 */
12517     {
12518       int move_direction = player->MovDir;
12519       int enter_side = MV_DIR_OPPOSITE(move_direction);
12520       int leave_side = move_direction;
12521       int old_jx = last_jx;
12522       int old_jy = last_jy;
12523       int old_element = Feld[old_jx][old_jy];
12524       int new_element = Feld[jx][jy];
12525
12526       if (IS_CUSTOM_ELEMENT(old_element))
12527         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12528                                    CE_LEFT_BY_PLAYER,
12529                                    player->index_bit, leave_side);
12530
12531       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12532                                           CE_PLAYER_LEAVES_X,
12533                                           player->index_bit, leave_side);
12534
12535       if (IS_CUSTOM_ELEMENT(new_element))
12536         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12537                                    player->index_bit, enter_side);
12538
12539       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12540                                           CE_PLAYER_ENTERS_X,
12541                                           player->index_bit, enter_side);
12542
12543       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12544                                         CE_MOVE_OF_X, move_direction);
12545     }
12546
12547     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12548     {
12549       TestIfPlayerTouchesBadThing(jx, jy);
12550       TestIfPlayerTouchesCustomElement(jx, jy);
12551
12552       /* needed because pushed element has not yet reached its destination,
12553          so it would trigger a change event at its previous field location */
12554       if (!player->is_pushing)
12555         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12556
12557       if (!player->active)
12558         RemovePlayer(player);
12559     }
12560
12561     if (!local_player->LevelSolved && level.use_step_counter)
12562     {
12563       int i;
12564
12565       TimePlayed++;
12566
12567       if (TimeLeft > 0)
12568       {
12569         TimeLeft--;
12570
12571         if (TimeLeft <= 10 && setup.time_limit)
12572           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12573
12574 #if 1
12575         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12576
12577         DisplayGameControlValues();
12578 #else
12579         DrawGameValue_Time(TimeLeft);
12580 #endif
12581
12582         if (!TimeLeft && setup.time_limit)
12583           for (i = 0; i < MAX_PLAYERS; i++)
12584             KillPlayer(&stored_player[i]);
12585       }
12586 #if 1
12587       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12588       {
12589         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12590
12591         DisplayGameControlValues();
12592       }
12593 #else
12594       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12595         DrawGameValue_Time(TimePlayed);
12596 #endif
12597     }
12598
12599     if (tape.single_step && tape.recording && !tape.pausing &&
12600         !player->programmed_action)
12601       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12602   }
12603 }
12604
12605 void ScrollScreen(struct PlayerInfo *player, int mode)
12606 {
12607   static unsigned long screen_frame_counter = 0;
12608
12609   if (mode == SCROLL_INIT)
12610   {
12611     /* set scrolling step size according to actual player's moving speed */
12612     ScrollStepSize = TILEX / player->move_delay_value;
12613
12614     screen_frame_counter = FrameCounter;
12615     ScreenMovDir = player->MovDir;
12616     ScreenMovPos = player->MovPos;
12617     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12618     return;
12619   }
12620   else if (!FrameReached(&screen_frame_counter, 1))
12621     return;
12622
12623   if (ScreenMovPos)
12624   {
12625     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12626     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12627     redraw_mask |= REDRAW_FIELD;
12628   }
12629   else
12630     ScreenMovDir = MV_NONE;
12631 }
12632
12633 void TestIfPlayerTouchesCustomElement(int x, int y)
12634 {
12635   static int xy[4][2] =
12636   {
12637     { 0, -1 },
12638     { -1, 0 },
12639     { +1, 0 },
12640     { 0, +1 }
12641   };
12642   static int trigger_sides[4][2] =
12643   {
12644     /* center side       border side */
12645     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12646     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12647     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12648     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12649   };
12650   static int touch_dir[4] =
12651   {
12652     MV_LEFT | MV_RIGHT,
12653     MV_UP   | MV_DOWN,
12654     MV_UP   | MV_DOWN,
12655     MV_LEFT | MV_RIGHT
12656   };
12657   int center_element = Feld[x][y];      /* should always be non-moving! */
12658   int i;
12659
12660   for (i = 0; i < NUM_DIRECTIONS; i++)
12661   {
12662     int xx = x + xy[i][0];
12663     int yy = y + xy[i][1];
12664     int center_side = trigger_sides[i][0];
12665     int border_side = trigger_sides[i][1];
12666     int border_element;
12667
12668     if (!IN_LEV_FIELD(xx, yy))
12669       continue;
12670
12671     if (IS_PLAYER(x, y))
12672     {
12673       struct PlayerInfo *player = PLAYERINFO(x, y);
12674
12675       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12676         border_element = Feld[xx][yy];          /* may be moving! */
12677       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12678         border_element = Feld[xx][yy];
12679       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12680         border_element = MovingOrBlocked2Element(xx, yy);
12681       else
12682         continue;               /* center and border element do not touch */
12683
12684       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12685                                  player->index_bit, border_side);
12686       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12687                                           CE_PLAYER_TOUCHES_X,
12688                                           player->index_bit, border_side);
12689     }
12690     else if (IS_PLAYER(xx, yy))
12691     {
12692       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12693
12694       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12695       {
12696         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12697           continue;             /* center and border element do not touch */
12698       }
12699
12700       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12701                                  player->index_bit, center_side);
12702       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12703                                           CE_PLAYER_TOUCHES_X,
12704                                           player->index_bit, center_side);
12705       break;
12706     }
12707   }
12708 }
12709
12710 #if USE_ELEMENT_TOUCHING_BUGFIX
12711
12712 void TestIfElementTouchesCustomElement(int x, int y)
12713 {
12714   static int xy[4][2] =
12715   {
12716     { 0, -1 },
12717     { -1, 0 },
12718     { +1, 0 },
12719     { 0, +1 }
12720   };
12721   static int trigger_sides[4][2] =
12722   {
12723     /* center side      border side */
12724     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12725     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12726     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12727     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12728   };
12729   static int touch_dir[4] =
12730   {
12731     MV_LEFT | MV_RIGHT,
12732     MV_UP   | MV_DOWN,
12733     MV_UP   | MV_DOWN,
12734     MV_LEFT | MV_RIGHT
12735   };
12736   boolean change_center_element = FALSE;
12737   int center_element = Feld[x][y];      /* should always be non-moving! */
12738   int border_element_old[NUM_DIRECTIONS];
12739   int i;
12740
12741   for (i = 0; i < NUM_DIRECTIONS; i++)
12742   {
12743     int xx = x + xy[i][0];
12744     int yy = y + xy[i][1];
12745     int border_element;
12746
12747     border_element_old[i] = -1;
12748
12749     if (!IN_LEV_FIELD(xx, yy))
12750       continue;
12751
12752     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12753       border_element = Feld[xx][yy];    /* may be moving! */
12754     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12755       border_element = Feld[xx][yy];
12756     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12757       border_element = MovingOrBlocked2Element(xx, yy);
12758     else
12759       continue;                 /* center and border element do not touch */
12760
12761     border_element_old[i] = border_element;
12762   }
12763
12764   for (i = 0; i < NUM_DIRECTIONS; i++)
12765   {
12766     int xx = x + xy[i][0];
12767     int yy = y + xy[i][1];
12768     int center_side = trigger_sides[i][0];
12769     int border_element = border_element_old[i];
12770
12771     if (border_element == -1)
12772       continue;
12773
12774     /* check for change of border element */
12775     CheckElementChangeBySide(xx, yy, border_element, center_element,
12776                              CE_TOUCHING_X, center_side);
12777   }
12778
12779   for (i = 0; i < NUM_DIRECTIONS; i++)
12780   {
12781     int border_side = trigger_sides[i][1];
12782     int border_element = border_element_old[i];
12783
12784     if (border_element == -1)
12785       continue;
12786
12787     /* check for change of center element (but change it only once) */
12788     if (!change_center_element)
12789       change_center_element =
12790         CheckElementChangeBySide(x, y, center_element, border_element,
12791                                  CE_TOUCHING_X, border_side);
12792   }
12793 }
12794
12795 #else
12796
12797 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12798 {
12799   static int xy[4][2] =
12800   {
12801     { 0, -1 },
12802     { -1, 0 },
12803     { +1, 0 },
12804     { 0, +1 }
12805   };
12806   static int trigger_sides[4][2] =
12807   {
12808     /* center side      border side */
12809     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12810     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12811     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12812     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12813   };
12814   static int touch_dir[4] =
12815   {
12816     MV_LEFT | MV_RIGHT,
12817     MV_UP   | MV_DOWN,
12818     MV_UP   | MV_DOWN,
12819     MV_LEFT | MV_RIGHT
12820   };
12821   boolean change_center_element = FALSE;
12822   int center_element = Feld[x][y];      /* should always be non-moving! */
12823   int i;
12824
12825   for (i = 0; i < NUM_DIRECTIONS; i++)
12826   {
12827     int xx = x + xy[i][0];
12828     int yy = y + xy[i][1];
12829     int center_side = trigger_sides[i][0];
12830     int border_side = trigger_sides[i][1];
12831     int border_element;
12832
12833     if (!IN_LEV_FIELD(xx, yy))
12834       continue;
12835
12836     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12837       border_element = Feld[xx][yy];    /* may be moving! */
12838     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12839       border_element = Feld[xx][yy];
12840     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12841       border_element = MovingOrBlocked2Element(xx, yy);
12842     else
12843       continue;                 /* center and border element do not touch */
12844
12845     /* check for change of center element (but change it only once) */
12846     if (!change_center_element)
12847       change_center_element =
12848         CheckElementChangeBySide(x, y, center_element, border_element,
12849                                  CE_TOUCHING_X, border_side);
12850
12851     /* check for change of border element */
12852     CheckElementChangeBySide(xx, yy, border_element, center_element,
12853                              CE_TOUCHING_X, center_side);
12854   }
12855 }
12856
12857 #endif
12858
12859 void TestIfElementHitsCustomElement(int x, int y, int direction)
12860 {
12861   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12862   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12863   int hitx = x + dx, hity = y + dy;
12864   int hitting_element = Feld[x][y];
12865   int touched_element;
12866
12867   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12868     return;
12869
12870   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12871                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12872
12873   if (IN_LEV_FIELD(hitx, hity))
12874   {
12875     int opposite_direction = MV_DIR_OPPOSITE(direction);
12876     int hitting_side = direction;
12877     int touched_side = opposite_direction;
12878     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12879                           MovDir[hitx][hity] != direction ||
12880                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12881
12882     object_hit = TRUE;
12883
12884     if (object_hit)
12885     {
12886       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12887                                CE_HITTING_X, touched_side);
12888
12889       CheckElementChangeBySide(hitx, hity, touched_element,
12890                                hitting_element, CE_HIT_BY_X, hitting_side);
12891
12892       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12893                                CE_HIT_BY_SOMETHING, opposite_direction);
12894     }
12895   }
12896
12897   /* "hitting something" is also true when hitting the playfield border */
12898   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12899                            CE_HITTING_SOMETHING, direction);
12900 }
12901
12902 #if 0
12903 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12904 {
12905   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12906   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12907   int hitx = x + dx, hity = y + dy;
12908   int hitting_element = Feld[x][y];
12909   int touched_element;
12910 #if 0
12911   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12912                         !IS_FREE(hitx, hity) &&
12913                         (!IS_MOVING(hitx, hity) ||
12914                          MovDir[hitx][hity] != direction ||
12915                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12916 #endif
12917
12918   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12919     return;
12920
12921 #if 0
12922   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12923     return;
12924 #endif
12925
12926   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12927                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12928
12929   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12930                            EP_CAN_SMASH_EVERYTHING, direction);
12931
12932   if (IN_LEV_FIELD(hitx, hity))
12933   {
12934     int opposite_direction = MV_DIR_OPPOSITE(direction);
12935     int hitting_side = direction;
12936     int touched_side = opposite_direction;
12937 #if 0
12938     int touched_element = MovingOrBlocked2Element(hitx, hity);
12939 #endif
12940 #if 1
12941     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12942                           MovDir[hitx][hity] != direction ||
12943                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12944
12945     object_hit = TRUE;
12946 #endif
12947
12948     if (object_hit)
12949     {
12950       int i;
12951
12952       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12953                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12954
12955       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12956                                CE_OTHER_IS_SMASHING, touched_side);
12957
12958       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12959                                CE_OTHER_GETS_SMASHED, hitting_side);
12960     }
12961   }
12962 }
12963 #endif
12964
12965 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12966 {
12967   int i, kill_x = -1, kill_y = -1;
12968
12969   int bad_element = -1;
12970   static int test_xy[4][2] =
12971   {
12972     { 0, -1 },
12973     { -1, 0 },
12974     { +1, 0 },
12975     { 0, +1 }
12976   };
12977   static int test_dir[4] =
12978   {
12979     MV_UP,
12980     MV_LEFT,
12981     MV_RIGHT,
12982     MV_DOWN
12983   };
12984
12985   for (i = 0; i < NUM_DIRECTIONS; i++)
12986   {
12987     int test_x, test_y, test_move_dir, test_element;
12988
12989     test_x = good_x + test_xy[i][0];
12990     test_y = good_y + test_xy[i][1];
12991
12992     if (!IN_LEV_FIELD(test_x, test_y))
12993       continue;
12994
12995     test_move_dir =
12996       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12997
12998     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12999
13000     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13001        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13002     */
13003     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13004         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13005     {
13006       kill_x = test_x;
13007       kill_y = test_y;
13008       bad_element = test_element;
13009
13010       break;
13011     }
13012   }
13013
13014   if (kill_x != -1 || kill_y != -1)
13015   {
13016     if (IS_PLAYER(good_x, good_y))
13017     {
13018       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13019
13020       if (player->shield_deadly_time_left > 0 &&
13021           !IS_INDESTRUCTIBLE(bad_element))
13022         Bang(kill_x, kill_y);
13023       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13024         KillPlayer(player);
13025     }
13026     else
13027       Bang(good_x, good_y);
13028   }
13029 }
13030
13031 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13032 {
13033   int i, kill_x = -1, kill_y = -1;
13034   int bad_element = Feld[bad_x][bad_y];
13035   static int test_xy[4][2] =
13036   {
13037     { 0, -1 },
13038     { -1, 0 },
13039     { +1, 0 },
13040     { 0, +1 }
13041   };
13042   static int touch_dir[4] =
13043   {
13044     MV_LEFT | MV_RIGHT,
13045     MV_UP   | MV_DOWN,
13046     MV_UP   | MV_DOWN,
13047     MV_LEFT | MV_RIGHT
13048   };
13049   static int test_dir[4] =
13050   {
13051     MV_UP,
13052     MV_LEFT,
13053     MV_RIGHT,
13054     MV_DOWN
13055   };
13056
13057   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13058     return;
13059
13060   for (i = 0; i < NUM_DIRECTIONS; i++)
13061   {
13062     int test_x, test_y, test_move_dir, test_element;
13063
13064     test_x = bad_x + test_xy[i][0];
13065     test_y = bad_y + test_xy[i][1];
13066     if (!IN_LEV_FIELD(test_x, test_y))
13067       continue;
13068
13069     test_move_dir =
13070       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13071
13072     test_element = Feld[test_x][test_y];
13073
13074     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13075        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13076     */
13077     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13078         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13079     {
13080       /* good thing is player or penguin that does not move away */
13081       if (IS_PLAYER(test_x, test_y))
13082       {
13083         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13084
13085         if (bad_element == EL_ROBOT && player->is_moving)
13086           continue;     /* robot does not kill player if he is moving */
13087
13088         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13089         {
13090           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13091             continue;           /* center and border element do not touch */
13092         }
13093
13094         kill_x = test_x;
13095         kill_y = test_y;
13096         break;
13097       }
13098       else if (test_element == EL_PENGUIN)
13099       {
13100         kill_x = test_x;
13101         kill_y = test_y;
13102         break;
13103       }
13104     }
13105   }
13106
13107   if (kill_x != -1 || kill_y != -1)
13108   {
13109     if (IS_PLAYER(kill_x, kill_y))
13110     {
13111       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13112
13113       if (player->shield_deadly_time_left > 0 &&
13114           !IS_INDESTRUCTIBLE(bad_element))
13115         Bang(bad_x, bad_y);
13116       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13117         KillPlayer(player);
13118     }
13119     else
13120       Bang(kill_x, kill_y);
13121   }
13122 }
13123
13124 void TestIfPlayerTouchesBadThing(int x, int y)
13125 {
13126   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13127 }
13128
13129 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13130 {
13131   TestIfGoodThingHitsBadThing(x, y, move_dir);
13132 }
13133
13134 void TestIfBadThingTouchesPlayer(int x, int y)
13135 {
13136   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13137 }
13138
13139 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13140 {
13141   TestIfBadThingHitsGoodThing(x, y, move_dir);
13142 }
13143
13144 void TestIfFriendTouchesBadThing(int x, int y)
13145 {
13146   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13147 }
13148
13149 void TestIfBadThingTouchesFriend(int x, int y)
13150 {
13151   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13152 }
13153
13154 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13155 {
13156   int i, kill_x = bad_x, kill_y = bad_y;
13157   static int xy[4][2] =
13158   {
13159     { 0, -1 },
13160     { -1, 0 },
13161     { +1, 0 },
13162     { 0, +1 }
13163   };
13164
13165   for (i = 0; i < NUM_DIRECTIONS; i++)
13166   {
13167     int x, y, element;
13168
13169     x = bad_x + xy[i][0];
13170     y = bad_y + xy[i][1];
13171     if (!IN_LEV_FIELD(x, y))
13172       continue;
13173
13174     element = Feld[x][y];
13175     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13176         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13177     {
13178       kill_x = x;
13179       kill_y = y;
13180       break;
13181     }
13182   }
13183
13184   if (kill_x != bad_x || kill_y != bad_y)
13185     Bang(bad_x, bad_y);
13186 }
13187
13188 void KillPlayer(struct PlayerInfo *player)
13189 {
13190   int jx = player->jx, jy = player->jy;
13191
13192   if (!player->active)
13193     return;
13194
13195   /* the following code was introduced to prevent an infinite loop when calling
13196      -> Bang()
13197      -> CheckTriggeredElementChangeExt()
13198      -> ExecuteCustomElementAction()
13199      -> KillPlayer()
13200      -> (infinitely repeating the above sequence of function calls)
13201      which occurs when killing the player while having a CE with the setting
13202      "kill player X when explosion of <player X>"; the solution using a new
13203      field "player->killed" was chosen for backwards compatibility, although
13204      clever use of the fields "player->active" etc. would probably also work */
13205 #if 1
13206   if (player->killed)
13207     return;
13208 #endif
13209
13210   player->killed = TRUE;
13211
13212   /* remove accessible field at the player's position */
13213   Feld[jx][jy] = EL_EMPTY;
13214
13215   /* deactivate shield (else Bang()/Explode() would not work right) */
13216   player->shield_normal_time_left = 0;
13217   player->shield_deadly_time_left = 0;
13218
13219   Bang(jx, jy);
13220   BuryPlayer(player);
13221 }
13222
13223 static void KillPlayerUnlessEnemyProtected(int x, int y)
13224 {
13225   if (!PLAYER_ENEMY_PROTECTED(x, y))
13226     KillPlayer(PLAYERINFO(x, y));
13227 }
13228
13229 static void KillPlayerUnlessExplosionProtected(int x, int y)
13230 {
13231   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13232     KillPlayer(PLAYERINFO(x, y));
13233 }
13234
13235 void BuryPlayer(struct PlayerInfo *player)
13236 {
13237   int jx = player->jx, jy = player->jy;
13238
13239   if (!player->active)
13240     return;
13241
13242   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13243   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13244
13245   player->GameOver = TRUE;
13246   RemovePlayer(player);
13247 }
13248
13249 void RemovePlayer(struct PlayerInfo *player)
13250 {
13251   int jx = player->jx, jy = player->jy;
13252   int i, found = FALSE;
13253
13254   player->present = FALSE;
13255   player->active = FALSE;
13256
13257   if (!ExplodeField[jx][jy])
13258     StorePlayer[jx][jy] = 0;
13259
13260   if (player->is_moving)
13261     DrawLevelField(player->last_jx, player->last_jy);
13262
13263   for (i = 0; i < MAX_PLAYERS; i++)
13264     if (stored_player[i].active)
13265       found = TRUE;
13266
13267   if (!found)
13268     AllPlayersGone = TRUE;
13269
13270   ExitX = ZX = jx;
13271   ExitY = ZY = jy;
13272 }
13273
13274 #if USE_NEW_SNAP_DELAY
13275 static void setFieldForSnapping(int x, int y, int element, int direction)
13276 {
13277   struct ElementInfo *ei = &element_info[element];
13278   int direction_bit = MV_DIR_TO_BIT(direction);
13279   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13280   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13281                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13282
13283   Feld[x][y] = EL_ELEMENT_SNAPPING;
13284   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13285
13286   ResetGfxAnimation(x, y);
13287
13288   GfxElement[x][y] = element;
13289   GfxAction[x][y] = action;
13290   GfxDir[x][y] = direction;
13291   GfxFrame[x][y] = -1;
13292 }
13293 #endif
13294
13295 /*
13296   =============================================================================
13297   checkDiagonalPushing()
13298   -----------------------------------------------------------------------------
13299   check if diagonal input device direction results in pushing of object
13300   (by checking if the alternative direction is walkable, diggable, ...)
13301   =============================================================================
13302 */
13303
13304 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13305                                     int x, int y, int real_dx, int real_dy)
13306 {
13307   int jx, jy, dx, dy, xx, yy;
13308
13309   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13310     return TRUE;
13311
13312   /* diagonal direction: check alternative direction */
13313   jx = player->jx;
13314   jy = player->jy;
13315   dx = x - jx;
13316   dy = y - jy;
13317   xx = jx + (dx == 0 ? real_dx : 0);
13318   yy = jy + (dy == 0 ? real_dy : 0);
13319
13320   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13321 }
13322
13323 /*
13324   =============================================================================
13325   DigField()
13326   -----------------------------------------------------------------------------
13327   x, y:                 field next to player (non-diagonal) to try to dig to
13328   real_dx, real_dy:     direction as read from input device (can be diagonal)
13329   =============================================================================
13330 */
13331
13332 int DigField(struct PlayerInfo *player,
13333              int oldx, int oldy, int x, int y,
13334              int real_dx, int real_dy, int mode)
13335 {
13336   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13337   boolean player_was_pushing = player->is_pushing;
13338   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13339   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13340   int jx = oldx, jy = oldy;
13341   int dx = x - jx, dy = y - jy;
13342   int nextx = x + dx, nexty = y + dy;
13343   int move_direction = (dx == -1 ? MV_LEFT  :
13344                         dx == +1 ? MV_RIGHT :
13345                         dy == -1 ? MV_UP    :
13346                         dy == +1 ? MV_DOWN  : MV_NONE);
13347   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13348   int dig_side = MV_DIR_OPPOSITE(move_direction);
13349   int old_element = Feld[jx][jy];
13350 #if USE_FIXED_DONT_RUN_INTO
13351   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13352 #else
13353   int element;
13354 #endif
13355   int collect_count;
13356
13357   if (is_player)                /* function can also be called by EL_PENGUIN */
13358   {
13359     if (player->MovPos == 0)
13360     {
13361       player->is_digging = FALSE;
13362       player->is_collecting = FALSE;
13363     }
13364
13365     if (player->MovPos == 0)    /* last pushing move finished */
13366       player->is_pushing = FALSE;
13367
13368     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13369     {
13370       player->is_switching = FALSE;
13371       player->push_delay = -1;
13372
13373       return MP_NO_ACTION;
13374     }
13375   }
13376
13377 #if !USE_FIXED_DONT_RUN_INTO
13378   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13379     return MP_NO_ACTION;
13380 #endif
13381
13382   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13383     old_element = Back[jx][jy];
13384
13385   /* in case of element dropped at player position, check background */
13386   else if (Back[jx][jy] != EL_EMPTY &&
13387            game.engine_version >= VERSION_IDENT(2,2,0,0))
13388     old_element = Back[jx][jy];
13389
13390   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13391     return MP_NO_ACTION;        /* field has no opening in this direction */
13392
13393   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13394     return MP_NO_ACTION;        /* field has no opening in this direction */
13395
13396 #if USE_FIXED_DONT_RUN_INTO
13397   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13398   {
13399     SplashAcid(x, y);
13400
13401     Feld[jx][jy] = player->artwork_element;
13402     InitMovingField(jx, jy, MV_DOWN);
13403     Store[jx][jy] = EL_ACID;
13404     ContinueMoving(jx, jy);
13405     BuryPlayer(player);
13406
13407     return MP_DONT_RUN_INTO;
13408   }
13409 #endif
13410
13411 #if USE_FIXED_DONT_RUN_INTO
13412   if (player_can_move && DONT_RUN_INTO(element))
13413   {
13414     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13415
13416     return MP_DONT_RUN_INTO;
13417   }
13418 #endif
13419
13420 #if USE_FIXED_DONT_RUN_INTO
13421   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13422     return MP_NO_ACTION;
13423 #endif
13424
13425 #if !USE_FIXED_DONT_RUN_INTO
13426   element = Feld[x][y];
13427 #endif
13428
13429   collect_count = element_info[element].collect_count_initial;
13430
13431   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13432     return MP_NO_ACTION;
13433
13434   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13435     player_can_move = player_can_move_or_snap;
13436
13437   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13438       game.engine_version >= VERSION_IDENT(2,2,0,0))
13439   {
13440     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13441                                player->index_bit, dig_side);
13442     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13443                                         player->index_bit, dig_side);
13444
13445     if (element == EL_DC_LANDMINE)
13446       Bang(x, y);
13447
13448     if (Feld[x][y] != element)          /* field changed by snapping */
13449       return MP_ACTION;
13450
13451     return MP_NO_ACTION;
13452   }
13453
13454 #if USE_PLAYER_GRAVITY
13455   if (player->gravity && is_player && !player->is_auto_moving &&
13456       canFallDown(player) && move_direction != MV_DOWN &&
13457       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13458     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13459 #else
13460   if (game.gravity && is_player && !player->is_auto_moving &&
13461       canFallDown(player) && move_direction != MV_DOWN &&
13462       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13463     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13464 #endif
13465
13466   if (player_can_move &&
13467       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13468   {
13469     int sound_element = SND_ELEMENT(element);
13470     int sound_action = ACTION_WALKING;
13471
13472     if (IS_RND_GATE(element))
13473     {
13474       if (!player->key[RND_GATE_NR(element)])
13475         return MP_NO_ACTION;
13476     }
13477     else if (IS_RND_GATE_GRAY(element))
13478     {
13479       if (!player->key[RND_GATE_GRAY_NR(element)])
13480         return MP_NO_ACTION;
13481     }
13482     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13483     {
13484       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13485         return MP_NO_ACTION;
13486     }
13487     else if (element == EL_EXIT_OPEN ||
13488              element == EL_EM_EXIT_OPEN ||
13489              element == EL_STEEL_EXIT_OPEN ||
13490              element == EL_EM_STEEL_EXIT_OPEN ||
13491              element == EL_SP_EXIT_OPEN ||
13492              element == EL_SP_EXIT_OPENING)
13493     {
13494       sound_action = ACTION_PASSING;    /* player is passing exit */
13495     }
13496     else if (element == EL_EMPTY)
13497     {
13498       sound_action = ACTION_MOVING;             /* nothing to walk on */
13499     }
13500
13501     /* play sound from background or player, whatever is available */
13502     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13503       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13504     else
13505       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13506   }
13507   else if (player_can_move &&
13508            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13509   {
13510     if (!ACCESS_FROM(element, opposite_direction))
13511       return MP_NO_ACTION;      /* field not accessible from this direction */
13512
13513     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13514       return MP_NO_ACTION;
13515
13516     if (IS_EM_GATE(element))
13517     {
13518       if (!player->key[EM_GATE_NR(element)])
13519         return MP_NO_ACTION;
13520     }
13521     else if (IS_EM_GATE_GRAY(element))
13522     {
13523       if (!player->key[EM_GATE_GRAY_NR(element)])
13524         return MP_NO_ACTION;
13525     }
13526     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13527     {
13528       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13529         return MP_NO_ACTION;
13530     }
13531     else if (IS_EMC_GATE(element))
13532     {
13533       if (!player->key[EMC_GATE_NR(element)])
13534         return MP_NO_ACTION;
13535     }
13536     else if (IS_EMC_GATE_GRAY(element))
13537     {
13538       if (!player->key[EMC_GATE_GRAY_NR(element)])
13539         return MP_NO_ACTION;
13540     }
13541     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13542     {
13543       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13544         return MP_NO_ACTION;
13545     }
13546     else if (element == EL_DC_GATE_WHITE ||
13547              element == EL_DC_GATE_WHITE_GRAY ||
13548              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13549     {
13550       if (player->num_white_keys == 0)
13551         return MP_NO_ACTION;
13552
13553       player->num_white_keys--;
13554     }
13555     else if (IS_SP_PORT(element))
13556     {
13557       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13558           element == EL_SP_GRAVITY_PORT_RIGHT ||
13559           element == EL_SP_GRAVITY_PORT_UP ||
13560           element == EL_SP_GRAVITY_PORT_DOWN)
13561 #if USE_PLAYER_GRAVITY
13562         player->gravity = !player->gravity;
13563 #else
13564         game.gravity = !game.gravity;
13565 #endif
13566       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13567                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13568                element == EL_SP_GRAVITY_ON_PORT_UP ||
13569                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13570 #if USE_PLAYER_GRAVITY
13571         player->gravity = TRUE;
13572 #else
13573         game.gravity = TRUE;
13574 #endif
13575       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13576                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13577                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13578                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13579 #if USE_PLAYER_GRAVITY
13580         player->gravity = FALSE;
13581 #else
13582         game.gravity = FALSE;
13583 #endif
13584     }
13585
13586     /* automatically move to the next field with double speed */
13587     player->programmed_action = move_direction;
13588
13589     if (player->move_delay_reset_counter == 0)
13590     {
13591       player->move_delay_reset_counter = 2;     /* two double speed steps */
13592
13593       DOUBLE_PLAYER_SPEED(player);
13594     }
13595
13596     PlayLevelSoundAction(x, y, ACTION_PASSING);
13597   }
13598   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13599   {
13600     RemoveField(x, y);
13601
13602     if (mode != DF_SNAP)
13603     {
13604       GfxElement[x][y] = GFX_ELEMENT(element);
13605       player->is_digging = TRUE;
13606     }
13607
13608     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13609
13610     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13611                                         player->index_bit, dig_side);
13612
13613     if (mode == DF_SNAP)
13614     {
13615 #if USE_NEW_SNAP_DELAY
13616       if (level.block_snap_field)
13617         setFieldForSnapping(x, y, element, move_direction);
13618       else
13619         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13620 #else
13621       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13622 #endif
13623
13624       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13625                                           player->index_bit, dig_side);
13626     }
13627   }
13628   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13629   {
13630     RemoveField(x, y);
13631
13632     if (is_player && mode != DF_SNAP)
13633     {
13634       GfxElement[x][y] = element;
13635       player->is_collecting = TRUE;
13636     }
13637
13638     if (element == EL_SPEED_PILL)
13639     {
13640       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13641     }
13642     else if (element == EL_EXTRA_TIME && level.time > 0)
13643     {
13644       TimeLeft += level.extra_time;
13645
13646 #if 1
13647       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13648
13649       DisplayGameControlValues();
13650 #else
13651       DrawGameValue_Time(TimeLeft);
13652 #endif
13653     }
13654     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13655     {
13656       player->shield_normal_time_left += level.shield_normal_time;
13657       if (element == EL_SHIELD_DEADLY)
13658         player->shield_deadly_time_left += level.shield_deadly_time;
13659     }
13660     else if (element == EL_DYNAMITE ||
13661              element == EL_EM_DYNAMITE ||
13662              element == EL_SP_DISK_RED)
13663     {
13664       if (player->inventory_size < MAX_INVENTORY_SIZE)
13665         player->inventory_element[player->inventory_size++] = element;
13666
13667       DrawGameDoorValues();
13668     }
13669     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13670     {
13671       player->dynabomb_count++;
13672       player->dynabombs_left++;
13673     }
13674     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13675     {
13676       player->dynabomb_size++;
13677     }
13678     else if (element == EL_DYNABOMB_INCREASE_POWER)
13679     {
13680       player->dynabomb_xl = TRUE;
13681     }
13682     else if (IS_KEY(element))
13683     {
13684       player->key[KEY_NR(element)] = TRUE;
13685
13686       DrawGameDoorValues();
13687     }
13688     else if (element == EL_DC_KEY_WHITE)
13689     {
13690       player->num_white_keys++;
13691
13692       /* display white keys? */
13693       /* DrawGameDoorValues(); */
13694     }
13695     else if (IS_ENVELOPE(element))
13696     {
13697       player->show_envelope = element;
13698     }
13699     else if (element == EL_EMC_LENSES)
13700     {
13701       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13702
13703       RedrawAllInvisibleElementsForLenses();
13704     }
13705     else if (element == EL_EMC_MAGNIFIER)
13706     {
13707       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13708
13709       RedrawAllInvisibleElementsForMagnifier();
13710     }
13711     else if (IS_DROPPABLE(element) ||
13712              IS_THROWABLE(element))     /* can be collected and dropped */
13713     {
13714       int i;
13715
13716       if (collect_count == 0)
13717         player->inventory_infinite_element = element;
13718       else
13719         for (i = 0; i < collect_count; i++)
13720           if (player->inventory_size < MAX_INVENTORY_SIZE)
13721             player->inventory_element[player->inventory_size++] = element;
13722
13723       DrawGameDoorValues();
13724     }
13725     else if (collect_count > 0)
13726     {
13727       local_player->gems_still_needed -= collect_count;
13728       if (local_player->gems_still_needed < 0)
13729         local_player->gems_still_needed = 0;
13730
13731 #if 1
13732       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13733
13734       DisplayGameControlValues();
13735 #else
13736       DrawGameValue_Emeralds(local_player->gems_still_needed);
13737 #endif
13738     }
13739
13740     RaiseScoreElement(element);
13741     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13742
13743     if (is_player)
13744       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13745                                           player->index_bit, dig_side);
13746
13747     if (mode == DF_SNAP)
13748     {
13749 #if USE_NEW_SNAP_DELAY
13750       if (level.block_snap_field)
13751         setFieldForSnapping(x, y, element, move_direction);
13752       else
13753         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13754 #else
13755       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13756 #endif
13757
13758       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13759                                           player->index_bit, dig_side);
13760     }
13761   }
13762   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13763   {
13764     if (mode == DF_SNAP && element != EL_BD_ROCK)
13765       return MP_NO_ACTION;
13766
13767     if (CAN_FALL(element) && dy)
13768       return MP_NO_ACTION;
13769
13770     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13771         !(element == EL_SPRING && level.use_spring_bug))
13772       return MP_NO_ACTION;
13773
13774     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13775         ((move_direction & MV_VERTICAL &&
13776           ((element_info[element].move_pattern & MV_LEFT &&
13777             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13778            (element_info[element].move_pattern & MV_RIGHT &&
13779             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13780          (move_direction & MV_HORIZONTAL &&
13781           ((element_info[element].move_pattern & MV_UP &&
13782             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13783            (element_info[element].move_pattern & MV_DOWN &&
13784             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13785       return MP_NO_ACTION;
13786
13787     /* do not push elements already moving away faster than player */
13788     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13789         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13790       return MP_NO_ACTION;
13791
13792     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13793     {
13794       if (player->push_delay_value == -1 || !player_was_pushing)
13795         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13796     }
13797     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13798     {
13799       if (player->push_delay_value == -1)
13800         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13801     }
13802     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13803     {
13804       if (!player->is_pushing)
13805         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13806     }
13807
13808     player->is_pushing = TRUE;
13809     player->is_active = TRUE;
13810
13811     if (!(IN_LEV_FIELD(nextx, nexty) &&
13812           (IS_FREE(nextx, nexty) ||
13813            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13814             IS_SB_ELEMENT(element)))))
13815       return MP_NO_ACTION;
13816
13817     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13818       return MP_NO_ACTION;
13819
13820     if (player->push_delay == -1)       /* new pushing; restart delay */
13821       player->push_delay = 0;
13822
13823     if (player->push_delay < player->push_delay_value &&
13824         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13825         element != EL_SPRING && element != EL_BALLOON)
13826     {
13827       /* make sure that there is no move delay before next try to push */
13828       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13829         player->move_delay = 0;
13830
13831       return MP_NO_ACTION;
13832     }
13833
13834     if (IS_SB_ELEMENT(element))
13835     {
13836       if (element == EL_SOKOBAN_FIELD_FULL)
13837       {
13838         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13839         local_player->sokobanfields_still_needed++;
13840       }
13841
13842       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13843       {
13844         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13845         local_player->sokobanfields_still_needed--;
13846       }
13847
13848       Feld[x][y] = EL_SOKOBAN_OBJECT;
13849
13850       if (Back[x][y] == Back[nextx][nexty])
13851         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13852       else if (Back[x][y] != 0)
13853         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13854                                     ACTION_EMPTYING);
13855       else
13856         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13857                                     ACTION_FILLING);
13858
13859       if (local_player->sokobanfields_still_needed == 0 &&
13860           game.emulation == EMU_SOKOBAN)
13861       {
13862         PlayerWins(player);
13863
13864         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13865       }
13866     }
13867     else
13868       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13869
13870     InitMovingField(x, y, move_direction);
13871     GfxAction[x][y] = ACTION_PUSHING;
13872
13873     if (mode == DF_SNAP)
13874       ContinueMoving(x, y);
13875     else
13876       MovPos[x][y] = (dx != 0 ? dx : dy);
13877
13878     Pushed[x][y] = TRUE;
13879     Pushed[nextx][nexty] = TRUE;
13880
13881     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13882       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13883     else
13884       player->push_delay_value = -1;    /* get new value later */
13885
13886     /* check for element change _after_ element has been pushed */
13887     if (game.use_change_when_pushing_bug)
13888     {
13889       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13890                                  player->index_bit, dig_side);
13891       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13892                                           player->index_bit, dig_side);
13893     }
13894   }
13895   else if (IS_SWITCHABLE(element))
13896   {
13897     if (PLAYER_SWITCHING(player, x, y))
13898     {
13899       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13900                                           player->index_bit, dig_side);
13901
13902       return MP_ACTION;
13903     }
13904
13905     player->is_switching = TRUE;
13906     player->switch_x = x;
13907     player->switch_y = y;
13908
13909     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13910
13911     if (element == EL_ROBOT_WHEEL)
13912     {
13913       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13914       ZX = x;
13915       ZY = y;
13916
13917       DrawLevelField(x, y);
13918     }
13919     else if (element == EL_SP_TERMINAL)
13920     {
13921       int xx, yy;
13922
13923       SCAN_PLAYFIELD(xx, yy)
13924       {
13925         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13926           Bang(xx, yy);
13927         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13928           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13929       }
13930     }
13931     else if (IS_BELT_SWITCH(element))
13932     {
13933       ToggleBeltSwitch(x, y);
13934     }
13935     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13936              element == EL_SWITCHGATE_SWITCH_DOWN ||
13937              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13938              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13939     {
13940       ToggleSwitchgateSwitch(x, y);
13941     }
13942     else if (element == EL_LIGHT_SWITCH ||
13943              element == EL_LIGHT_SWITCH_ACTIVE)
13944     {
13945       ToggleLightSwitch(x, y);
13946     }
13947     else if (element == EL_TIMEGATE_SWITCH ||
13948              element == EL_DC_TIMEGATE_SWITCH)
13949     {
13950       ActivateTimegateSwitch(x, y);
13951     }
13952     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13953              element == EL_BALLOON_SWITCH_RIGHT ||
13954              element == EL_BALLOON_SWITCH_UP    ||
13955              element == EL_BALLOON_SWITCH_DOWN  ||
13956              element == EL_BALLOON_SWITCH_NONE  ||
13957              element == EL_BALLOON_SWITCH_ANY)
13958     {
13959       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13960                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13961                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13962                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13963                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13964                              move_direction);
13965     }
13966     else if (element == EL_LAMP)
13967     {
13968       Feld[x][y] = EL_LAMP_ACTIVE;
13969       local_player->lights_still_needed--;
13970
13971       ResetGfxAnimation(x, y);
13972       DrawLevelField(x, y);
13973     }
13974     else if (element == EL_TIME_ORB_FULL)
13975     {
13976       Feld[x][y] = EL_TIME_ORB_EMPTY;
13977
13978       if (level.time > 0 || level.use_time_orb_bug)
13979       {
13980         TimeLeft += level.time_orb_time;
13981
13982 #if 1
13983         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13984
13985         DisplayGameControlValues();
13986 #else
13987         DrawGameValue_Time(TimeLeft);
13988 #endif
13989       }
13990
13991       ResetGfxAnimation(x, y);
13992       DrawLevelField(x, y);
13993     }
13994     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13995              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13996     {
13997       int xx, yy;
13998
13999       game.ball_state = !game.ball_state;
14000
14001       SCAN_PLAYFIELD(xx, yy)
14002       {
14003         int e = Feld[xx][yy];
14004
14005         if (game.ball_state)
14006         {
14007           if (e == EL_EMC_MAGIC_BALL)
14008             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14009           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14010             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14011         }
14012         else
14013         {
14014           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14015             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14016           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14017             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14018         }
14019       }
14020     }
14021
14022     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14023                                         player->index_bit, dig_side);
14024
14025     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14026                                         player->index_bit, dig_side);
14027
14028     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14029                                         player->index_bit, dig_side);
14030
14031     return MP_ACTION;
14032   }
14033   else
14034   {
14035     if (!PLAYER_SWITCHING(player, x, y))
14036     {
14037       player->is_switching = TRUE;
14038       player->switch_x = x;
14039       player->switch_y = y;
14040
14041       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14042                                  player->index_bit, dig_side);
14043       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14044                                           player->index_bit, dig_side);
14045
14046       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14047                                  player->index_bit, dig_side);
14048       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14049                                           player->index_bit, dig_side);
14050     }
14051
14052     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14053                                player->index_bit, dig_side);
14054     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14055                                         player->index_bit, dig_side);
14056
14057     return MP_NO_ACTION;
14058   }
14059
14060   player->push_delay = -1;
14061
14062   if (is_player)                /* function can also be called by EL_PENGUIN */
14063   {
14064     if (Feld[x][y] != element)          /* really digged/collected something */
14065     {
14066       player->is_collecting = !player->is_digging;
14067       player->is_active = TRUE;
14068     }
14069   }
14070
14071   return MP_MOVING;
14072 }
14073
14074 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14075 {
14076   int jx = player->jx, jy = player->jy;
14077   int x = jx + dx, y = jy + dy;
14078   int snap_direction = (dx == -1 ? MV_LEFT  :
14079                         dx == +1 ? MV_RIGHT :
14080                         dy == -1 ? MV_UP    :
14081                         dy == +1 ? MV_DOWN  : MV_NONE);
14082   boolean can_continue_snapping = (level.continuous_snapping &&
14083                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14084
14085   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14086     return FALSE;
14087
14088   if (!player->active || !IN_LEV_FIELD(x, y))
14089     return FALSE;
14090
14091   if (dx && dy)
14092     return FALSE;
14093
14094   if (!dx && !dy)
14095   {
14096     if (player->MovPos == 0)
14097       player->is_pushing = FALSE;
14098
14099     player->is_snapping = FALSE;
14100
14101     if (player->MovPos == 0)
14102     {
14103       player->is_moving = FALSE;
14104       player->is_digging = FALSE;
14105       player->is_collecting = FALSE;
14106     }
14107
14108     return FALSE;
14109   }
14110
14111 #if USE_NEW_CONTINUOUS_SNAPPING
14112   /* prevent snapping with already pressed snap key when not allowed */
14113   if (player->is_snapping && !can_continue_snapping)
14114     return FALSE;
14115 #else
14116   if (player->is_snapping)
14117     return FALSE;
14118 #endif
14119
14120   player->MovDir = snap_direction;
14121
14122   if (player->MovPos == 0)
14123   {
14124     player->is_moving = FALSE;
14125     player->is_digging = FALSE;
14126     player->is_collecting = FALSE;
14127   }
14128
14129   player->is_dropping = FALSE;
14130   player->is_dropping_pressed = FALSE;
14131   player->drop_pressed_delay = 0;
14132
14133   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14134     return FALSE;
14135
14136   player->is_snapping = TRUE;
14137   player->is_active = TRUE;
14138
14139   if (player->MovPos == 0)
14140   {
14141     player->is_moving = FALSE;
14142     player->is_digging = FALSE;
14143     player->is_collecting = FALSE;
14144   }
14145
14146   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14147     DrawLevelField(player->last_jx, player->last_jy);
14148
14149   DrawLevelField(x, y);
14150
14151   return TRUE;
14152 }
14153
14154 boolean DropElement(struct PlayerInfo *player)
14155 {
14156   int old_element, new_element;
14157   int dropx = player->jx, dropy = player->jy;
14158   int drop_direction = player->MovDir;
14159   int drop_side = drop_direction;
14160 #if 1
14161   int drop_element = get_next_drop_element(player);
14162 #else
14163   int drop_element = (player->inventory_size > 0 ?
14164                       player->inventory_element[player->inventory_size - 1] :
14165                       player->inventory_infinite_element != EL_UNDEFINED ?
14166                       player->inventory_infinite_element :
14167                       player->dynabombs_left > 0 ?
14168                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14169                       EL_UNDEFINED);
14170 #endif
14171
14172   player->is_dropping_pressed = TRUE;
14173
14174   /* do not drop an element on top of another element; when holding drop key
14175      pressed without moving, dropped element must move away before the next
14176      element can be dropped (this is especially important if the next element
14177      is dynamite, which can be placed on background for historical reasons) */
14178   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14179     return MP_ACTION;
14180
14181   if (IS_THROWABLE(drop_element))
14182   {
14183     dropx += GET_DX_FROM_DIR(drop_direction);
14184     dropy += GET_DY_FROM_DIR(drop_direction);
14185
14186     if (!IN_LEV_FIELD(dropx, dropy))
14187       return FALSE;
14188   }
14189
14190   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14191   new_element = drop_element;           /* default: no change when dropping */
14192
14193   /* check if player is active, not moving and ready to drop */
14194   if (!player->active || player->MovPos || player->drop_delay > 0)
14195     return FALSE;
14196
14197   /* check if player has anything that can be dropped */
14198   if (new_element == EL_UNDEFINED)
14199     return FALSE;
14200
14201   /* check if drop key was pressed long enough for EM style dynamite */
14202   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14203     return FALSE;
14204
14205   /* check if anything can be dropped at the current position */
14206   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14207     return FALSE;
14208
14209   /* collected custom elements can only be dropped on empty fields */
14210   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14211     return FALSE;
14212
14213   if (old_element != EL_EMPTY)
14214     Back[dropx][dropy] = old_element;   /* store old element on this field */
14215
14216   ResetGfxAnimation(dropx, dropy);
14217   ResetRandomAnimationValue(dropx, dropy);
14218
14219   if (player->inventory_size > 0 ||
14220       player->inventory_infinite_element != EL_UNDEFINED)
14221   {
14222     if (player->inventory_size > 0)
14223     {
14224       player->inventory_size--;
14225
14226       DrawGameDoorValues();
14227
14228       if (new_element == EL_DYNAMITE)
14229         new_element = EL_DYNAMITE_ACTIVE;
14230       else if (new_element == EL_EM_DYNAMITE)
14231         new_element = EL_EM_DYNAMITE_ACTIVE;
14232       else if (new_element == EL_SP_DISK_RED)
14233         new_element = EL_SP_DISK_RED_ACTIVE;
14234     }
14235
14236     Feld[dropx][dropy] = new_element;
14237
14238     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14239       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14240                           el2img(Feld[dropx][dropy]), 0);
14241
14242     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14243
14244     /* needed if previous element just changed to "empty" in the last frame */
14245     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14246
14247     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14248                                player->index_bit, drop_side);
14249     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14250                                         CE_PLAYER_DROPS_X,
14251                                         player->index_bit, drop_side);
14252
14253     TestIfElementTouchesCustomElement(dropx, dropy);
14254   }
14255   else          /* player is dropping a dyna bomb */
14256   {
14257     player->dynabombs_left--;
14258
14259     Feld[dropx][dropy] = new_element;
14260
14261     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14262       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14263                           el2img(Feld[dropx][dropy]), 0);
14264
14265     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14266   }
14267
14268   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14269     InitField_WithBug1(dropx, dropy, FALSE);
14270
14271   new_element = Feld[dropx][dropy];     /* element might have changed */
14272
14273   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14274       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14275   {
14276     int move_direction, nextx, nexty;
14277
14278     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14279       MovDir[dropx][dropy] = drop_direction;
14280
14281     move_direction = MovDir[dropx][dropy];
14282     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14283     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14284
14285     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14286
14287 #if USE_FIX_IMPACT_COLLISION
14288     /* do not cause impact style collision by dropping elements that can fall */
14289     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14290 #else
14291     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14292 #endif
14293   }
14294
14295   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14296   player->is_dropping = TRUE;
14297
14298   player->drop_pressed_delay = 0;
14299   player->is_dropping_pressed = FALSE;
14300
14301   player->drop_x = dropx;
14302   player->drop_y = dropy;
14303
14304   return TRUE;
14305 }
14306
14307 /* ------------------------------------------------------------------------- */
14308 /* game sound playing functions                                              */
14309 /* ------------------------------------------------------------------------- */
14310
14311 static int *loop_sound_frame = NULL;
14312 static int *loop_sound_volume = NULL;
14313
14314 void InitPlayLevelSound()
14315 {
14316   int num_sounds = getSoundListSize();
14317
14318   checked_free(loop_sound_frame);
14319   checked_free(loop_sound_volume);
14320
14321   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14322   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14323 }
14324
14325 static void PlayLevelSound(int x, int y, int nr)
14326 {
14327   int sx = SCREENX(x), sy = SCREENY(y);
14328   int volume, stereo_position;
14329   int max_distance = 8;
14330   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14331
14332   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14333       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14334     return;
14335
14336   if (!IN_LEV_FIELD(x, y) ||
14337       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14338       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14339     return;
14340
14341   volume = SOUND_MAX_VOLUME;
14342
14343   if (!IN_SCR_FIELD(sx, sy))
14344   {
14345     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14346     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14347
14348     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14349   }
14350
14351   stereo_position = (SOUND_MAX_LEFT +
14352                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14353                      (SCR_FIELDX + 2 * max_distance));
14354
14355   if (IS_LOOP_SOUND(nr))
14356   {
14357     /* This assures that quieter loop sounds do not overwrite louder ones,
14358        while restarting sound volume comparison with each new game frame. */
14359
14360     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14361       return;
14362
14363     loop_sound_volume[nr] = volume;
14364     loop_sound_frame[nr] = FrameCounter;
14365   }
14366
14367   PlaySoundExt(nr, volume, stereo_position, type);
14368 }
14369
14370 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14371 {
14372   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14373                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14374                  y < LEVELY(BY1) ? LEVELY(BY1) :
14375                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14376                  sound_action);
14377 }
14378
14379 static void PlayLevelSoundAction(int x, int y, int action)
14380 {
14381   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14382 }
14383
14384 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14385 {
14386   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14387
14388   if (sound_effect != SND_UNDEFINED)
14389     PlayLevelSound(x, y, sound_effect);
14390 }
14391
14392 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14393                                               int action)
14394 {
14395   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14396
14397   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14398     PlayLevelSound(x, y, sound_effect);
14399 }
14400
14401 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14402 {
14403   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14404
14405   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14406     PlayLevelSound(x, y, sound_effect);
14407 }
14408
14409 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14410 {
14411   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14412
14413   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14414     StopSound(sound_effect);
14415 }
14416
14417 static void PlayLevelMusic()
14418 {
14419   if (levelset.music[level_nr] != MUS_UNDEFINED)
14420     PlayMusic(levelset.music[level_nr]);        /* from config file */
14421   else
14422     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14423 }
14424
14425 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14426 {
14427   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14428   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14429   int x = xx - 1 - offset;
14430   int y = yy - 1 - offset;
14431
14432   switch (sample)
14433   {
14434     case SAMPLE_blank:
14435       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14436       break;
14437
14438     case SAMPLE_roll:
14439       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14440       break;
14441
14442     case SAMPLE_stone:
14443       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14444       break;
14445
14446     case SAMPLE_nut:
14447       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14448       break;
14449
14450     case SAMPLE_crack:
14451       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14452       break;
14453
14454     case SAMPLE_bug:
14455       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14456       break;
14457
14458     case SAMPLE_tank:
14459       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14460       break;
14461
14462     case SAMPLE_android_clone:
14463       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14464       break;
14465
14466     case SAMPLE_android_move:
14467       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14468       break;
14469
14470     case SAMPLE_spring:
14471       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14472       break;
14473
14474     case SAMPLE_slurp:
14475       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14476       break;
14477
14478     case SAMPLE_eater:
14479       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14480       break;
14481
14482     case SAMPLE_eater_eat:
14483       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14484       break;
14485
14486     case SAMPLE_alien:
14487       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14488       break;
14489
14490     case SAMPLE_collect:
14491       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14492       break;
14493
14494     case SAMPLE_diamond:
14495       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14496       break;
14497
14498     case SAMPLE_squash:
14499       /* !!! CHECK THIS !!! */
14500 #if 1
14501       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14502 #else
14503       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14504 #endif
14505       break;
14506
14507     case SAMPLE_wonderfall:
14508       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14509       break;
14510
14511     case SAMPLE_drip:
14512       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14513       break;
14514
14515     case SAMPLE_push:
14516       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14517       break;
14518
14519     case SAMPLE_dirt:
14520       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14521       break;
14522
14523     case SAMPLE_acid:
14524       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14525       break;
14526
14527     case SAMPLE_ball:
14528       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14529       break;
14530
14531     case SAMPLE_grow:
14532       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14533       break;
14534
14535     case SAMPLE_wonder:
14536       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14537       break;
14538
14539     case SAMPLE_door:
14540       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14541       break;
14542
14543     case SAMPLE_exit_open:
14544       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14545       break;
14546
14547     case SAMPLE_exit_leave:
14548       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14549       break;
14550
14551     case SAMPLE_dynamite:
14552       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14553       break;
14554
14555     case SAMPLE_tick:
14556       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14557       break;
14558
14559     case SAMPLE_press:
14560       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14561       break;
14562
14563     case SAMPLE_wheel:
14564       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14565       break;
14566
14567     case SAMPLE_boom:
14568       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14569       break;
14570
14571     case SAMPLE_die:
14572       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14573       break;
14574
14575     case SAMPLE_time:
14576       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14577       break;
14578
14579     default:
14580       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14581       break;
14582   }
14583 }
14584
14585 #if 0
14586 void ChangeTime(int value)
14587 {
14588   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14589
14590   *time += value;
14591
14592   /* EMC game engine uses value from time counter of RND game engine */
14593   level.native_em_level->lev->time = *time;
14594
14595   DrawGameValue_Time(*time);
14596 }
14597
14598 void RaiseScore(int value)
14599 {
14600   /* EMC game engine and RND game engine have separate score counters */
14601   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14602                 &level.native_em_level->lev->score : &local_player->score);
14603
14604   *score += value;
14605
14606   DrawGameValue_Score(*score);
14607 }
14608 #endif
14609
14610 void RaiseScore(int value)
14611 {
14612   local_player->score += value;
14613
14614 #if 1
14615   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14616
14617   DisplayGameControlValues();
14618 #else
14619   DrawGameValue_Score(local_player->score);
14620 #endif
14621 }
14622
14623 void RaiseScoreElement(int element)
14624 {
14625   switch (element)
14626   {
14627     case EL_EMERALD:
14628     case EL_BD_DIAMOND:
14629     case EL_EMERALD_YELLOW:
14630     case EL_EMERALD_RED:
14631     case EL_EMERALD_PURPLE:
14632     case EL_SP_INFOTRON:
14633       RaiseScore(level.score[SC_EMERALD]);
14634       break;
14635     case EL_DIAMOND:
14636       RaiseScore(level.score[SC_DIAMOND]);
14637       break;
14638     case EL_CRYSTAL:
14639       RaiseScore(level.score[SC_CRYSTAL]);
14640       break;
14641     case EL_PEARL:
14642       RaiseScore(level.score[SC_PEARL]);
14643       break;
14644     case EL_BUG:
14645     case EL_BD_BUTTERFLY:
14646     case EL_SP_ELECTRON:
14647       RaiseScore(level.score[SC_BUG]);
14648       break;
14649     case EL_SPACESHIP:
14650     case EL_BD_FIREFLY:
14651     case EL_SP_SNIKSNAK:
14652       RaiseScore(level.score[SC_SPACESHIP]);
14653       break;
14654     case EL_YAMYAM:
14655     case EL_DARK_YAMYAM:
14656       RaiseScore(level.score[SC_YAMYAM]);
14657       break;
14658     case EL_ROBOT:
14659       RaiseScore(level.score[SC_ROBOT]);
14660       break;
14661     case EL_PACMAN:
14662       RaiseScore(level.score[SC_PACMAN]);
14663       break;
14664     case EL_NUT:
14665       RaiseScore(level.score[SC_NUT]);
14666       break;
14667     case EL_DYNAMITE:
14668     case EL_EM_DYNAMITE:
14669     case EL_SP_DISK_RED:
14670     case EL_DYNABOMB_INCREASE_NUMBER:
14671     case EL_DYNABOMB_INCREASE_SIZE:
14672     case EL_DYNABOMB_INCREASE_POWER:
14673       RaiseScore(level.score[SC_DYNAMITE]);
14674       break;
14675     case EL_SHIELD_NORMAL:
14676     case EL_SHIELD_DEADLY:
14677       RaiseScore(level.score[SC_SHIELD]);
14678       break;
14679     case EL_EXTRA_TIME:
14680       RaiseScore(level.extra_time_score);
14681       break;
14682     case EL_KEY_1:
14683     case EL_KEY_2:
14684     case EL_KEY_3:
14685     case EL_KEY_4:
14686     case EL_EM_KEY_1:
14687     case EL_EM_KEY_2:
14688     case EL_EM_KEY_3:
14689     case EL_EM_KEY_4:
14690     case EL_EMC_KEY_5:
14691     case EL_EMC_KEY_6:
14692     case EL_EMC_KEY_7:
14693     case EL_EMC_KEY_8:
14694     case EL_DC_KEY_WHITE:
14695       RaiseScore(level.score[SC_KEY]);
14696       break;
14697     default:
14698       RaiseScore(element_info[element].collect_score);
14699       break;
14700   }
14701 }
14702
14703 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14704 {
14705   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14706   {
14707 #if defined(NETWORK_AVALIABLE)
14708     if (options.network)
14709       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14710     else
14711 #endif
14712     {
14713       if (quick_quit)
14714       {
14715 #if 1
14716
14717 #if 1
14718         FadeSkipNextFadeIn();
14719 #else
14720         fading = fading_none;
14721 #endif
14722
14723 #else
14724         OpenDoor(DOOR_CLOSE_1);
14725 #endif
14726
14727         game_status = GAME_MODE_MAIN;
14728
14729 #if 1
14730         DrawAndFadeInMainMenu(REDRAW_FIELD);
14731 #else
14732         DrawMainMenu();
14733 #endif
14734       }
14735       else
14736       {
14737 #if 0
14738         FadeOut(REDRAW_FIELD);
14739 #endif
14740
14741         game_status = GAME_MODE_MAIN;
14742
14743         DrawAndFadeInMainMenu(REDRAW_FIELD);
14744       }
14745     }
14746   }
14747   else          /* continue playing the game */
14748   {
14749     if (tape.playing && tape.deactivate_display)
14750       TapeDeactivateDisplayOff(TRUE);
14751
14752     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14753
14754     if (tape.playing && tape.deactivate_display)
14755       TapeDeactivateDisplayOn();
14756   }
14757 }
14758
14759 void RequestQuitGame(boolean ask_if_really_quit)
14760 {
14761   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14762   boolean skip_request = AllPlayersGone || quick_quit;
14763
14764   RequestQuitGameExt(skip_request, quick_quit,
14765                      "Do you really want to quit the game ?");
14766 }
14767
14768
14769 /* ------------------------------------------------------------------------- */
14770 /* random generator functions                                                */
14771 /* ------------------------------------------------------------------------- */
14772
14773 unsigned int InitEngineRandom_RND(long seed)
14774 {
14775   game.num_random_calls = 0;
14776
14777 #if 0
14778   unsigned int rnd_seed = InitEngineRandom(seed);
14779
14780   printf("::: START RND: %d\n", rnd_seed);
14781
14782   return rnd_seed;
14783 #else
14784
14785   return InitEngineRandom(seed);
14786
14787 #endif
14788
14789 }
14790
14791 unsigned int RND(int max)
14792 {
14793   if (max > 0)
14794   {
14795     game.num_random_calls++;
14796
14797     return GetEngineRandom(max);
14798   }
14799
14800   return 0;
14801 }
14802
14803
14804 /* ------------------------------------------------------------------------- */
14805 /* game engine snapshot handling functions                                   */
14806 /* ------------------------------------------------------------------------- */
14807
14808 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14809
14810 struct EngineSnapshotInfo
14811 {
14812   /* runtime values for custom element collect score */
14813   int collect_score[NUM_CUSTOM_ELEMENTS];
14814
14815   /* runtime values for group element choice position */
14816   int choice_pos[NUM_GROUP_ELEMENTS];
14817
14818   /* runtime values for belt position animations */
14819   int belt_graphic[4 * NUM_BELT_PARTS];
14820   int belt_anim_mode[4 * NUM_BELT_PARTS];
14821 };
14822
14823 struct EngineSnapshotNodeInfo
14824 {
14825   void *buffer_orig;
14826   void *buffer_copy;
14827   int size;
14828 };
14829
14830 static struct EngineSnapshotInfo engine_snapshot_rnd;
14831 static ListNode *engine_snapshot_list = NULL;
14832 static char *snapshot_level_identifier = NULL;
14833 static int snapshot_level_nr = -1;
14834
14835 void FreeEngineSnapshot()
14836 {
14837   while (engine_snapshot_list != NULL)
14838     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14839                        checked_free);
14840
14841   setString(&snapshot_level_identifier, NULL);
14842   snapshot_level_nr = -1;
14843 }
14844
14845 static void SaveEngineSnapshotValues_RND()
14846 {
14847   static int belt_base_active_element[4] =
14848   {
14849     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14850     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14851     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14852     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14853   };
14854   int i, j;
14855
14856   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14857   {
14858     int element = EL_CUSTOM_START + i;
14859
14860     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14861   }
14862
14863   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14864   {
14865     int element = EL_GROUP_START + i;
14866
14867     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14868   }
14869
14870   for (i = 0; i < 4; i++)
14871   {
14872     for (j = 0; j < NUM_BELT_PARTS; j++)
14873     {
14874       int element = belt_base_active_element[i] + j;
14875       int graphic = el2img(element);
14876       int anim_mode = graphic_info[graphic].anim_mode;
14877
14878       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14879       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14880     }
14881   }
14882 }
14883
14884 static void LoadEngineSnapshotValues_RND()
14885 {
14886   unsigned long num_random_calls = game.num_random_calls;
14887   int i, j;
14888
14889   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14890   {
14891     int element = EL_CUSTOM_START + i;
14892
14893     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14894   }
14895
14896   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14897   {
14898     int element = EL_GROUP_START + i;
14899
14900     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14901   }
14902
14903   for (i = 0; i < 4; i++)
14904   {
14905     for (j = 0; j < NUM_BELT_PARTS; j++)
14906     {
14907       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14908       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14909
14910       graphic_info[graphic].anim_mode = anim_mode;
14911     }
14912   }
14913
14914   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14915   {
14916     InitRND(tape.random_seed);
14917     for (i = 0; i < num_random_calls; i++)
14918       RND(1);
14919   }
14920
14921   if (game.num_random_calls != num_random_calls)
14922   {
14923     Error(ERR_INFO, "number of random calls out of sync");
14924     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14925     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14926     Error(ERR_EXIT, "this should not happen -- please debug");
14927   }
14928 }
14929
14930 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14931 {
14932   struct EngineSnapshotNodeInfo *bi =
14933     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14934
14935   bi->buffer_orig = buffer;
14936   bi->buffer_copy = checked_malloc(size);
14937   bi->size = size;
14938
14939   memcpy(bi->buffer_copy, buffer, size);
14940
14941   addNodeToList(&engine_snapshot_list, NULL, bi);
14942 }
14943
14944 void SaveEngineSnapshot()
14945 {
14946   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14947
14948   if (level_editor_test_game)   /* do not save snapshots from editor */
14949     return;
14950
14951   /* copy some special values to a structure better suited for the snapshot */
14952
14953   SaveEngineSnapshotValues_RND();
14954   SaveEngineSnapshotValues_EM();
14955
14956   /* save values stored in special snapshot structure */
14957
14958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14959   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14960
14961   /* save further RND engine values */
14962
14963   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14964   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14966
14967   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14968   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14971
14972   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14973   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14974   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14976   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14977
14978   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14981
14982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14983
14984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14985
14986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14988
14989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14996   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15000   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15003   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15005   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15007
15008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15009   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15010
15011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15012   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15013   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15014
15015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15016   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15017
15018   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15019   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15020   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15021   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15022   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15023
15024   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15025   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15026
15027   /* save level identification information */
15028
15029   setString(&snapshot_level_identifier, leveldir_current->identifier);
15030   snapshot_level_nr = level_nr;
15031
15032 #if 0
15033   ListNode *node = engine_snapshot_list;
15034   int num_bytes = 0;
15035
15036   while (node != NULL)
15037   {
15038     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15039
15040     node = node->next;
15041   }
15042
15043   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15044 #endif
15045 }
15046
15047 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15048 {
15049   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15050 }
15051
15052 void LoadEngineSnapshot()
15053 {
15054   ListNode *node = engine_snapshot_list;
15055
15056   if (engine_snapshot_list == NULL)
15057     return;
15058
15059   while (node != NULL)
15060   {
15061     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15062
15063     node = node->next;
15064   }
15065
15066   /* restore special values from snapshot structure */
15067
15068   LoadEngineSnapshotValues_RND();
15069   LoadEngineSnapshotValues_EM();
15070 }
15071
15072 boolean CheckEngineSnapshot()
15073 {
15074   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15075           snapshot_level_nr == level_nr);
15076 }
15077
15078
15079 /* ---------- new game button stuff ---------------------------------------- */
15080
15081 /* graphic position values for game buttons */
15082 #define GAME_BUTTON_XSIZE       30
15083 #define GAME_BUTTON_YSIZE       30
15084 #define GAME_BUTTON_XPOS        5
15085 #define GAME_BUTTON_YPOS        215
15086 #define SOUND_BUTTON_XPOS       5
15087 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15088
15089 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15090 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15091 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15092 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15093 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15094 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15095
15096 static struct
15097 {
15098   int *x, *y;
15099   int gd_x, gd_y;
15100   int gadget_id;
15101   char *infotext;
15102 } gamebutton_info[NUM_GAME_BUTTONS] =
15103 {
15104 #if 1
15105   {
15106     &game.button.stop.x,        &game.button.stop.y,
15107     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15108     GAME_CTRL_ID_STOP,
15109     "stop game"
15110   },
15111   {
15112     &game.button.pause.x,       &game.button.pause.y,
15113     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15114     GAME_CTRL_ID_PAUSE,
15115     "pause game"
15116   },
15117   {
15118     &game.button.play.x,        &game.button.play.y,
15119     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15120     GAME_CTRL_ID_PLAY,
15121     "play game"
15122   },
15123   {
15124     &game.button.sound_music.x, &game.button.sound_music.y,
15125     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15126     SOUND_CTRL_ID_MUSIC,
15127     "background music on/off"
15128   },
15129   {
15130     &game.button.sound_loops.x, &game.button.sound_loops.y,
15131     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15132     SOUND_CTRL_ID_LOOPS,
15133     "sound loops on/off"
15134   },
15135   {
15136     &game.button.sound_simple.x,&game.button.sound_simple.y,
15137     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15138     SOUND_CTRL_ID_SIMPLE,
15139     "normal sounds on/off"
15140   }
15141 #else
15142   {
15143     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15144     GAME_CTRL_ID_STOP,
15145     "stop game"
15146   },
15147   {
15148     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15149     GAME_CTRL_ID_PAUSE,
15150     "pause game"
15151   },
15152   {
15153     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15154     GAME_CTRL_ID_PLAY,
15155     "play game"
15156   },
15157   {
15158     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15159     SOUND_CTRL_ID_MUSIC,
15160     "background music on/off"
15161   },
15162   {
15163     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15164     SOUND_CTRL_ID_LOOPS,
15165     "sound loops on/off"
15166   },
15167   {
15168     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15169     SOUND_CTRL_ID_SIMPLE,
15170     "normal sounds on/off"
15171   }
15172 #endif
15173 };
15174
15175 void CreateGameButtons()
15176 {
15177   int i;
15178
15179   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15180   {
15181     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15182     struct GadgetInfo *gi;
15183     int button_type;
15184     boolean checked;
15185     unsigned long event_mask;
15186     int x, y;
15187     int gd_xoffset, gd_yoffset;
15188     int gd_x1, gd_x2, gd_y1, gd_y2;
15189     int id = i;
15190
15191     x = DX + *gamebutton_info[i].x;
15192     y = DY + *gamebutton_info[i].y;
15193     gd_xoffset = gamebutton_info[i].gd_x;
15194     gd_yoffset = gamebutton_info[i].gd_y;
15195     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15196     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15197
15198     if (id == GAME_CTRL_ID_STOP ||
15199         id == GAME_CTRL_ID_PAUSE ||
15200         id == GAME_CTRL_ID_PLAY)
15201     {
15202       button_type = GD_TYPE_NORMAL_BUTTON;
15203       checked = FALSE;
15204       event_mask = GD_EVENT_RELEASED;
15205       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15206       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15207     }
15208     else
15209     {
15210       button_type = GD_TYPE_CHECK_BUTTON;
15211       checked =
15212         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15213          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15214          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15215       event_mask = GD_EVENT_PRESSED;
15216       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15217       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15218     }
15219
15220     gi = CreateGadget(GDI_CUSTOM_ID, id,
15221                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15222 #if 1
15223                       GDI_X, x,
15224                       GDI_Y, y,
15225 #else
15226                       GDI_X, DX + gd_xoffset,
15227                       GDI_Y, DY + gd_yoffset,
15228 #endif
15229                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15230                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15231                       GDI_TYPE, button_type,
15232                       GDI_STATE, GD_BUTTON_UNPRESSED,
15233                       GDI_CHECKED, checked,
15234                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15235                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15236                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15237                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15238                       GDI_EVENT_MASK, event_mask,
15239                       GDI_CALLBACK_ACTION, HandleGameButtons,
15240                       GDI_END);
15241
15242     if (gi == NULL)
15243       Error(ERR_EXIT, "cannot create gadget");
15244
15245     game_gadget[id] = gi;
15246   }
15247 }
15248
15249 void FreeGameButtons()
15250 {
15251   int i;
15252
15253   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15254     FreeGadget(game_gadget[i]);
15255 }
15256
15257 static void MapGameButtons()
15258 {
15259   int i;
15260
15261   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15262     MapGadget(game_gadget[i]);
15263 }
15264
15265 void UnmapGameButtons()
15266 {
15267   int i;
15268
15269   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15270     UnmapGadget(game_gadget[i]);
15271 }
15272
15273 static void HandleGameButtons(struct GadgetInfo *gi)
15274 {
15275   int id = gi->custom_id;
15276
15277   if (game_status != GAME_MODE_PLAYING)
15278     return;
15279
15280   switch (id)
15281   {
15282     case GAME_CTRL_ID_STOP:
15283       if (tape.playing)
15284         TapeStop();
15285       else
15286         RequestQuitGame(TRUE);
15287       break;
15288
15289     case GAME_CTRL_ID_PAUSE:
15290       if (options.network)
15291       {
15292 #if defined(NETWORK_AVALIABLE)
15293         if (tape.pausing)
15294           SendToServer_ContinuePlaying();
15295         else
15296           SendToServer_PausePlaying();
15297 #endif
15298       }
15299       else
15300         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15301       break;
15302
15303     case GAME_CTRL_ID_PLAY:
15304       if (tape.pausing)
15305       {
15306 #if defined(NETWORK_AVALIABLE)
15307         if (options.network)
15308           SendToServer_ContinuePlaying();
15309         else
15310 #endif
15311         {
15312           tape.pausing = FALSE;
15313           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15314         }
15315       }
15316       break;
15317
15318     case SOUND_CTRL_ID_MUSIC:
15319       if (setup.sound_music)
15320       { 
15321         setup.sound_music = FALSE;
15322         FadeMusic();
15323       }
15324       else if (audio.music_available)
15325       { 
15326         setup.sound = setup.sound_music = TRUE;
15327
15328         SetAudioMode(setup.sound);
15329
15330         PlayLevelMusic();
15331       }
15332       break;
15333
15334     case SOUND_CTRL_ID_LOOPS:
15335       if (setup.sound_loops)
15336         setup.sound_loops = FALSE;
15337       else if (audio.loops_available)
15338       {
15339         setup.sound = setup.sound_loops = TRUE;
15340         SetAudioMode(setup.sound);
15341       }
15342       break;
15343
15344     case SOUND_CTRL_ID_SIMPLE:
15345       if (setup.sound_simple)
15346         setup.sound_simple = FALSE;
15347       else if (audio.sound_available)
15348       {
15349         setup.sound = setup.sound_simple = TRUE;
15350         SetAudioMode(setup.sound);
15351       }
15352       break;
15353
15354     default:
15355       break;
15356   }
15357 }