6b687642f6e6950495f39241f635096d53df6946
[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_EMC_MAGIC_BALL             39
174 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      40
175 #define GAME_CONTROL_LIGHT_SWITCH               41
176 #define GAME_CONTROL_LIGHT_SWITCH_TIME          42
177 #define GAME_CONTROL_TIMEGATE_SWITCH            43
178 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       44
179 #define GAME_CONTROL_SWITCHGATE_SWITCH          45
180 #define GAME_CONTROL_EMC_LENSES                 46
181 #define GAME_CONTROL_EMC_LENSES_TIME            47
182 #define GAME_CONTROL_EMC_MAGNIFIER              48
183 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         49
184 #define GAME_CONTROL_BALLOON_SWITCH             50
185 #define GAME_CONTROL_DYNABOMB_NUMBER            51
186 #define GAME_CONTROL_DYNABOMB_SIZE              52
187 #define GAME_CONTROL_DYNABOMB_POWER             53
188 #define GAME_CONTROL_PENGUINS                   54
189 #define GAME_CONTROL_SOKOBAN_OBJECTS            55
190 #define GAME_CONTROL_SOKOBAN_FIELDS             56
191 #define GAME_CONTROL_ROBOT_WHEEL                57
192 #define GAME_CONTROL_CONVEYOR_BELT_1            58
193 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     59
194 #define GAME_CONTROL_CONVEYOR_BELT_2            60
195 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     61
196 #define GAME_CONTROL_CONVEYOR_BELT_3            62
197 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     63
198 #define GAME_CONTROL_CONVEYOR_BELT_4            64
199 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     65
200 #define GAME_CONTROL_MAGIC_WALL                 66
201 #define GAME_CONTROL_MAGIC_WALL_TIME            67
202 #define GAME_CONTROL_GRAVITY_STATE              68
203 #define GAME_CONTROL_PLAYER_NAME                69
204 #define GAME_CONTROL_LEVEL_NAME                 70
205 #define GAME_CONTROL_LEVEL_AUTHOR               71
206
207 #define NUM_GAME_CONTROLS                       72
208
209 int game_control_value[NUM_GAME_CONTROLS];
210 int last_game_control_value[NUM_GAME_CONTROLS];
211
212 struct GameControlInfo
213 {
214   int nr;
215
216   struct TextPosInfo *pos;
217   int type;
218 };
219
220 static struct GameControlInfo game_controls[] =
221 {
222   {
223     GAME_CONTROL_LEVEL_NUMBER,
224     &game.panel.level_number,
225     TYPE_INTEGER,
226   },
227   {
228     GAME_CONTROL_GEMS,
229     &game.panel.gems,
230     TYPE_INTEGER,
231   },
232   {
233     GAME_CONTROL_INVENTORY_COUNT,
234     &game.panel.inventory_count,
235     TYPE_INTEGER,
236   },
237   {
238     GAME_CONTROL_INVENTORY_FIRST_1,
239     &game.panel.inventory_first_1,
240     TYPE_ELEMENT,
241   },
242   {
243     GAME_CONTROL_INVENTORY_FIRST_2,
244     &game.panel.inventory_first_2,
245     TYPE_ELEMENT,
246   },
247   {
248     GAME_CONTROL_INVENTORY_FIRST_3,
249     &game.panel.inventory_first_3,
250     TYPE_ELEMENT,
251   },
252   {
253     GAME_CONTROL_INVENTORY_FIRST_4,
254     &game.panel.inventory_first_4,
255     TYPE_ELEMENT,
256   },
257   {
258     GAME_CONTROL_INVENTORY_FIRST_5,
259     &game.panel.inventory_first_5,
260     TYPE_ELEMENT,
261   },
262   {
263     GAME_CONTROL_INVENTORY_FIRST_6,
264     &game.panel.inventory_first_6,
265     TYPE_ELEMENT,
266   },
267   {
268     GAME_CONTROL_INVENTORY_FIRST_7,
269     &game.panel.inventory_first_7,
270     TYPE_ELEMENT,
271   },
272   {
273     GAME_CONTROL_INVENTORY_FIRST_8,
274     &game.panel.inventory_first_8,
275     TYPE_ELEMENT,
276   },
277   {
278     GAME_CONTROL_INVENTORY_LAST_1,
279     &game.panel.inventory_last_1,
280     TYPE_ELEMENT,
281   },
282   {
283     GAME_CONTROL_INVENTORY_LAST_2,
284     &game.panel.inventory_last_2,
285     TYPE_ELEMENT,
286   },
287   {
288     GAME_CONTROL_INVENTORY_LAST_3,
289     &game.panel.inventory_last_3,
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_CONTROL_INVENTORY_LAST_4,
294     &game.panel.inventory_last_4,
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_CONTROL_INVENTORY_LAST_5,
299     &game.panel.inventory_last_5,
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_CONTROL_INVENTORY_LAST_6,
304     &game.panel.inventory_last_6,
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_CONTROL_INVENTORY_LAST_7,
309     &game.panel.inventory_last_7,
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_CONTROL_INVENTORY_LAST_8,
314     &game.panel.inventory_last_8,
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_CONTROL_KEY_1,
319     &game.panel.key[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_CONTROL_KEY_2,
324     &game.panel.key[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_CONTROL_KEY_3,
329     &game.panel.key[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_CONTROL_KEY_4,
334     &game.panel.key[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_CONTROL_KEY_5,
339     &game.panel.key[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_CONTROL_KEY_6,
344     &game.panel.key[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_CONTROL_KEY_7,
349     &game.panel.key[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_CONTROL_KEY_8,
354     &game.panel.key[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_CONTROL_KEY_WHITE,
359     &game.panel.key_white,
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_CONTROL_KEY_WHITE_COUNT,
364     &game.panel.key_white_count,
365     TYPE_INTEGER,
366   },
367   {
368     GAME_CONTROL_SCORE,
369     &game.panel.score,
370     TYPE_INTEGER,
371   },
372   {
373     GAME_CONTROL_TIME,
374     &game.panel.time,
375     TYPE_INTEGER,
376   },
377   {
378     GAME_CONTROL_TIME_HH,
379     &game.panel.time_hh,
380     TYPE_INTEGER,
381   },
382   {
383     GAME_CONTROL_TIME_MM,
384     &game.panel.time_mm,
385     TYPE_INTEGER,
386   },
387   {
388     GAME_CONTROL_TIME_SS,
389     &game.panel.time_ss,
390     TYPE_INTEGER,
391   },
392   {
393     GAME_CONTROL_SHIELD_NORMAL,
394     &game.panel.shield_normal,
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_CONTROL_SHIELD_NORMAL_TIME,
399     &game.panel.shield_normal_time,
400     TYPE_INTEGER,
401   },
402   {
403     GAME_CONTROL_SHIELD_DEADLY,
404     &game.panel.shield_deadly,
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_CONTROL_SHIELD_DEADLY_TIME,
409     &game.panel.shield_deadly_time,
410     TYPE_INTEGER,
411   },
412   {
413     GAME_CONTROL_EXIT,
414     &game.panel.exit,
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_CONTROL_EMC_MAGIC_BALL,
419     &game.panel.emc_magic_ball,
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
424     &game.panel.emc_magic_ball_switch,
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_CONTROL_LIGHT_SWITCH,
429     &game.panel.light_switch,
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_CONTROL_LIGHT_SWITCH_TIME,
434     &game.panel.light_switch_time,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_CONTROL_TIMEGATE_SWITCH,
439     &game.panel.timegate_switch,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
444     &game.panel.timegate_switch_time,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_CONTROL_SWITCHGATE_SWITCH,
449     &game.panel.switchgate_switch,
450     TYPE_ELEMENT,
451   },
452   {
453     GAME_CONTROL_EMC_LENSES,
454     &game.panel.emc_lenses,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_CONTROL_EMC_LENSES_TIME,
459     &game.panel.emc_lenses_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_CONTROL_EMC_MAGNIFIER,
464     &game.panel.emc_magnifier,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_CONTROL_EMC_MAGNIFIER_TIME,
469     &game.panel.emc_magnifier_time,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_CONTROL_BALLOON_SWITCH,
474     &game.panel.balloon_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_CONTROL_DYNABOMB_NUMBER,
479     &game.panel.dynabomb_number,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_CONTROL_DYNABOMB_SIZE,
484     &game.panel.dynabomb_size,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_CONTROL_DYNABOMB_POWER,
489     &game.panel.dynabomb_power,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_CONTROL_PENGUINS,
494     &game.panel.penguins,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_CONTROL_SOKOBAN_OBJECTS,
499     &game.panel.sokoban_objects,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_CONTROL_SOKOBAN_FIELDS,
504     &game.panel.sokoban_fields,
505     TYPE_INTEGER,
506   },
507   {
508     GAME_CONTROL_ROBOT_WHEEL,
509     &game.panel.robot_wheel,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_CONTROL_CONVEYOR_BELT_1,
514     &game.panel.conveyor_belt_1,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
519     &game.panel.conveyor_belt_1_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_CONTROL_CONVEYOR_BELT_2,
524     &game.panel.conveyor_belt_2,
525     TYPE_ELEMENT,
526   },
527   {
528     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
529     &game.panel.conveyor_belt_2_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_CONTROL_CONVEYOR_BELT_3,
534     &game.panel.conveyor_belt_3,
535     TYPE_ELEMENT,
536   },
537   {
538     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
539     &game.panel.conveyor_belt_3_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_CONTROL_CONVEYOR_BELT_4,
544     &game.panel.conveyor_belt_4,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
549     &game.panel.conveyor_belt_4_switch,
550     TYPE_ELEMENT,
551   },
552   {
553     GAME_CONTROL_MAGIC_WALL,
554     &game.panel.magic_wall,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_CONTROL_MAGIC_WALL_TIME,
559     &game.panel.magic_wall_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_CONTROL_GRAVITY_STATE,
564     &game.panel.gravity_state,
565     TYPE_STRING,
566   },
567   {
568     GAME_CONTROL_PLAYER_NAME,
569     &game.panel.player_name,
570     TYPE_STRING,
571   },
572   {
573     GAME_CONTROL_LEVEL_NAME,
574     &game.panel.level_name,
575     TYPE_STRING,
576   },
577   {
578     GAME_CONTROL_LEVEL_AUTHOR,
579     &game.panel.level_author,
580     TYPE_STRING,
581   },
582
583   {
584     -1,
585     NULL,
586     -1,
587   }
588 };
589 #endif
590
591
592 /* values for delayed check of falling and moving elements and for collision */
593 #define CHECK_DELAY_MOVING      3
594 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
595 #define CHECK_DELAY_COLLISION   2
596 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
597
598 /* values for initial player move delay (initial delay counter value) */
599 #define INITIAL_MOVE_DELAY_OFF  -1
600 #define INITIAL_MOVE_DELAY_ON   0
601
602 /* values for player movement speed (which is in fact a delay value) */
603 #define MOVE_DELAY_MIN_SPEED    32
604 #define MOVE_DELAY_NORMAL_SPEED 8
605 #define MOVE_DELAY_HIGH_SPEED   4
606 #define MOVE_DELAY_MAX_SPEED    1
607
608 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
609 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
610
611 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
612 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
613
614 /* values for other actions */
615 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
616 #define MOVE_STEPSIZE_MIN       (1)
617 #define MOVE_STEPSIZE_MAX       (TILEX)
618
619 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
620 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
621
622 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
623
624 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
625                                  RND(element_info[e].push_delay_random))
626 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
627                                  RND(element_info[e].drop_delay_random))
628 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
629                                  RND(element_info[e].move_delay_random))
630 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
631                                     (element_info[e].move_delay_random))
632 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
633                                  RND(element_info[e].ce_value_random_initial))
634 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
635 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
636                                  RND((c)->delay_random * (c)->delay_frames))
637 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
638                                  RND((c)->delay_random))
639
640
641 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
642          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
643
644 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
645         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
646          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
647          (be) + (e) - EL_SELF)
648
649 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
650         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
651          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
652          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
653          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
654          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
655          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
656          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
657          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
658          (e))
659
660 #define CAN_GROW_INTO(e)                                                \
661         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
662
663 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
664                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
665                                         (condition)))
666
667 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
668                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
669                                         (CAN_MOVE_INTO_ACID(e) &&       \
670                                          Feld[x][y] == EL_ACID) ||      \
671                                         (condition)))
672
673 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
674                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
675                                         (CAN_MOVE_INTO_ACID(e) &&       \
676                                          Feld[x][y] == EL_ACID) ||      \
677                                         (condition)))
678
679 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
680                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
681                                         (condition) ||                  \
682                                         (CAN_MOVE_INTO_ACID(e) &&       \
683                                          Feld[x][y] == EL_ACID) ||      \
684                                         (DONT_COLLIDE_WITH(e) &&        \
685                                          IS_PLAYER(x, y) &&             \
686                                          !PLAYER_ENEMY_PROTECTED(x, y))))
687
688 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
689         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
690
691 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
692         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
693
694 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
695         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
696
697 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
698         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
699                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
700
701 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
702         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
703
704 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
705         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
706
707 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
708         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
709
710 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
711         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
712
713 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
714         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
715
716 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
717         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
718                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
719                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
720                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
721                                                  IS_FOOD_PENGUIN(Feld[x][y])))
722 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
723         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
724
725 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
726         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
727
728 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
729         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
730
731 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
732         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
733                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
734
735 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
736
737 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
738                 (!IS_PLAYER(x, y) &&                                    \
739                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
740
741 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
742         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
743
744 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
745 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
746
747 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
748 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
749 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
750 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
751
752 /* game button identifiers */
753 #define GAME_CTRL_ID_STOP               0
754 #define GAME_CTRL_ID_PAUSE              1
755 #define GAME_CTRL_ID_PLAY               2
756 #define SOUND_CTRL_ID_MUSIC             3
757 #define SOUND_CTRL_ID_LOOPS             4
758 #define SOUND_CTRL_ID_SIMPLE            5
759
760 #define NUM_GAME_BUTTONS                6
761
762
763 /* forward declaration for internal use */
764
765 static void CreateField(int, int, int);
766
767 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
768 static void AdvanceFrameAndPlayerCounters(int);
769
770 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
771 static boolean MovePlayer(struct PlayerInfo *, int, int);
772 static void ScrollPlayer(struct PlayerInfo *, int);
773 static void ScrollScreen(struct PlayerInfo *, int);
774
775 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
776
777 static void InitBeltMovement(void);
778 static void CloseAllOpenTimegates(void);
779 static void CheckGravityMovement(struct PlayerInfo *);
780 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
781 static void KillPlayerUnlessEnemyProtected(int, int);
782 static void KillPlayerUnlessExplosionProtected(int, int);
783
784 static void TestIfPlayerTouchesCustomElement(int, int);
785 static void TestIfElementTouchesCustomElement(int, int);
786 static void TestIfElementHitsCustomElement(int, int, int);
787 #if 0
788 static void TestIfElementSmashesCustomElement(int, int, int);
789 #endif
790
791 static void HandleElementChange(int, int, int);
792 static void ExecuteCustomElementAction(int, int, int, int);
793 static boolean ChangeElement(int, int, int, int);
794
795 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
796 #define CheckTriggeredElementChange(x, y, e, ev)                        \
797         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
798 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
799         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
800 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
801         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
802 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
803         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
804
805 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
806 #define CheckElementChange(x, y, e, te, ev)                             \
807         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
808 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
809         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
810 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
811         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
812
813 static void PlayLevelSound(int, int, int);
814 static void PlayLevelSoundNearest(int, int, int);
815 static void PlayLevelSoundAction(int, int, int);
816 static void PlayLevelSoundElementAction(int, int, int, int);
817 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
818 static void PlayLevelSoundActionIfLoop(int, int, int);
819 static void StopLevelSoundActionIfLoop(int, int, int);
820 static void PlayLevelMusic();
821
822 static void MapGameButtons();
823 static void HandleGameButtons(struct GadgetInfo *);
824
825 int AmoebeNachbarNr(int, int);
826 void AmoebeUmwandeln(int, int);
827 void ContinueMoving(int, int);
828 void Bang(int, int);
829 void InitMovDir(int, int);
830 void InitAmoebaNr(int, int);
831 int NewHiScore(void);
832
833 void TestIfGoodThingHitsBadThing(int, int, int);
834 void TestIfBadThingHitsGoodThing(int, int, int);
835 void TestIfPlayerTouchesBadThing(int, int);
836 void TestIfPlayerRunsIntoBadThing(int, int, int);
837 void TestIfBadThingTouchesPlayer(int, int);
838 void TestIfBadThingRunsIntoPlayer(int, int, int);
839 void TestIfFriendTouchesBadThing(int, int);
840 void TestIfBadThingTouchesFriend(int, int);
841 void TestIfBadThingTouchesOtherBadThing(int, int);
842
843 void KillPlayer(struct PlayerInfo *);
844 void BuryPlayer(struct PlayerInfo *);
845 void RemovePlayer(struct PlayerInfo *);
846
847 boolean SnapField(struct PlayerInfo *, int, int);
848 boolean DropElement(struct PlayerInfo *);
849
850 static int getInvisibleActiveFromInvisibleElement(int);
851 static int getInvisibleFromInvisibleActiveElement(int);
852
853 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
854
855 /* for detection of endless loops, caused by custom element programming */
856 /* (using maximal playfield width x 10 is just a rough approximation) */
857 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
858
859 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
860 {                                                                       \
861   if (recursion_loop_detected)                                          \
862     return (rc);                                                        \
863                                                                         \
864   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
865   {                                                                     \
866     recursion_loop_detected = TRUE;                                     \
867     recursion_loop_element = (e);                                       \
868   }                                                                     \
869                                                                         \
870   recursion_loop_depth++;                                               \
871 }
872
873 #define RECURSION_LOOP_DETECTION_END()                                  \
874 {                                                                       \
875   recursion_loop_depth--;                                               \
876 }
877
878 static int recursion_loop_depth;
879 static boolean recursion_loop_detected;
880 static boolean recursion_loop_element;
881
882
883 /* ------------------------------------------------------------------------- */
884 /* definition of elements that automatically change to other elements after  */
885 /* a specified time, eventually calling a function when changing             */
886 /* ------------------------------------------------------------------------- */
887
888 /* forward declaration for changer functions */
889 static void InitBuggyBase(int, int);
890 static void WarnBuggyBase(int, int);
891
892 static void InitTrap(int, int);
893 static void ActivateTrap(int, int);
894 static void ChangeActiveTrap(int, int);
895
896 static void InitRobotWheel(int, int);
897 static void RunRobotWheel(int, int);
898 static void StopRobotWheel(int, int);
899
900 static void InitTimegateWheel(int, int);
901 static void RunTimegateWheel(int, int);
902
903 static void InitMagicBallDelay(int, int);
904 static void ActivateMagicBall(int, int);
905
906 struct ChangingElementInfo
907 {
908   int element;
909   int target_element;
910   int change_delay;
911   void (*pre_change_function)(int x, int y);
912   void (*change_function)(int x, int y);
913   void (*post_change_function)(int x, int y);
914 };
915
916 static struct ChangingElementInfo change_delay_list[] =
917 {
918   {
919     EL_NUT_BREAKING,
920     EL_EMERALD,
921     6,
922     NULL,
923     NULL,
924     NULL
925   },
926   {
927     EL_PEARL_BREAKING,
928     EL_EMPTY,
929     8,
930     NULL,
931     NULL,
932     NULL
933   },
934   {
935     EL_EXIT_OPENING,
936     EL_EXIT_OPEN,
937     29,
938     NULL,
939     NULL,
940     NULL
941   },
942   {
943     EL_EXIT_CLOSING,
944     EL_EXIT_CLOSED,
945     29,
946     NULL,
947     NULL,
948     NULL
949   },
950   {
951     EL_STEEL_EXIT_OPENING,
952     EL_STEEL_EXIT_OPEN,
953     29,
954     NULL,
955     NULL,
956     NULL
957   },
958   {
959     EL_STEEL_EXIT_CLOSING,
960     EL_STEEL_EXIT_CLOSED,
961     29,
962     NULL,
963     NULL,
964     NULL
965   },
966   {
967     EL_EM_EXIT_OPENING,
968     EL_EM_EXIT_OPEN,
969     29,
970     NULL,
971     NULL,
972     NULL
973   },
974   {
975     EL_EM_EXIT_CLOSING,
976 #if 1
977     EL_EMPTY,
978 #else
979     EL_EM_EXIT_CLOSED,
980 #endif
981     29,
982     NULL,
983     NULL,
984     NULL
985   },
986   {
987     EL_EM_STEEL_EXIT_OPENING,
988     EL_EM_STEEL_EXIT_OPEN,
989     29,
990     NULL,
991     NULL,
992     NULL
993   },
994   {
995     EL_EM_STEEL_EXIT_CLOSING,
996 #if 1
997     EL_STEELWALL,
998 #else
999     EL_EM_STEEL_EXIT_CLOSED,
1000 #endif
1001     29,
1002     NULL,
1003     NULL,
1004     NULL
1005   },
1006   {
1007     EL_SP_EXIT_OPENING,
1008     EL_SP_EXIT_OPEN,
1009     29,
1010     NULL,
1011     NULL,
1012     NULL
1013   },
1014   {
1015     EL_SP_EXIT_CLOSING,
1016     EL_SP_EXIT_CLOSED,
1017     29,
1018     NULL,
1019     NULL,
1020     NULL
1021   },
1022   {
1023     EL_SWITCHGATE_OPENING,
1024     EL_SWITCHGATE_OPEN,
1025     29,
1026     NULL,
1027     NULL,
1028     NULL
1029   },
1030   {
1031     EL_SWITCHGATE_CLOSING,
1032     EL_SWITCHGATE_CLOSED,
1033     29,
1034     NULL,
1035     NULL,
1036     NULL
1037   },
1038   {
1039     EL_TIMEGATE_OPENING,
1040     EL_TIMEGATE_OPEN,
1041     29,
1042     NULL,
1043     NULL,
1044     NULL
1045   },
1046   {
1047     EL_TIMEGATE_CLOSING,
1048     EL_TIMEGATE_CLOSED,
1049     29,
1050     NULL,
1051     NULL,
1052     NULL
1053   },
1054
1055   {
1056     EL_ACID_SPLASH_LEFT,
1057     EL_EMPTY,
1058     8,
1059     NULL,
1060     NULL,
1061     NULL
1062   },
1063   {
1064     EL_ACID_SPLASH_RIGHT,
1065     EL_EMPTY,
1066     8,
1067     NULL,
1068     NULL,
1069     NULL
1070   },
1071   {
1072     EL_SP_BUGGY_BASE,
1073     EL_SP_BUGGY_BASE_ACTIVATING,
1074     0,
1075     InitBuggyBase,
1076     NULL,
1077     NULL
1078   },
1079   {
1080     EL_SP_BUGGY_BASE_ACTIVATING,
1081     EL_SP_BUGGY_BASE_ACTIVE,
1082     0,
1083     InitBuggyBase,
1084     NULL,
1085     NULL
1086   },
1087   {
1088     EL_SP_BUGGY_BASE_ACTIVE,
1089     EL_SP_BUGGY_BASE,
1090     0,
1091     InitBuggyBase,
1092     WarnBuggyBase,
1093     NULL
1094   },
1095   {
1096     EL_TRAP,
1097     EL_TRAP_ACTIVE,
1098     0,
1099     InitTrap,
1100     NULL,
1101     ActivateTrap
1102   },
1103   {
1104     EL_TRAP_ACTIVE,
1105     EL_TRAP,
1106     31,
1107     NULL,
1108     ChangeActiveTrap,
1109     NULL
1110   },
1111   {
1112     EL_ROBOT_WHEEL_ACTIVE,
1113     EL_ROBOT_WHEEL,
1114     0,
1115     InitRobotWheel,
1116     RunRobotWheel,
1117     StopRobotWheel
1118   },
1119   {
1120     EL_TIMEGATE_SWITCH_ACTIVE,
1121     EL_TIMEGATE_SWITCH,
1122     0,
1123     InitTimegateWheel,
1124     RunTimegateWheel,
1125     NULL
1126   },
1127   {
1128     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1129     EL_DC_TIMEGATE_SWITCH,
1130     0,
1131     InitTimegateWheel,
1132     RunTimegateWheel,
1133     NULL
1134   },
1135   {
1136     EL_EMC_MAGIC_BALL_ACTIVE,
1137     EL_EMC_MAGIC_BALL_ACTIVE,
1138     0,
1139     InitMagicBallDelay,
1140     NULL,
1141     ActivateMagicBall
1142   },
1143   {
1144     EL_EMC_SPRING_BUMPER_ACTIVE,
1145     EL_EMC_SPRING_BUMPER,
1146     8,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_DIAGONAL_SHRINKING,
1153     EL_UNDEFINED,
1154     0,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_DIAGONAL_GROWING,
1161     EL_UNDEFINED,
1162     0,
1163     NULL,
1164     NULL,
1165     NULL,
1166   },
1167
1168   {
1169     EL_UNDEFINED,
1170     EL_UNDEFINED,
1171     -1,
1172     NULL,
1173     NULL,
1174     NULL
1175   }
1176 };
1177
1178 struct
1179 {
1180   int element;
1181   int push_delay_fixed, push_delay_random;
1182 }
1183 push_delay_list[] =
1184 {
1185   { EL_SPRING,                  0, 0 },
1186   { EL_BALLOON,                 0, 0 },
1187
1188   { EL_SOKOBAN_OBJECT,          2, 0 },
1189   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1190   { EL_SATELLITE,               2, 0 },
1191   { EL_SP_DISK_YELLOW,          2, 0 },
1192
1193   { EL_UNDEFINED,               0, 0 },
1194 };
1195
1196 struct
1197 {
1198   int element;
1199   int move_stepsize;
1200 }
1201 move_stepsize_list[] =
1202 {
1203   { EL_AMOEBA_DROP,             2 },
1204   { EL_AMOEBA_DROPPING,         2 },
1205   { EL_QUICKSAND_FILLING,       1 },
1206   { EL_QUICKSAND_EMPTYING,      1 },
1207   { EL_QUICKSAND_FAST_FILLING,  2 },
1208   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1209   { EL_MAGIC_WALL_FILLING,      2 },
1210   { EL_MAGIC_WALL_EMPTYING,     2 },
1211   { EL_BD_MAGIC_WALL_FILLING,   2 },
1212   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1213   { EL_DC_MAGIC_WALL_FILLING,   2 },
1214   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1215
1216   { EL_UNDEFINED,               0 },
1217 };
1218
1219 struct
1220 {
1221   int element;
1222   int count;
1223 }
1224 collect_count_list[] =
1225 {
1226   { EL_EMERALD,                 1 },
1227   { EL_BD_DIAMOND,              1 },
1228   { EL_EMERALD_YELLOW,          1 },
1229   { EL_EMERALD_RED,             1 },
1230   { EL_EMERALD_PURPLE,          1 },
1231   { EL_DIAMOND,                 3 },
1232   { EL_SP_INFOTRON,             1 },
1233   { EL_PEARL,                   5 },
1234   { EL_CRYSTAL,                 8 },
1235
1236   { EL_UNDEFINED,               0 },
1237 };
1238
1239 struct
1240 {
1241   int element;
1242   int direction;
1243 }
1244 access_direction_list[] =
1245 {
1246   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1248   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1249   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1250   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1251   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1252   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1253   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1254   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1255   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1256   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1257
1258   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1259   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1260   { EL_SP_PORT_UP,                                                   MV_DOWN },
1261   { EL_SP_PORT_DOWN,                                         MV_UP           },
1262   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1263   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1264   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1265   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1266   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1267   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1268   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1269   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1270   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1271   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1272   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1273   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1274   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1275   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1276   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1277
1278   { EL_UNDEFINED,                       MV_NONE                              }
1279 };
1280
1281 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1282
1283 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1284 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1285 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1286                                  IS_JUST_CHANGING(x, y))
1287
1288 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1289
1290 /* static variables for playfield scan mode (scanning forward or backward) */
1291 static int playfield_scan_start_x = 0;
1292 static int playfield_scan_start_y = 0;
1293 static int playfield_scan_delta_x = 1;
1294 static int playfield_scan_delta_y = 1;
1295
1296 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1297                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1298                                      (y) += playfield_scan_delta_y)     \
1299                                 for ((x) = playfield_scan_start_x;      \
1300                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1301                                      (x) += playfield_scan_delta_x)
1302
1303 #ifdef DEBUG
1304 void DEBUG_SetMaximumDynamite()
1305 {
1306   int i;
1307
1308   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1309     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1310       local_player->inventory_element[local_player->inventory_size++] =
1311         EL_DYNAMITE;
1312 }
1313 #endif
1314
1315 static void InitPlayfieldScanModeVars()
1316 {
1317   if (game.use_reverse_scan_direction)
1318   {
1319     playfield_scan_start_x = lev_fieldx - 1;
1320     playfield_scan_start_y = lev_fieldy - 1;
1321
1322     playfield_scan_delta_x = -1;
1323     playfield_scan_delta_y = -1;
1324   }
1325   else
1326   {
1327     playfield_scan_start_x = 0;
1328     playfield_scan_start_y = 0;
1329
1330     playfield_scan_delta_x = 1;
1331     playfield_scan_delta_y = 1;
1332   }
1333 }
1334
1335 static void InitPlayfieldScanMode(int mode)
1336 {
1337   game.use_reverse_scan_direction =
1338     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1339
1340   InitPlayfieldScanModeVars();
1341 }
1342
1343 static int get_move_delay_from_stepsize(int move_stepsize)
1344 {
1345   move_stepsize =
1346     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1347
1348   /* make sure that stepsize value is always a power of 2 */
1349   move_stepsize = (1 << log_2(move_stepsize));
1350
1351   return TILEX / move_stepsize;
1352 }
1353
1354 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1355                                boolean init_game)
1356 {
1357   int player_nr = player->index_nr;
1358   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1359   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1360
1361   /* do no immediately change move delay -- the player might just be moving */
1362   player->move_delay_value_next = move_delay;
1363
1364   /* information if player can move must be set separately */
1365   player->cannot_move = cannot_move;
1366
1367   if (init_game)
1368   {
1369     player->move_delay       = game.initial_move_delay[player_nr];
1370     player->move_delay_value = game.initial_move_delay_value[player_nr];
1371
1372     player->move_delay_value_next = -1;
1373
1374     player->move_delay_reset_counter = 0;
1375   }
1376 }
1377
1378 void GetPlayerConfig()
1379 {
1380   GameFrameDelay = setup.game_frame_delay;
1381
1382   if (!audio.sound_available)
1383     setup.sound_simple = FALSE;
1384
1385   if (!audio.loops_available)
1386     setup.sound_loops = FALSE;
1387
1388   if (!audio.music_available)
1389     setup.sound_music = FALSE;
1390
1391   if (!video.fullscreen_available)
1392     setup.fullscreen = FALSE;
1393
1394   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1395
1396   SetAudioMode(setup.sound);
1397   InitJoysticks();
1398 }
1399
1400 int GetElementFromGroupElement(int element)
1401 {
1402   if (IS_GROUP_ELEMENT(element))
1403   {
1404     struct ElementGroupInfo *group = element_info[element].group;
1405     int last_anim_random_frame = gfx.anim_random_frame;
1406     int element_pos;
1407
1408     if (group->choice_mode == ANIM_RANDOM)
1409       gfx.anim_random_frame = RND(group->num_elements_resolved);
1410
1411     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1412                                     group->choice_mode, 0,
1413                                     group->choice_pos);
1414
1415     if (group->choice_mode == ANIM_RANDOM)
1416       gfx.anim_random_frame = last_anim_random_frame;
1417
1418     group->choice_pos++;
1419
1420     element = group->element_resolved[element_pos];
1421   }
1422
1423   return element;
1424 }
1425
1426 static void InitPlayerField(int x, int y, int element, boolean init_game)
1427 {
1428   if (element == EL_SP_MURPHY)
1429   {
1430     if (init_game)
1431     {
1432       if (stored_player[0].present)
1433       {
1434         Feld[x][y] = EL_SP_MURPHY_CLONE;
1435
1436         return;
1437       }
1438       else
1439       {
1440         stored_player[0].use_murphy = TRUE;
1441
1442         if (!level.use_artwork_element[0])
1443           stored_player[0].artwork_element = EL_SP_MURPHY;
1444       }
1445
1446       Feld[x][y] = EL_PLAYER_1;
1447     }
1448   }
1449
1450   if (init_game)
1451   {
1452     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1453     int jx = player->jx, jy = player->jy;
1454
1455     player->present = TRUE;
1456
1457     player->block_last_field = (element == EL_SP_MURPHY ?
1458                                 level.sp_block_last_field :
1459                                 level.block_last_field);
1460
1461     /* ---------- initialize player's last field block delay --------------- */
1462
1463     /* always start with reliable default value (no adjustment needed) */
1464     player->block_delay_adjustment = 0;
1465
1466     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1467     if (player->block_last_field && element == EL_SP_MURPHY)
1468       player->block_delay_adjustment = 1;
1469
1470     /* special case 2: in game engines before 3.1.1, blocking was different */
1471     if (game.use_block_last_field_bug)
1472       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1473
1474     if (!options.network || player->connected)
1475     {
1476       player->active = TRUE;
1477
1478       /* remove potentially duplicate players */
1479       if (StorePlayer[jx][jy] == Feld[x][y])
1480         StorePlayer[jx][jy] = 0;
1481
1482       StorePlayer[x][y] = Feld[x][y];
1483
1484       if (options.debug)
1485       {
1486         printf("Player %d activated.\n", player->element_nr);
1487         printf("[Local player is %d and currently %s.]\n",
1488                local_player->element_nr,
1489                local_player->active ? "active" : "not active");
1490       }
1491     }
1492
1493     Feld[x][y] = EL_EMPTY;
1494
1495     player->jx = player->last_jx = x;
1496     player->jy = player->last_jy = y;
1497   }
1498 }
1499
1500 static void InitField(int x, int y, boolean init_game)
1501 {
1502   int element = Feld[x][y];
1503
1504   switch (element)
1505   {
1506     case EL_SP_MURPHY:
1507     case EL_PLAYER_1:
1508     case EL_PLAYER_2:
1509     case EL_PLAYER_3:
1510     case EL_PLAYER_4:
1511       InitPlayerField(x, y, element, init_game);
1512       break;
1513
1514     case EL_SOKOBAN_FIELD_PLAYER:
1515       element = Feld[x][y] = EL_PLAYER_1;
1516       InitField(x, y, init_game);
1517
1518       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1519       InitField(x, y, init_game);
1520       break;
1521
1522     case EL_SOKOBAN_FIELD_EMPTY:
1523       local_player->sokobanfields_still_needed++;
1524       break;
1525
1526     case EL_STONEBLOCK:
1527       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1528         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1529       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1530         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1531       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1532         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1533       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1534         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1535       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1536         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1537       break;
1538
1539     case EL_BUG:
1540     case EL_BUG_RIGHT:
1541     case EL_BUG_UP:
1542     case EL_BUG_LEFT:
1543     case EL_BUG_DOWN:
1544     case EL_SPACESHIP:
1545     case EL_SPACESHIP_RIGHT:
1546     case EL_SPACESHIP_UP:
1547     case EL_SPACESHIP_LEFT:
1548     case EL_SPACESHIP_DOWN:
1549     case EL_BD_BUTTERFLY:
1550     case EL_BD_BUTTERFLY_RIGHT:
1551     case EL_BD_BUTTERFLY_UP:
1552     case EL_BD_BUTTERFLY_LEFT:
1553     case EL_BD_BUTTERFLY_DOWN:
1554     case EL_BD_FIREFLY:
1555     case EL_BD_FIREFLY_RIGHT:
1556     case EL_BD_FIREFLY_UP:
1557     case EL_BD_FIREFLY_LEFT:
1558     case EL_BD_FIREFLY_DOWN:
1559     case EL_PACMAN_RIGHT:
1560     case EL_PACMAN_UP:
1561     case EL_PACMAN_LEFT:
1562     case EL_PACMAN_DOWN:
1563     case EL_YAMYAM:
1564     case EL_YAMYAM_LEFT:
1565     case EL_YAMYAM_RIGHT:
1566     case EL_YAMYAM_UP:
1567     case EL_YAMYAM_DOWN:
1568     case EL_DARK_YAMYAM:
1569     case EL_ROBOT:
1570     case EL_PACMAN:
1571     case EL_SP_SNIKSNAK:
1572     case EL_SP_ELECTRON:
1573     case EL_MOLE:
1574     case EL_MOLE_LEFT:
1575     case EL_MOLE_RIGHT:
1576     case EL_MOLE_UP:
1577     case EL_MOLE_DOWN:
1578       InitMovDir(x, y);
1579       break;
1580
1581     case EL_AMOEBA_FULL:
1582     case EL_BD_AMOEBA:
1583       InitAmoebaNr(x, y);
1584       break;
1585
1586     case EL_AMOEBA_DROP:
1587       if (y == lev_fieldy - 1)
1588       {
1589         Feld[x][y] = EL_AMOEBA_GROWING;
1590         Store[x][y] = EL_AMOEBA_WET;
1591       }
1592       break;
1593
1594     case EL_DYNAMITE_ACTIVE:
1595     case EL_SP_DISK_RED_ACTIVE:
1596     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1597     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1598     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1599     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1600       MovDelay[x][y] = 96;
1601       break;
1602
1603     case EL_EM_DYNAMITE_ACTIVE:
1604       MovDelay[x][y] = 32;
1605       break;
1606
1607     case EL_LAMP:
1608       local_player->lights_still_needed++;
1609       break;
1610
1611     case EL_PENGUIN:
1612       local_player->friends_still_needed++;
1613       break;
1614
1615     case EL_PIG:
1616     case EL_DRAGON:
1617       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1618       break;
1619
1620     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1621     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1622     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1623     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1624     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1625     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1626     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1627     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1628     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1629     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1630     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1631     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1632       if (init_game)
1633       {
1634         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1635         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1636         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1637
1638         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1639         {
1640           game.belt_dir[belt_nr] = belt_dir;
1641           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1642         }
1643         else    /* more than one switch -- set it like the first switch */
1644         {
1645           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1646         }
1647       }
1648       break;
1649
1650 #if !USE_BOTH_SWITCHGATE_SWITCHES
1651     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1652       if (init_game)
1653         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1654       break;
1655
1656     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1657       if (init_game)
1658         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1659       break;
1660 #endif
1661
1662     case EL_LIGHT_SWITCH_ACTIVE:
1663       if (init_game)
1664         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1665       break;
1666
1667     case EL_INVISIBLE_STEELWALL:
1668     case EL_INVISIBLE_WALL:
1669     case EL_INVISIBLE_SAND:
1670       if (game.light_time_left > 0 ||
1671           game.lenses_time_left > 0)
1672         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1673       break;
1674
1675     case EL_EMC_MAGIC_BALL:
1676       if (game.ball_state)
1677         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1678       break;
1679
1680     case EL_EMC_MAGIC_BALL_SWITCH:
1681       if (game.ball_state)
1682         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1683       break;
1684
1685     default:
1686       if (IS_CUSTOM_ELEMENT(element))
1687       {
1688         if (CAN_MOVE(element))
1689           InitMovDir(x, y);
1690
1691 #if USE_NEW_CUSTOM_VALUE
1692         if (!element_info[element].use_last_ce_value || init_game)
1693           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1694 #endif
1695       }
1696       else if (IS_GROUP_ELEMENT(element))
1697       {
1698         Feld[x][y] = GetElementFromGroupElement(element);
1699
1700         InitField(x, y, init_game);
1701       }
1702
1703       break;
1704   }
1705
1706   if (!init_game)
1707     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1708 }
1709
1710 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1711 {
1712   InitField(x, y, init_game);
1713
1714   /* not needed to call InitMovDir() -- already done by InitField()! */
1715   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1716       CAN_MOVE(Feld[x][y]))
1717     InitMovDir(x, y);
1718 }
1719
1720 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1721 {
1722   int old_element = Feld[x][y];
1723
1724   InitField(x, y, init_game);
1725
1726   /* not needed to call InitMovDir() -- already done by InitField()! */
1727   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1728       CAN_MOVE(old_element) &&
1729       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1730     InitMovDir(x, y);
1731
1732   /* this case is in fact a combination of not less than three bugs:
1733      first, it calls InitMovDir() for elements that can move, although this is
1734      already done by InitField(); then, it checks the element that was at this
1735      field _before_ the call to InitField() (which can change it); lastly, it
1736      was not called for "mole with direction" elements, which were treated as
1737      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1738   */
1739 }
1740
1741 #if 1
1742
1743 static int get_key_element_from_nr(int key_nr)
1744 {
1745   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1746                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1747                           EL_EM_KEY_1 : EL_KEY_1);
1748
1749   return key_base_element + key_nr;
1750 }
1751
1752 static int get_next_drop_element(struct PlayerInfo *player)
1753 {
1754   return (player->inventory_size > 0 ?
1755           player->inventory_element[player->inventory_size - 1] :
1756           player->inventory_infinite_element != EL_UNDEFINED ?
1757           player->inventory_infinite_element :
1758           player->dynabombs_left > 0 ?
1759           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1760           EL_UNDEFINED);
1761 }
1762
1763 static int get_drop_element_from_pos(struct PlayerInfo *player, int pos)
1764 {
1765   /* pos >= 0: get element from bottom of the stack;
1766      pos <  0: get element from top of the stack */
1767
1768   if (pos < 0)
1769   {
1770     int min_inventory_size = -pos;
1771     int inventory_pos = player->inventory_size - min_inventory_size;
1772     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1773
1774     return (player->inventory_size >= min_inventory_size ?
1775             player->inventory_element[inventory_pos] :
1776             player->inventory_infinite_element != EL_UNDEFINED ?
1777             player->inventory_infinite_element :
1778             player->dynabombs_left >= min_dynabombs_left ?
1779             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1780             EL_UNDEFINED);
1781   }
1782   else
1783   {
1784     int min_dynabombs_left = pos + 1;
1785     int min_inventory_size = pos + 1 - player->dynabombs_left;
1786     int inventory_pos = pos - player->dynabombs_left;
1787
1788     return (player->inventory_infinite_element != EL_UNDEFINED ?
1789             player->inventory_infinite_element :
1790             player->dynabombs_left >= min_dynabombs_left ?
1791             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1792             player->inventory_size >= min_inventory_size ?
1793             player->inventory_element[inventory_pos] :
1794             EL_UNDEFINED);
1795   }
1796 }
1797
1798 void InitGameControlValues()
1799 {
1800   int i;
1801
1802   for (i = 0; game_controls[i].nr != -1; i++)
1803   {
1804     int nr = game_controls[i].nr;
1805     int type = game_controls[i].type;
1806     struct TextPosInfo *pos = game_controls[i].pos;
1807
1808     /* force update of game controls after initialization */
1809     game_control_value[nr] = last_game_control_value[nr] = -1;
1810
1811     /* determine panel value width for later calculation of alignment */
1812     if (type == TYPE_INTEGER || type == TYPE_STRING)
1813     {
1814       pos->width = pos->size * getFontWidth(pos->font);
1815       pos->height = getFontHeight(pos->font);
1816     }
1817     else if (type == TYPE_ELEMENT)
1818     {
1819       pos->width = pos->size;
1820       pos->height = pos->size;
1821     }
1822   }
1823 }
1824
1825 void UpdateGameControlValues()
1826 {
1827   int i, k;
1828   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1829   int score = (local_player->LevelSolved ? local_player->score_final :
1830                local_player->score);
1831   int exit_closed = (local_player->gems_still_needed > 0 ||
1832                      local_player->sokobanfields_still_needed > 0 ||
1833                      local_player->lights_still_needed > 0);
1834
1835   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1836   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1837
1838   game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0;
1839   for (i = 0; i < MAX_NUM_KEYS; i++)
1840     game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1841   game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1842   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1843
1844   if (game.centered_player_nr == -1)
1845   {
1846     for (i = 0; i < MAX_PLAYERS; i++)
1847     {
1848       for (k = 0; k < MAX_NUM_KEYS; k++)
1849         if (stored_player[i].key[k])
1850           game_control_value[GAME_CONTROL_KEY_1 + k] =
1851             get_key_element_from_nr(k);
1852
1853       game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1854         stored_player[i].inventory_size;
1855
1856       if (stored_player[i].num_white_keys > 0)
1857         game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1858
1859       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1860         stored_player[i].num_white_keys;
1861     }
1862   }
1863   else
1864   {
1865     int player_nr = game.centered_player_nr;
1866
1867     for (k = 0; k < MAX_NUM_KEYS; k++)
1868       if (stored_player[player_nr].key[k])
1869         game_control_value[GAME_CONTROL_KEY_1 + k] =
1870           get_key_element_from_nr(k);
1871
1872     game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1873       stored_player[player_nr].inventory_size;
1874
1875     if (stored_player[player_nr].num_white_keys > 0)
1876       game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1877
1878     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1879       stored_player[player_nr].num_white_keys;
1880   }
1881
1882   for (i = 0; i < 8; i++)
1883   {
1884     game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] =
1885       get_drop_element_from_pos(local_player, i);
1886     game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] =
1887       get_drop_element_from_pos(local_player, -i - 1);
1888   }
1889
1890   game_control_value[GAME_CONTROL_SCORE] = score;
1891
1892   game_control_value[GAME_CONTROL_TIME] = time;
1893
1894   game_control_value[GAME_CONTROL_TIME_HH] = time / 3600;
1895   game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60;
1896   game_control_value[GAME_CONTROL_TIME_SS] = time % 60;
1897
1898   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1899     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1900      EL_EMPTY);
1901   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1902     local_player->shield_normal_time_left;
1903   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1904     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1905      EL_EMPTY);
1906   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1907     local_player->shield_deadly_time_left;
1908
1909   game_control_value[GAME_CONTROL_EXIT] =
1910     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
1911
1912   /* !!! TODO !!! */
1913   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] =
1914     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
1915   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] =
1916     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
1917      EL_EMC_MAGIC_BALL_SWITCH);
1918
1919   game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1920     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1921   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1922
1923   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1924     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1925   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1926     game.timegate_time_left;
1927
1928   /* !!! TODO !!! */
1929   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] =
1930     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
1931
1932   game_control_value[GAME_CONTROL_EMC_LENSES] =
1933     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1934   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1935
1936   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1937     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1938   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1939
1940   game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1941     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1942      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1943      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1944      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1945      EL_BALLOON_SWITCH_NONE);
1946
1947   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1948     local_player->dynabomb_count;
1949   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1950     local_player->dynabomb_size;
1951   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1952     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1953
1954   game_control_value[GAME_CONTROL_PENGUINS] =
1955     local_player->friends_still_needed;
1956
1957   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1958     local_player->sokobanfields_still_needed;
1959   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1960     local_player->sokobanfields_still_needed;
1961
1962   /* !!! TODO !!! */
1963   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1964
1965   /* !!! TODO !!! */
1966   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1967   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1968   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1969   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1970   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1971   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1972   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1973   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1974
1975   /* !!! TODO !!! */
1976   game_control_value[GAME_CONTROL_MAGIC_WALL] =
1977     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
1978   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] = game.magic_wall_time_left;
1979
1980 #if USE_PLAYER_GRAVITY
1981   game_control_value[GAME_CONTROL_GRAVITY_STATE] = local_player->gravity;
1982 #else
1983   game_control_value[GAME_CONTROL_GRAVITY_STATE] = game.gravity;
1984 #endif
1985
1986   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1987   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1988   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1989 }
1990
1991 void DisplayGameControlValues()
1992 {
1993   int i;
1994
1995   game_status = GAME_MODE_PSEUDO_PANEL;
1996
1997   for (i = 0; game_controls[i].nr != -1; i++)
1998   {
1999     int nr = game_controls[i].nr;
2000     int type = game_controls[i].type;
2001     struct TextPosInfo *pos = game_controls[i].pos;
2002     int value = game_control_value[nr];
2003     int last_value = last_game_control_value[nr];
2004     int size = pos->size;
2005     int font = pos->font;
2006
2007     if (value == last_value)
2008       continue;
2009
2010     last_game_control_value[nr] = value;
2011
2012 #if 0
2013     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2014 #endif
2015
2016     if (PANEL_DEACTIVATED(pos))
2017       continue;
2018
2019     if (type == TYPE_INTEGER)
2020     {
2021       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2022       {
2023         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2024
2025         if (use_dynamic_size)           /* use dynamic number of digits */
2026         {
2027           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2028           int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2029           int size2 = size1 + 1;
2030           int font1 = pos->font;
2031           int font2 = pos->font_alt;
2032
2033           size = (value < value_change ? size1 : size2);
2034           font = (value < value_change ? font1 : font2);
2035
2036           /* clear background if value just changed its size (dynamic digits) */
2037           if ((last_value < value_change) != (value < value_change))
2038           {
2039             int width1 = size1 * getFontWidth(font1);
2040             int width2 = size2 * getFontWidth(font2);
2041             int max_width = MAX(width1, width2);
2042             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2043
2044             pos->width = max_width;
2045
2046             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2047                                        max_width, max_height);
2048           }
2049         }
2050
2051         pos->width = size * getFontWidth(font);
2052       }
2053
2054       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2055     }
2056     else if (type == TYPE_ELEMENT)
2057     {
2058       int dst_x = PANEL_XPOS(pos);
2059       int dst_y = PANEL_YPOS(pos);
2060
2061       if (value == EL_UNDEFINED || value == EL_EMPTY)
2062       {
2063         int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2064         int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2065
2066         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2067                    size, size, dst_x, dst_y);
2068       }
2069       else
2070       {
2071         int graphic = el2panelimg(value);
2072
2073         DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2074       }
2075     }
2076     else if (type == TYPE_STRING)
2077     {
2078       boolean active = (value != 0);
2079       char *state_normal = "off";
2080       char *state_active = "on";
2081       char *state = (active ? state_active : state_normal);
2082       char *s = (nr == GAME_CONTROL_GRAVITY_STATE ? state :
2083                  nr == GAME_CONTROL_PLAYER_NAME   ? setup.player_name :
2084                  nr == GAME_CONTROL_LEVEL_NAME    ? level.name :
2085                  nr == GAME_CONTROL_LEVEL_AUTHOR  ? level.author : NULL);
2086
2087       if (nr == GAME_CONTROL_GRAVITY_STATE)
2088       {
2089         int font1 = pos->font;          /* (used for normal state) */
2090         int font2 = pos->font_alt;      /* (used for active state) */
2091         int size1 = strlen(state_normal);
2092         int size2 = strlen(state_active);
2093         int width1 = size1 * getFontWidth(font1);
2094         int width2 = size2 * getFontWidth(font2);
2095         int max_width = MAX(width1, width2);
2096         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2097
2098         pos->width = max_width;
2099
2100         /* clear background for values that may have changed its size */
2101         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2102                                    max_width, max_height);
2103
2104         font = (active ? font2 : font1);
2105       }
2106
2107       if (s != NULL)
2108       {
2109         char *s_cut = getStringCopyN(s, size);
2110
2111         size = strlen(s_cut);   /* string size may be smaller than "chars" */
2112         pos->width = size * getFontWidth(font);
2113
2114         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2115
2116         free(s_cut);
2117       }
2118     }
2119
2120     redraw_mask |= REDRAW_DOOR_1;
2121   }
2122
2123   game_status = GAME_MODE_PLAYING;
2124 }
2125
2126 void DrawGameValue_Emeralds(int value)
2127 {
2128   struct TextPosInfo *pos = &game.panel.gems;
2129 #if 1
2130   int font_nr = pos->font;
2131 #else
2132   int font_nr = FONT_TEXT_2;
2133 #endif
2134   int font_width = getFontWidth(font_nr);
2135   int chars = pos->size;
2136
2137 #if 1
2138   return;       /* !!! USE NEW STUFF !!! */
2139 #endif
2140
2141   if (PANEL_DEACTIVATED(pos))
2142     return;
2143
2144   pos->width = chars * font_width;
2145
2146   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2147 }
2148
2149 void DrawGameValue_Dynamite(int value)
2150 {
2151   struct TextPosInfo *pos = &game.panel.inventory_count;
2152 #if 1
2153   int font_nr = pos->font;
2154 #else
2155   int font_nr = FONT_TEXT_2;
2156 #endif
2157   int font_width = getFontWidth(font_nr);
2158   int chars = pos->size;
2159
2160 #if 1
2161   return;       /* !!! USE NEW STUFF !!! */
2162 #endif
2163
2164   if (PANEL_DEACTIVATED(pos))
2165     return;
2166
2167   pos->width = chars * font_width;
2168
2169   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2170 }
2171
2172 void DrawGameValue_Score(int value)
2173 {
2174   struct TextPosInfo *pos = &game.panel.score;
2175 #if 1
2176   int font_nr = pos->font;
2177 #else
2178   int font_nr = FONT_TEXT_2;
2179 #endif
2180   int font_width = getFontWidth(font_nr);
2181   int chars = pos->size;
2182
2183 #if 1
2184   return;       /* !!! USE NEW STUFF !!! */
2185 #endif
2186
2187   if (PANEL_DEACTIVATED(pos))
2188     return;
2189
2190   pos->width = chars * font_width;
2191
2192   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2193 }
2194
2195 void DrawGameValue_Time(int value)
2196 {
2197   struct TextPosInfo *pos = &game.panel.time;
2198   static int last_value = -1;
2199   int chars1 = 3;
2200   int chars2 = 4;
2201   int chars = pos->size;
2202 #if 1
2203   int font1_nr = pos->font;
2204   int font2_nr = pos->font_alt;
2205 #else
2206   int font1_nr = FONT_TEXT_2;
2207   int font2_nr = FONT_TEXT_1;
2208 #endif
2209   int font_nr = font1_nr;
2210   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2211
2212 #if 1
2213   return;       /* !!! USE NEW STUFF !!! */
2214 #endif
2215
2216   if (PANEL_DEACTIVATED(pos))
2217     return;
2218
2219   if (use_dynamic_chars)                /* use dynamic number of chars */
2220   {
2221     chars   = (value < 1000 ? chars1   : chars2);
2222     font_nr = (value < 1000 ? font1_nr : font2_nr);
2223   }
2224
2225   /* clear background if value just changed its size (dynamic chars only) */
2226   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2227   {
2228     int width1 = chars1 * getFontWidth(font1_nr);
2229     int width2 = chars2 * getFontWidth(font2_nr);
2230     int max_width = MAX(width1, width2);
2231     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2232
2233     pos->width = max_width;
2234
2235     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2236                                max_width, max_height);
2237   }
2238
2239   pos->width = chars * getFontWidth(font_nr);
2240
2241   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2242
2243   last_value = value;
2244 }
2245
2246 void DrawGameValue_Level(int value)
2247 {
2248   struct TextPosInfo *pos = &game.panel.level_number;
2249   int chars1 = 2;
2250   int chars2 = 3;
2251   int chars = pos->size;
2252 #if 1
2253   int font1_nr = pos->font;
2254   int font2_nr = pos->font_alt;
2255 #else
2256   int font1_nr = FONT_TEXT_2;
2257   int font2_nr = FONT_TEXT_1;
2258 #endif
2259   int font_nr = font1_nr;
2260   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2261
2262 #if 1
2263   return;       /* !!! USE NEW STUFF !!! */
2264 #endif
2265
2266   if (PANEL_DEACTIVATED(pos))
2267     return;
2268
2269   if (use_dynamic_chars)                /* use dynamic number of chars */
2270   {
2271     chars   = (level_nr < 100 ? chars1   : chars2);
2272     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2273   }
2274
2275   pos->width = chars * getFontWidth(font_nr);
2276
2277   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2278 }
2279
2280 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2281 {
2282 #if 0
2283   struct TextPosInfo *pos = &game.panel.keys;
2284 #endif
2285 #if 0
2286   int base_key_graphic = EL_KEY_1;
2287 #endif
2288   int i;
2289
2290 #if 1
2291   return;       /* !!! USE NEW STUFF !!! */
2292 #endif
2293
2294 #if 0
2295   if (PANEL_DEACTIVATED(pos))
2296     return;
2297 #endif
2298
2299 #if 0
2300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2301     base_key_graphic = EL_EM_KEY_1;
2302 #endif
2303
2304 #if 0
2305   pos->width = 4 * MINI_TILEX;
2306 #endif
2307
2308 #if 1
2309   for (i = 0; i < MAX_NUM_KEYS; i++)
2310 #else
2311   /* currently only 4 of 8 possible keys are displayed */
2312   for (i = 0; i < STD_NUM_KEYS; i++)
2313 #endif
2314   {
2315 #if 1
2316     struct TextPosInfo *pos = &game.panel.key[i];
2317 #endif
2318     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2319     int src_y = DOOR_GFX_PAGEY1 + 123;
2320 #if 1
2321     int dst_x = PANEL_XPOS(pos);
2322     int dst_y = PANEL_YPOS(pos);
2323 #else
2324     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2325     int dst_y = PANEL_YPOS(pos);
2326 #endif
2327
2328 #if 1
2329     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2330                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2331                    EL_KEY_1) + i;
2332     int graphic = el2edimg(element);
2333 #endif
2334
2335 #if 1
2336     if (PANEL_DEACTIVATED(pos))
2337       continue;
2338 #endif
2339
2340 #if 0
2341     /* masked blit with tiles from half-size scaled bitmap does not work yet
2342        (no mask bitmap created for these sizes after loading and scaling) --
2343        solution: load without creating mask, scale, then create final mask */
2344
2345     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2346                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2347
2348     if (key[i])
2349     {
2350 #if 0
2351       int graphic = el2edimg(base_key_graphic + i);
2352 #endif
2353       Bitmap *src_bitmap;
2354       int src_x, src_y;
2355
2356       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2357
2358       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2359                     dst_x - src_x, dst_y - src_y);
2360       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2361                        dst_x, dst_y);
2362     }
2363 #else
2364 #if 1
2365     if (key[i])
2366       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2367     else
2368       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2369                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2370 #else
2371     if (key[i])
2372       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2373     else
2374       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2375                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2376 #endif
2377 #endif
2378   }
2379 }
2380
2381 #else
2382
2383 void DrawGameValue_Emeralds(int value)
2384 {
2385   int font_nr = FONT_TEXT_2;
2386   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2387
2388   if (PANEL_DEACTIVATED(game.panel.gems))
2389     return;
2390
2391   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2392 }
2393
2394 void DrawGameValue_Dynamite(int value)
2395 {
2396   int font_nr = FONT_TEXT_2;
2397   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2398
2399   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2400     return;
2401
2402   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2403 }
2404
2405 void DrawGameValue_Score(int value)
2406 {
2407   int font_nr = FONT_TEXT_2;
2408   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2409
2410   if (PANEL_DEACTIVATED(game.panel.score))
2411     return;
2412
2413   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2414 }
2415
2416 void DrawGameValue_Time(int value)
2417 {
2418   int font1_nr = FONT_TEXT_2;
2419 #if 1
2420   int font2_nr = FONT_TEXT_1;
2421 #else
2422   int font2_nr = FONT_LEVEL_NUMBER;
2423 #endif
2424   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2425   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2426
2427   if (PANEL_DEACTIVATED(game.panel.time))
2428     return;
2429
2430   /* clear background if value just changed its size */
2431   if (value == 999 || value == 1000)
2432     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2433
2434   if (value < 1000)
2435     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2436   else
2437     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2438 }
2439
2440 void DrawGameValue_Level(int value)
2441 {
2442   int font1_nr = FONT_TEXT_2;
2443 #if 1
2444   int font2_nr = FONT_TEXT_1;
2445 #else
2446   int font2_nr = FONT_LEVEL_NUMBER;
2447 #endif
2448
2449   if (PANEL_DEACTIVATED(game.panel.level))
2450     return;
2451
2452   if (level_nr < 100)
2453     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2454   else
2455     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2456 }
2457
2458 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2459 {
2460   int base_key_graphic = EL_KEY_1;
2461   int i;
2462
2463   if (PANEL_DEACTIVATED(game.panel.keys))
2464     return;
2465
2466   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2467     base_key_graphic = EL_EM_KEY_1;
2468
2469   /* currently only 4 of 8 possible keys are displayed */
2470   for (i = 0; i < STD_NUM_KEYS; i++)
2471   {
2472     int x = XX_KEYS + i * MINI_TILEX;
2473     int y = YY_KEYS;
2474
2475     if (key[i])
2476       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2477     else
2478       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2479                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2480   }
2481 }
2482
2483 #endif
2484
2485 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2486                        int key_bits)
2487 {
2488   int key[MAX_NUM_KEYS];
2489   int i;
2490
2491   /* prevent EM engine from updating time/score values parallel to GameWon() */
2492   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2493       local_player->LevelSolved)
2494     return;
2495
2496   for (i = 0; i < MAX_NUM_KEYS; i++)
2497     key[i] = key_bits & (1 << i);
2498
2499   DrawGameValue_Level(level_nr);
2500
2501   DrawGameValue_Emeralds(emeralds);
2502   DrawGameValue_Dynamite(dynamite);
2503   DrawGameValue_Score(score);
2504   DrawGameValue_Time(time);
2505
2506   DrawGameValue_Keys(key);
2507 }
2508
2509 void DrawGameDoorValues()
2510 {
2511   UpdateGameControlValues();
2512   DisplayGameControlValues();
2513 }
2514
2515 void DrawGameDoorValues_OLD()
2516 {
2517   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2518   int dynamite_value = 0;
2519   int score_value = (local_player->LevelSolved ? local_player->score_final :
2520                      local_player->score);
2521   int gems_value = local_player->gems_still_needed;
2522   int key_bits = 0;
2523   int i, j;
2524
2525   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2526   {
2527     DrawGameDoorValues_EM();
2528
2529     return;
2530   }
2531
2532   if (game.centered_player_nr == -1)
2533   {
2534     for (i = 0; i < MAX_PLAYERS; i++)
2535     {
2536       for (j = 0; j < MAX_NUM_KEYS; j++)
2537         if (stored_player[i].key[j])
2538           key_bits |= (1 << j);
2539
2540       dynamite_value += stored_player[i].inventory_size;
2541     }
2542   }
2543   else
2544   {
2545     int player_nr = game.centered_player_nr;
2546
2547     for (i = 0; i < MAX_NUM_KEYS; i++)
2548       if (stored_player[player_nr].key[i])
2549         key_bits |= (1 << i);
2550
2551     dynamite_value = stored_player[player_nr].inventory_size;
2552   }
2553
2554   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2555                     key_bits);
2556 }
2557
2558
2559 /*
2560   =============================================================================
2561   InitGameEngine()
2562   -----------------------------------------------------------------------------
2563   initialize game engine due to level / tape version number
2564   =============================================================================
2565 */
2566
2567 static void InitGameEngine()
2568 {
2569   int i, j, k, l, x, y;
2570
2571   /* set game engine from tape file when re-playing, else from level file */
2572   game.engine_version = (tape.playing ? tape.engine_version :
2573                          level.game_version);
2574
2575   /* ---------------------------------------------------------------------- */
2576   /* set flags for bugs and changes according to active game engine version */
2577   /* ---------------------------------------------------------------------- */
2578
2579   /*
2580     Summary of bugfix/change:
2581     Fixed handling for custom elements that change when pushed by the player.
2582
2583     Fixed/changed in version:
2584     3.1.0
2585
2586     Description:
2587     Before 3.1.0, custom elements that "change when pushing" changed directly
2588     after the player started pushing them (until then handled in "DigField()").
2589     Since 3.1.0, these custom elements are not changed until the "pushing"
2590     move of the element is finished (now handled in "ContinueMoving()").
2591
2592     Affected levels/tapes:
2593     The first condition is generally needed for all levels/tapes before version
2594     3.1.0, which might use the old behaviour before it was changed; known tapes
2595     that are affected are some tapes from the level set "Walpurgis Gardens" by
2596     Jamie Cullen.
2597     The second condition is an exception from the above case and is needed for
2598     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2599     above (including some development versions of 3.1.0), but before it was
2600     known that this change would break tapes like the above and was fixed in
2601     3.1.1, so that the changed behaviour was active although the engine version
2602     while recording maybe was before 3.1.0. There is at least one tape that is
2603     affected by this exception, which is the tape for the one-level set "Bug
2604     Machine" by Juergen Bonhagen.
2605   */
2606
2607   game.use_change_when_pushing_bug =
2608     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2609      !(tape.playing &&
2610        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2611        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2612
2613   /*
2614     Summary of bugfix/change:
2615     Fixed handling for blocking the field the player leaves when moving.
2616
2617     Fixed/changed in version:
2618     3.1.1
2619
2620     Description:
2621     Before 3.1.1, when "block last field when moving" was enabled, the field
2622     the player is leaving when moving was blocked for the time of the move,
2623     and was directly unblocked afterwards. This resulted in the last field
2624     being blocked for exactly one less than the number of frames of one player
2625     move. Additionally, even when blocking was disabled, the last field was
2626     blocked for exactly one frame.
2627     Since 3.1.1, due to changes in player movement handling, the last field
2628     is not blocked at all when blocking is disabled. When blocking is enabled,
2629     the last field is blocked for exactly the number of frames of one player
2630     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2631     last field is blocked for exactly one more than the number of frames of
2632     one player move.
2633
2634     Affected levels/tapes:
2635     (!!! yet to be determined -- probably many !!!)
2636   */
2637
2638   game.use_block_last_field_bug =
2639     (game.engine_version < VERSION_IDENT(3,1,1,0));
2640
2641   /*
2642     Summary of bugfix/change:
2643     Changed behaviour of CE changes with multiple changes per single frame.
2644
2645     Fixed/changed in version:
2646     3.2.0-6
2647
2648     Description:
2649     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2650     This resulted in race conditions where CEs seem to behave strange in some
2651     situations (where triggered CE changes were just skipped because there was
2652     already a CE change on that tile in the playfield in that engine frame).
2653     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2654     (The number of changes per frame must be limited in any case, because else
2655     it is easily possible to define CE changes that would result in an infinite
2656     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2657     should be set large enough so that it would only be reached in cases where
2658     the corresponding CE change conditions run into a loop. Therefore, it seems
2659     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2660     maximal number of change pages for custom elements.)
2661
2662     Affected levels/tapes:
2663     Probably many.
2664   */
2665
2666 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2667   game.max_num_changes_per_frame = 1;
2668 #else
2669   game.max_num_changes_per_frame =
2670     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2671 #endif
2672
2673   /* ---------------------------------------------------------------------- */
2674
2675   /* default scan direction: scan playfield from top/left to bottom/right */
2676   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2677
2678   /* dynamically adjust element properties according to game engine version */
2679   InitElementPropertiesEngine(game.engine_version);
2680
2681 #if 0
2682   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2683   printf("          tape version == %06d [%s] [file: %06d]\n",
2684          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2685          tape.file_version);
2686   printf("       => game.engine_version == %06d\n", game.engine_version);
2687 #endif
2688
2689   /* ---------- initialize player's initial move delay --------------------- */
2690
2691   /* dynamically adjust player properties according to level information */
2692   for (i = 0; i < MAX_PLAYERS; i++)
2693     game.initial_move_delay_value[i] =
2694       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2695
2696   /* dynamically adjust player properties according to game engine version */
2697   for (i = 0; i < MAX_PLAYERS; i++)
2698     game.initial_move_delay[i] =
2699       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2700        game.initial_move_delay_value[i] : 0);
2701
2702   /* ---------- initialize player's initial push delay --------------------- */
2703
2704   /* dynamically adjust player properties according to game engine version */
2705   game.initial_push_delay_value =
2706     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2707
2708   /* ---------- initialize changing elements ------------------------------- */
2709
2710   /* initialize changing elements information */
2711   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2712   {
2713     struct ElementInfo *ei = &element_info[i];
2714
2715     /* this pointer might have been changed in the level editor */
2716     ei->change = &ei->change_page[0];
2717
2718     if (!IS_CUSTOM_ELEMENT(i))
2719     {
2720       ei->change->target_element = EL_EMPTY_SPACE;
2721       ei->change->delay_fixed = 0;
2722       ei->change->delay_random = 0;
2723       ei->change->delay_frames = 1;
2724     }
2725
2726     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2727     {
2728       ei->has_change_event[j] = FALSE;
2729
2730       ei->event_page_nr[j] = 0;
2731       ei->event_page[j] = &ei->change_page[0];
2732     }
2733   }
2734
2735   /* add changing elements from pre-defined list */
2736   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2737   {
2738     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2739     struct ElementInfo *ei = &element_info[ch_delay->element];
2740
2741     ei->change->target_element       = ch_delay->target_element;
2742     ei->change->delay_fixed          = ch_delay->change_delay;
2743
2744     ei->change->pre_change_function  = ch_delay->pre_change_function;
2745     ei->change->change_function      = ch_delay->change_function;
2746     ei->change->post_change_function = ch_delay->post_change_function;
2747
2748     ei->change->can_change = TRUE;
2749     ei->change->can_change_or_has_action = TRUE;
2750
2751     ei->has_change_event[CE_DELAY] = TRUE;
2752
2753     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2754     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2755   }
2756
2757   /* ---------- initialize internal run-time variables ------------- */
2758
2759   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2760   {
2761     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2762
2763     for (j = 0; j < ei->num_change_pages; j++)
2764     {
2765       ei->change_page[j].can_change_or_has_action =
2766         (ei->change_page[j].can_change |
2767          ei->change_page[j].has_action);
2768     }
2769   }
2770
2771   /* add change events from custom element configuration */
2772   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2773   {
2774     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2775
2776     for (j = 0; j < ei->num_change_pages; j++)
2777     {
2778       if (!ei->change_page[j].can_change_or_has_action)
2779         continue;
2780
2781       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2782       {
2783         /* only add event page for the first page found with this event */
2784         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2785         {
2786           ei->has_change_event[k] = TRUE;
2787
2788           ei->event_page_nr[k] = j;
2789           ei->event_page[k] = &ei->change_page[j];
2790         }
2791       }
2792     }
2793   }
2794
2795   /* ---------- initialize run-time trigger player and element ------------- */
2796
2797   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2798   {
2799     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2800
2801     for (j = 0; j < ei->num_change_pages; j++)
2802     {
2803       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2804       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2805       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2806       ei->change_page[j].actual_trigger_ce_value = 0;
2807       ei->change_page[j].actual_trigger_ce_score = 0;
2808     }
2809   }
2810
2811   /* ---------- initialize trigger events ---------------------------------- */
2812
2813   /* initialize trigger events information */
2814   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2815     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2816       trigger_events[i][j] = FALSE;
2817
2818   /* add trigger events from element change event properties */
2819   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2820   {
2821     struct ElementInfo *ei = &element_info[i];
2822
2823     for (j = 0; j < ei->num_change_pages; j++)
2824     {
2825       if (!ei->change_page[j].can_change_or_has_action)
2826         continue;
2827
2828       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2829       {
2830         int trigger_element = ei->change_page[j].trigger_element;
2831
2832         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2833         {
2834           if (ei->change_page[j].has_event[k])
2835           {
2836             if (IS_GROUP_ELEMENT(trigger_element))
2837             {
2838               struct ElementGroupInfo *group =
2839                 element_info[trigger_element].group;
2840
2841               for (l = 0; l < group->num_elements_resolved; l++)
2842                 trigger_events[group->element_resolved[l]][k] = TRUE;
2843             }
2844             else if (trigger_element == EL_ANY_ELEMENT)
2845               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2846                 trigger_events[l][k] = TRUE;
2847             else
2848               trigger_events[trigger_element][k] = TRUE;
2849           }
2850         }
2851       }
2852     }
2853   }
2854
2855   /* ---------- initialize push delay -------------------------------------- */
2856
2857   /* initialize push delay values to default */
2858   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2859   {
2860     if (!IS_CUSTOM_ELEMENT(i))
2861     {
2862       /* set default push delay values (corrected since version 3.0.7-1) */
2863       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2864       {
2865         element_info[i].push_delay_fixed = 2;
2866         element_info[i].push_delay_random = 8;
2867       }
2868       else
2869       {
2870         element_info[i].push_delay_fixed = 8;
2871         element_info[i].push_delay_random = 8;
2872       }
2873     }
2874   }
2875
2876   /* set push delay value for certain elements from pre-defined list */
2877   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2878   {
2879     int e = push_delay_list[i].element;
2880
2881     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2882     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2883   }
2884
2885   /* set push delay value for Supaplex elements for newer engine versions */
2886   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2887   {
2888     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2889     {
2890       if (IS_SP_ELEMENT(i))
2891       {
2892         /* set SP push delay to just enough to push under a falling zonk */
2893         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2894
2895         element_info[i].push_delay_fixed  = delay;
2896         element_info[i].push_delay_random = 0;
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize move stepsize ----------------------------------- */
2902
2903   /* initialize move stepsize values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905     if (!IS_CUSTOM_ELEMENT(i))
2906       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2907
2908   /* set move stepsize value for certain elements from pre-defined list */
2909   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2910   {
2911     int e = move_stepsize_list[i].element;
2912
2913     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2914   }
2915
2916   /* ---------- initialize collect score ----------------------------------- */
2917
2918   /* initialize collect score values for custom elements from initial value */
2919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2920     if (IS_CUSTOM_ELEMENT(i))
2921       element_info[i].collect_score = element_info[i].collect_score_initial;
2922
2923   /* ---------- initialize collect count ----------------------------------- */
2924
2925   /* initialize collect count values for non-custom elements */
2926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927     if (!IS_CUSTOM_ELEMENT(i))
2928       element_info[i].collect_count_initial = 0;
2929
2930   /* add collect count values for all elements from pre-defined list */
2931   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2932     element_info[collect_count_list[i].element].collect_count_initial =
2933       collect_count_list[i].count;
2934
2935   /* ---------- initialize access direction -------------------------------- */
2936
2937   /* initialize access direction values to default (access from every side) */
2938   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2939     if (!IS_CUSTOM_ELEMENT(i))
2940       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2941
2942   /* set access direction value for certain elements from pre-defined list */
2943   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2944     element_info[access_direction_list[i].element].access_direction =
2945       access_direction_list[i].direction;
2946
2947   /* ---------- initialize explosion content ------------------------------- */
2948   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949   {
2950     if (IS_CUSTOM_ELEMENT(i))
2951       continue;
2952
2953     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2954     {
2955       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2956
2957       element_info[i].content.e[x][y] =
2958         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2959          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2960          i == EL_PLAYER_3 ? EL_EMERALD :
2961          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2962          i == EL_MOLE ? EL_EMERALD_RED :
2963          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2964          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2965          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2966          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2967          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2968          i == EL_WALL_EMERALD ? EL_EMERALD :
2969          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2970          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2971          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2972          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2973          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2974          i == EL_WALL_PEARL ? EL_PEARL :
2975          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2976          EL_EMPTY);
2977     }
2978   }
2979
2980   /* ---------- initialize recursion detection ------------------------------ */
2981   recursion_loop_depth = 0;
2982   recursion_loop_detected = FALSE;
2983   recursion_loop_element = EL_UNDEFINED;
2984
2985   /* ---------- initialize graphics engine ---------------------------------- */
2986   game.scroll_delay_value =
2987     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2988      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
2989   game.scroll_delay_value =
2990     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2991 }
2992
2993 int get_num_special_action(int element, int action_first, int action_last)
2994 {
2995   int num_special_action = 0;
2996   int i, j;
2997
2998   for (i = action_first; i <= action_last; i++)
2999   {
3000     boolean found = FALSE;
3001
3002     for (j = 0; j < NUM_DIRECTIONS; j++)
3003       if (el_act_dir2img(element, i, j) !=
3004           el_act_dir2img(element, ACTION_DEFAULT, j))
3005         found = TRUE;
3006
3007     if (found)
3008       num_special_action++;
3009     else
3010       break;
3011   }
3012
3013   return num_special_action;
3014 }
3015
3016
3017 /*
3018   =============================================================================
3019   InitGame()
3020   -----------------------------------------------------------------------------
3021   initialize and start new game
3022   =============================================================================
3023 */
3024
3025 void InitGame()
3026 {
3027   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3028   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3029   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3030 #if 0
3031   boolean do_fading = (game_status == GAME_MODE_MAIN);
3032 #endif
3033   int i, j, x, y;
3034
3035   game_status = GAME_MODE_PLAYING;
3036
3037   InitGameEngine();
3038   InitGameControlValues();
3039
3040   /* don't play tapes over network */
3041   network_playing = (options.network && !tape.playing);
3042
3043   for (i = 0; i < MAX_PLAYERS; i++)
3044   {
3045     struct PlayerInfo *player = &stored_player[i];
3046
3047     player->index_nr = i;
3048     player->index_bit = (1 << i);
3049     player->element_nr = EL_PLAYER_1 + i;
3050
3051     player->present = FALSE;
3052     player->active = FALSE;
3053     player->killed = FALSE;
3054
3055     player->action = 0;
3056     player->effective_action = 0;
3057     player->programmed_action = 0;
3058
3059     player->score = 0;
3060     player->score_final = 0;
3061
3062     player->gems_still_needed = level.gems_needed;
3063     player->sokobanfields_still_needed = 0;
3064     player->lights_still_needed = 0;
3065     player->friends_still_needed = 0;
3066
3067     for (j = 0; j < MAX_NUM_KEYS; j++)
3068       player->key[j] = FALSE;
3069
3070     player->num_white_keys = 0;
3071
3072     player->dynabomb_count = 0;
3073     player->dynabomb_size = 1;
3074     player->dynabombs_left = 0;
3075     player->dynabomb_xl = FALSE;
3076
3077     player->MovDir = MV_NONE;
3078     player->MovPos = 0;
3079     player->GfxPos = 0;
3080     player->GfxDir = MV_NONE;
3081     player->GfxAction = ACTION_DEFAULT;
3082     player->Frame = 0;
3083     player->StepFrame = 0;
3084
3085     player->use_murphy = FALSE;
3086     player->artwork_element =
3087       (level.use_artwork_element[i] ? level.artwork_element[i] :
3088        player->element_nr);
3089
3090     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3091     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3092
3093     player->gravity = level.initial_player_gravity[i];
3094
3095     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3096
3097     player->actual_frame_counter = 0;
3098
3099     player->step_counter = 0;
3100
3101     player->last_move_dir = MV_NONE;
3102
3103     player->is_active = FALSE;
3104
3105     player->is_waiting = FALSE;
3106     player->is_moving = FALSE;
3107     player->is_auto_moving = FALSE;
3108     player->is_digging = FALSE;
3109     player->is_snapping = FALSE;
3110     player->is_collecting = FALSE;
3111     player->is_pushing = FALSE;
3112     player->is_switching = FALSE;
3113     player->is_dropping = FALSE;
3114     player->is_dropping_pressed = FALSE;
3115
3116     player->is_bored = FALSE;
3117     player->is_sleeping = FALSE;
3118
3119     player->frame_counter_bored = -1;
3120     player->frame_counter_sleeping = -1;
3121
3122     player->anim_delay_counter = 0;
3123     player->post_delay_counter = 0;
3124
3125     player->dir_waiting = MV_NONE;
3126     player->action_waiting = ACTION_DEFAULT;
3127     player->last_action_waiting = ACTION_DEFAULT;
3128     player->special_action_bored = ACTION_DEFAULT;
3129     player->special_action_sleeping = ACTION_DEFAULT;
3130
3131     player->switch_x = -1;
3132     player->switch_y = -1;
3133
3134     player->drop_x = -1;
3135     player->drop_y = -1;
3136
3137     player->show_envelope = 0;
3138
3139     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3140
3141     player->push_delay       = -1;      /* initialized when pushing starts */
3142     player->push_delay_value = game.initial_push_delay_value;
3143
3144     player->drop_delay = 0;
3145     player->drop_pressed_delay = 0;
3146
3147     player->last_jx = -1;
3148     player->last_jy = -1;
3149     player->jx = -1;
3150     player->jy = -1;
3151
3152     player->shield_normal_time_left = 0;
3153     player->shield_deadly_time_left = 0;
3154
3155     player->inventory_infinite_element = EL_UNDEFINED;
3156     player->inventory_size = 0;
3157
3158     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3159     SnapField(player, 0, 0);
3160
3161     player->LevelSolved = FALSE;
3162     player->GameOver = FALSE;
3163
3164     player->LevelSolved_GameWon = FALSE;
3165     player->LevelSolved_GameEnd = FALSE;
3166     player->LevelSolved_PanelOff = FALSE;
3167     player->LevelSolved_SaveTape = FALSE;
3168     player->LevelSolved_SaveScore = FALSE;
3169   }
3170
3171   network_player_action_received = FALSE;
3172
3173 #if defined(NETWORK_AVALIABLE)
3174   /* initial null action */
3175   if (network_playing)
3176     SendToServer_MovePlayer(MV_NONE);
3177 #endif
3178
3179   ZX = ZY = -1;
3180   ExitX = ExitY = -1;
3181
3182   FrameCounter = 0;
3183   TimeFrames = 0;
3184   TimePlayed = 0;
3185   TimeLeft = level.time;
3186   TapeTime = 0;
3187
3188   ScreenMovDir = MV_NONE;
3189   ScreenMovPos = 0;
3190   ScreenGfxPos = 0;
3191
3192   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3193
3194   AllPlayersGone = FALSE;
3195
3196   game.yamyam_content_nr = 0;
3197   game.magic_wall_active = FALSE;
3198   game.magic_wall_time_left = 0;
3199   game.light_time_left = 0;
3200   game.timegate_time_left = 0;
3201   game.switchgate_pos = 0;
3202   game.wind_direction = level.wind_direction_initial;
3203
3204 #if !USE_PLAYER_GRAVITY
3205   game.gravity = FALSE;
3206   game.explosions_delayed = TRUE;
3207 #endif
3208
3209   game.lenses_time_left = 0;
3210   game.magnify_time_left = 0;
3211
3212   game.ball_state = level.ball_state_initial;
3213   game.ball_content_nr = 0;
3214
3215   game.envelope_active = FALSE;
3216
3217   /* set focus to local player for network games, else to all players */
3218   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3219   game.centered_player_nr_next = game.centered_player_nr;
3220   game.set_centered_player = FALSE;
3221
3222   if (network_playing && tape.recording)
3223   {
3224     /* store client dependent player focus when recording network games */
3225     tape.centered_player_nr_next = game.centered_player_nr_next;
3226     tape.set_centered_player = TRUE;
3227   }
3228
3229   for (i = 0; i < NUM_BELTS; i++)
3230   {
3231     game.belt_dir[i] = MV_NONE;
3232     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3233   }
3234
3235   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3236     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3237
3238   SCAN_PLAYFIELD(x, y)
3239   {
3240     Feld[x][y] = level.field[x][y];
3241     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3242     ChangeDelay[x][y] = 0;
3243     ChangePage[x][y] = -1;
3244 #if USE_NEW_CUSTOM_VALUE
3245     CustomValue[x][y] = 0;              /* initialized in InitField() */
3246 #endif
3247     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3248     AmoebaNr[x][y] = 0;
3249     WasJustMoving[x][y] = 0;
3250     WasJustFalling[x][y] = 0;
3251     CheckCollision[x][y] = 0;
3252     CheckImpact[x][y] = 0;
3253     Stop[x][y] = FALSE;
3254     Pushed[x][y] = FALSE;
3255
3256     ChangeCount[x][y] = 0;
3257     ChangeEvent[x][y] = -1;
3258
3259     ExplodePhase[x][y] = 0;
3260     ExplodeDelay[x][y] = 0;
3261     ExplodeField[x][y] = EX_TYPE_NONE;
3262
3263     RunnerVisit[x][y] = 0;
3264     PlayerVisit[x][y] = 0;
3265
3266     GfxFrame[x][y] = 0;
3267     GfxRandom[x][y] = INIT_GFX_RANDOM();
3268     GfxElement[x][y] = EL_UNDEFINED;
3269     GfxAction[x][y] = ACTION_DEFAULT;
3270     GfxDir[x][y] = MV_NONE;
3271   }
3272
3273   SCAN_PLAYFIELD(x, y)
3274   {
3275     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3276       emulate_bd = FALSE;
3277     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3278       emulate_sb = FALSE;
3279     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3280       emulate_sp = FALSE;
3281
3282     InitField(x, y, TRUE);
3283   }
3284
3285   InitBeltMovement();
3286
3287   for (i = 0; i < MAX_PLAYERS; i++)
3288   {
3289     struct PlayerInfo *player = &stored_player[i];
3290
3291     /* set number of special actions for bored and sleeping animation */
3292     player->num_special_action_bored =
3293       get_num_special_action(player->artwork_element,
3294                              ACTION_BORING_1, ACTION_BORING_LAST);
3295     player->num_special_action_sleeping =
3296       get_num_special_action(player->artwork_element,
3297                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3298   }
3299
3300   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3301                     emulate_sb ? EMU_SOKOBAN :
3302                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3303
3304 #if USE_NEW_ALL_SLIPPERY
3305   /* initialize type of slippery elements */
3306   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3307   {
3308     if (!IS_CUSTOM_ELEMENT(i))
3309     {
3310       /* default: elements slip down either to the left or right randomly */
3311       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3312
3313       /* SP style elements prefer to slip down on the left side */
3314       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3315         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3316
3317       /* BD style elements prefer to slip down on the left side */
3318       if (game.emulation == EMU_BOULDERDASH)
3319         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3320     }
3321   }
3322 #endif
3323
3324   /* initialize explosion and ignition delay */
3325   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3326   {
3327     if (!IS_CUSTOM_ELEMENT(i))
3328     {
3329       int num_phase = 8;
3330       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3331                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3332                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3333       int last_phase = (num_phase + 1) * delay;
3334       int half_phase = (num_phase / 2) * delay;
3335
3336       element_info[i].explosion_delay = last_phase - 1;
3337       element_info[i].ignition_delay = half_phase;
3338
3339       if (i == EL_BLACK_ORB)
3340         element_info[i].ignition_delay = 1;
3341     }
3342
3343 #if 0
3344     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3345       element_info[i].explosion_delay = 1;
3346
3347     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3348       element_info[i].ignition_delay = 1;
3349 #endif
3350   }
3351
3352   /* correct non-moving belts to start moving left */
3353   for (i = 0; i < NUM_BELTS; i++)
3354     if (game.belt_dir[i] == MV_NONE)
3355       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3356
3357   /* check if any connected player was not found in playfield */
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359   {
3360     struct PlayerInfo *player = &stored_player[i];
3361
3362     if (player->connected && !player->present)
3363     {
3364       for (j = 0; j < MAX_PLAYERS; j++)
3365       {
3366         struct PlayerInfo *some_player = &stored_player[j];
3367         int jx = some_player->jx, jy = some_player->jy;
3368
3369         /* assign first free player found that is present in the playfield */
3370         if (some_player->present && !some_player->connected)
3371         {
3372           player->present = TRUE;
3373           player->active = TRUE;
3374
3375           some_player->present = FALSE;
3376           some_player->active = FALSE;
3377
3378           player->artwork_element = some_player->artwork_element;
3379
3380           player->block_last_field       = some_player->block_last_field;
3381           player->block_delay_adjustment = some_player->block_delay_adjustment;
3382
3383           StorePlayer[jx][jy] = player->element_nr;
3384           player->jx = player->last_jx = jx;
3385           player->jy = player->last_jy = jy;
3386
3387           break;
3388         }
3389       }
3390     }
3391   }
3392
3393   if (tape.playing)
3394   {
3395     /* when playing a tape, eliminate all players who do not participate */
3396
3397     for (i = 0; i < MAX_PLAYERS; i++)
3398     {
3399       if (stored_player[i].active && !tape.player_participates[i])
3400       {
3401         struct PlayerInfo *player = &stored_player[i];
3402         int jx = player->jx, jy = player->jy;
3403
3404         player->active = FALSE;
3405         StorePlayer[jx][jy] = 0;
3406         Feld[jx][jy] = EL_EMPTY;
3407       }
3408     }
3409   }
3410   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3411   {
3412     /* when in single player mode, eliminate all but the first active player */
3413
3414     for (i = 0; i < MAX_PLAYERS; i++)
3415     {
3416       if (stored_player[i].active)
3417       {
3418         for (j = i + 1; j < MAX_PLAYERS; j++)
3419         {
3420           if (stored_player[j].active)
3421           {
3422             struct PlayerInfo *player = &stored_player[j];
3423             int jx = player->jx, jy = player->jy;
3424
3425             player->active = FALSE;
3426             player->present = FALSE;
3427
3428             StorePlayer[jx][jy] = 0;
3429             Feld[jx][jy] = EL_EMPTY;
3430           }
3431         }
3432       }
3433     }
3434   }
3435
3436   /* when recording the game, store which players take part in the game */
3437   if (tape.recording)
3438   {
3439     for (i = 0; i < MAX_PLAYERS; i++)
3440       if (stored_player[i].active)
3441         tape.player_participates[i] = TRUE;
3442   }
3443
3444   if (options.debug)
3445   {
3446     for (i = 0; i < MAX_PLAYERS; i++)
3447     {
3448       struct PlayerInfo *player = &stored_player[i];
3449
3450       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3451              i+1,
3452              player->present,
3453              player->connected,
3454              player->active);
3455       if (local_player == player)
3456         printf("Player  %d is local player.\n", i+1);
3457     }
3458   }
3459
3460   if (BorderElement == EL_EMPTY)
3461   {
3462     SBX_Left = 0;
3463     SBX_Right = lev_fieldx - SCR_FIELDX;
3464     SBY_Upper = 0;
3465     SBY_Lower = lev_fieldy - SCR_FIELDY;
3466   }
3467   else
3468   {
3469     SBX_Left = -1;
3470     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3471     SBY_Upper = -1;
3472     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3473   }
3474
3475   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3476     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3477
3478   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3479     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3480
3481   /* if local player not found, look for custom element that might create
3482      the player (make some assumptions about the right custom element) */
3483   if (!local_player->present)
3484   {
3485     int start_x = 0, start_y = 0;
3486     int found_rating = 0;
3487     int found_element = EL_UNDEFINED;
3488     int player_nr = local_player->index_nr;
3489
3490     SCAN_PLAYFIELD(x, y)
3491     {
3492       int element = Feld[x][y];
3493       int content;
3494       int xx, yy;
3495       boolean is_player;
3496
3497       if (level.use_start_element[player_nr] &&
3498           level.start_element[player_nr] == element &&
3499           found_rating < 4)
3500       {
3501         start_x = x;
3502         start_y = y;
3503
3504         found_rating = 4;
3505         found_element = element;
3506       }
3507
3508       if (!IS_CUSTOM_ELEMENT(element))
3509         continue;
3510
3511       if (CAN_CHANGE(element))
3512       {
3513         for (i = 0; i < element_info[element].num_change_pages; i++)
3514         {
3515           /* check for player created from custom element as single target */
3516           content = element_info[element].change_page[i].target_element;
3517           is_player = ELEM_IS_PLAYER(content);
3518
3519           if (is_player && (found_rating < 3 ||
3520                             (found_rating == 3 && element < found_element)))
3521           {
3522             start_x = x;
3523             start_y = y;
3524
3525             found_rating = 3;
3526             found_element = element;
3527           }
3528         }
3529       }
3530
3531       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3532       {
3533         /* check for player created from custom element as explosion content */
3534         content = element_info[element].content.e[xx][yy];
3535         is_player = ELEM_IS_PLAYER(content);
3536
3537         if (is_player && (found_rating < 2 ||
3538                           (found_rating == 2 && element < found_element)))
3539         {
3540           start_x = x + xx - 1;
3541           start_y = y + yy - 1;
3542
3543           found_rating = 2;
3544           found_element = element;
3545         }
3546
3547         if (!CAN_CHANGE(element))
3548           continue;
3549
3550         for (i = 0; i < element_info[element].num_change_pages; i++)
3551         {
3552           /* check for player created from custom element as extended target */
3553           content =
3554             element_info[element].change_page[i].target_content.e[xx][yy];
3555
3556           is_player = ELEM_IS_PLAYER(content);
3557
3558           if (is_player && (found_rating < 1 ||
3559                             (found_rating == 1 && element < found_element)))
3560           {
3561             start_x = x + xx - 1;
3562             start_y = y + yy - 1;
3563
3564             found_rating = 1;
3565             found_element = element;
3566           }
3567         }
3568       }
3569     }
3570
3571     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3572                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3573                 start_x - MIDPOSX);
3574
3575     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3576                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3577                 start_y - MIDPOSY);
3578   }
3579   else
3580   {
3581     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3582                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3583                 local_player->jx - MIDPOSX);
3584
3585     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3586                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3587                 local_player->jy - MIDPOSY);
3588   }
3589
3590   /* do not use PLAYING mask for fading out from main screen */
3591   game_status = GAME_MODE_MAIN;
3592
3593   StopAnimation();
3594
3595   if (!game.restart_level)
3596     CloseDoor(DOOR_CLOSE_1);
3597
3598 #if 1
3599   if (level_editor_test_game)
3600     FadeSkipNextFadeIn();
3601   else
3602     FadeSetEnterScreen();
3603 #else
3604   if (level_editor_test_game)
3605     fading = fading_none;
3606   else
3607     fading = menu.destination;
3608 #endif
3609
3610 #if 1
3611   FadeOut(REDRAW_FIELD);
3612 #else
3613   if (do_fading)
3614     FadeOut(REDRAW_FIELD);
3615 #endif
3616
3617   game_status = GAME_MODE_PLAYING;
3618
3619   /* !!! FIX THIS (START) !!! */
3620   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3621   {
3622     InitGameEngine_EM();
3623
3624     /* blit playfield from scroll buffer to normal back buffer for fading in */
3625     BlitScreenToBitmap_EM(backbuffer);
3626   }
3627   else
3628   {
3629     DrawLevel();
3630     DrawAllPlayers();
3631
3632     /* after drawing the level, correct some elements */
3633     if (game.timegate_time_left == 0)
3634       CloseAllOpenTimegates();
3635
3636     /* blit playfield from scroll buffer to normal back buffer for fading in */
3637     if (setup.soft_scrolling)
3638       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3639
3640     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3641   }
3642   /* !!! FIX THIS (END) !!! */
3643
3644 #if 1
3645   FadeIn(REDRAW_FIELD);
3646 #else
3647   if (do_fading)
3648     FadeIn(REDRAW_FIELD);
3649
3650   BackToFront();
3651 #endif
3652
3653   if (!game.restart_level)
3654   {
3655     /* copy default game door content to main double buffer */
3656     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3657                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3658   }
3659
3660   SetPanelBackground();
3661   SetDrawBackgroundMask(REDRAW_DOOR_1);
3662
3663   DrawGameDoorValues();
3664
3665   if (!game.restart_level)
3666   {
3667     UnmapGameButtons();
3668     UnmapTapeButtons();
3669     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3670     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3671     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3672     MapGameButtons();
3673     MapTapeButtons();
3674
3675     /* copy actual game door content to door double buffer for OpenDoor() */
3676     BlitBitmap(drawto, bitmap_db_door,
3677                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3678
3679     OpenDoor(DOOR_OPEN_ALL);
3680
3681     PlaySound(SND_GAME_STARTING);
3682
3683     if (setup.sound_music)
3684       PlayLevelMusic();
3685
3686     KeyboardAutoRepeatOffUnlessAutoplay();
3687
3688     if (options.debug)
3689     {
3690       for (i = 0; i < MAX_PLAYERS; i++)
3691         printf("Player %d %sactive.\n",
3692                i + 1, (stored_player[i].active ? "" : "not "));
3693     }
3694   }
3695
3696 #if 1
3697   UnmapAllGadgets();
3698
3699   MapGameButtons();
3700   MapTapeButtons();
3701 #endif
3702
3703   game.restart_level = FALSE;
3704 }
3705
3706 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3707 {
3708   /* this is used for non-R'n'D game engines to update certain engine values */
3709
3710   /* needed to determine if sounds are played within the visible screen area */
3711   scroll_x = actual_scroll_x;
3712   scroll_y = actual_scroll_y;
3713 }
3714
3715 void InitMovDir(int x, int y)
3716 {
3717   int i, element = Feld[x][y];
3718   static int xy[4][2] =
3719   {
3720     {  0, +1 },
3721     { +1,  0 },
3722     {  0, -1 },
3723     { -1,  0 }
3724   };
3725   static int direction[3][4] =
3726   {
3727     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3728     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3729     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3730   };
3731
3732   switch (element)
3733   {
3734     case EL_BUG_RIGHT:
3735     case EL_BUG_UP:
3736     case EL_BUG_LEFT:
3737     case EL_BUG_DOWN:
3738       Feld[x][y] = EL_BUG;
3739       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3740       break;
3741
3742     case EL_SPACESHIP_RIGHT:
3743     case EL_SPACESHIP_UP:
3744     case EL_SPACESHIP_LEFT:
3745     case EL_SPACESHIP_DOWN:
3746       Feld[x][y] = EL_SPACESHIP;
3747       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3748       break;
3749
3750     case EL_BD_BUTTERFLY_RIGHT:
3751     case EL_BD_BUTTERFLY_UP:
3752     case EL_BD_BUTTERFLY_LEFT:
3753     case EL_BD_BUTTERFLY_DOWN:
3754       Feld[x][y] = EL_BD_BUTTERFLY;
3755       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3756       break;
3757
3758     case EL_BD_FIREFLY_RIGHT:
3759     case EL_BD_FIREFLY_UP:
3760     case EL_BD_FIREFLY_LEFT:
3761     case EL_BD_FIREFLY_DOWN:
3762       Feld[x][y] = EL_BD_FIREFLY;
3763       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3764       break;
3765
3766     case EL_PACMAN_RIGHT:
3767     case EL_PACMAN_UP:
3768     case EL_PACMAN_LEFT:
3769     case EL_PACMAN_DOWN:
3770       Feld[x][y] = EL_PACMAN;
3771       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3772       break;
3773
3774     case EL_YAMYAM_LEFT:
3775     case EL_YAMYAM_RIGHT:
3776     case EL_YAMYAM_UP:
3777     case EL_YAMYAM_DOWN:
3778       Feld[x][y] = EL_YAMYAM;
3779       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3780       break;
3781
3782     case EL_SP_SNIKSNAK:
3783       MovDir[x][y] = MV_UP;
3784       break;
3785
3786     case EL_SP_ELECTRON:
3787       MovDir[x][y] = MV_LEFT;
3788       break;
3789
3790     case EL_MOLE_LEFT:
3791     case EL_MOLE_RIGHT:
3792     case EL_MOLE_UP:
3793     case EL_MOLE_DOWN:
3794       Feld[x][y] = EL_MOLE;
3795       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3796       break;
3797
3798     default:
3799       if (IS_CUSTOM_ELEMENT(element))
3800       {
3801         struct ElementInfo *ei = &element_info[element];
3802         int move_direction_initial = ei->move_direction_initial;
3803         int move_pattern = ei->move_pattern;
3804
3805         if (move_direction_initial == MV_START_PREVIOUS)
3806         {
3807           if (MovDir[x][y] != MV_NONE)
3808             return;
3809
3810           move_direction_initial = MV_START_AUTOMATIC;
3811         }
3812
3813         if (move_direction_initial == MV_START_RANDOM)
3814           MovDir[x][y] = 1 << RND(4);
3815         else if (move_direction_initial & MV_ANY_DIRECTION)
3816           MovDir[x][y] = move_direction_initial;
3817         else if (move_pattern == MV_ALL_DIRECTIONS ||
3818                  move_pattern == MV_TURNING_LEFT ||
3819                  move_pattern == MV_TURNING_RIGHT ||
3820                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3821                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3822                  move_pattern == MV_TURNING_RANDOM)
3823           MovDir[x][y] = 1 << RND(4);
3824         else if (move_pattern == MV_HORIZONTAL)
3825           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3826         else if (move_pattern == MV_VERTICAL)
3827           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3828         else if (move_pattern & MV_ANY_DIRECTION)
3829           MovDir[x][y] = element_info[element].move_pattern;
3830         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3831                  move_pattern == MV_ALONG_RIGHT_SIDE)
3832         {
3833           /* use random direction as default start direction */
3834           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3835             MovDir[x][y] = 1 << RND(4);
3836
3837           for (i = 0; i < NUM_DIRECTIONS; i++)
3838           {
3839             int x1 = x + xy[i][0];
3840             int y1 = y + xy[i][1];
3841
3842             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3843             {
3844               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3845                 MovDir[x][y] = direction[0][i];
3846               else
3847                 MovDir[x][y] = direction[1][i];
3848
3849               break;
3850             }
3851           }
3852         }                
3853       }
3854       else
3855       {
3856         MovDir[x][y] = 1 << RND(4);
3857
3858         if (element != EL_BUG &&
3859             element != EL_SPACESHIP &&
3860             element != EL_BD_BUTTERFLY &&
3861             element != EL_BD_FIREFLY)
3862           break;
3863
3864         for (i = 0; i < NUM_DIRECTIONS; i++)
3865         {
3866           int x1 = x + xy[i][0];
3867           int y1 = y + xy[i][1];
3868
3869           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3870           {
3871             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3872             {
3873               MovDir[x][y] = direction[0][i];
3874               break;
3875             }
3876             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3877                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3878             {
3879               MovDir[x][y] = direction[1][i];
3880               break;
3881             }
3882           }
3883         }
3884       }
3885       break;
3886   }
3887
3888   GfxDir[x][y] = MovDir[x][y];
3889 }
3890
3891 void InitAmoebaNr(int x, int y)
3892 {
3893   int i;
3894   int group_nr = AmoebeNachbarNr(x, y);
3895
3896   if (group_nr == 0)
3897   {
3898     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3899     {
3900       if (AmoebaCnt[i] == 0)
3901       {
3902         group_nr = i;
3903         break;
3904       }
3905     }
3906   }
3907
3908   AmoebaNr[x][y] = group_nr;
3909   AmoebaCnt[group_nr]++;
3910   AmoebaCnt2[group_nr]++;
3911 }
3912
3913 static void PlayerWins(struct PlayerInfo *player)
3914 {
3915   player->LevelSolved = TRUE;
3916   player->GameOver = TRUE;
3917
3918   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3919                          level.native_em_level->lev->score : player->score);
3920 }
3921
3922 void GameWon()
3923 {
3924   static int time, time_final;
3925   static int score, score_final;
3926   static int game_over_delay_1 = 0;
3927   static int game_over_delay_2 = 0;
3928   int game_over_delay_value_1 = 50;
3929   int game_over_delay_value_2 = 50;
3930
3931   if (!local_player->LevelSolved_GameWon)
3932   {
3933     int i;
3934
3935     /* do not start end game actions before the player stops moving (to exit) */
3936     if (local_player->MovPos)
3937       return;
3938
3939     local_player->LevelSolved_GameWon = TRUE;
3940     local_player->LevelSolved_SaveTape = tape.recording;
3941     local_player->LevelSolved_SaveScore = !tape.playing;
3942
3943     if (tape.auto_play)         /* tape might already be stopped here */
3944       tape.auto_play_level_solved = TRUE;
3945
3946 #if 1
3947     TapeStop();
3948 #endif
3949
3950     game_over_delay_1 = game_over_delay_value_1;
3951     game_over_delay_2 = game_over_delay_value_2;
3952
3953     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3954     score = score_final = local_player->score_final;
3955
3956     if (TimeLeft > 0)
3957     {
3958       time_final = 0;
3959       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3960     }
3961     else if (level.time == 0 && TimePlayed < 999)
3962     {
3963       time_final = 999;
3964       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3965     }
3966
3967     local_player->score_final = score_final;
3968
3969     if (level_editor_test_game)
3970     {
3971       time = time_final;
3972       score = score_final;
3973
3974 #if 1
3975       game_control_value[GAME_CONTROL_TIME] = time;
3976       game_control_value[GAME_CONTROL_SCORE] = score;
3977
3978       DisplayGameControlValues();
3979 #else
3980       DrawGameValue_Time(time);
3981       DrawGameValue_Score(score);
3982 #endif
3983     }
3984
3985     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3986     {
3987       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3988       {
3989         /* close exit door after last player */
3990         if ((AllPlayersGone &&
3991              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3992               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3993               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3994             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3995             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3996         {
3997           int element = Feld[ExitX][ExitY];
3998
3999 #if 0
4000           if (element == EL_EM_EXIT_OPEN ||
4001               element == EL_EM_STEEL_EXIT_OPEN)
4002           {
4003             Bang(ExitX, ExitY);
4004           }
4005           else
4006 #endif
4007           {
4008             Feld[ExitX][ExitY] =
4009               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4010                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4011                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4012                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4013                EL_EM_STEEL_EXIT_CLOSING);
4014
4015             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4016           }
4017         }
4018
4019         /* player disappears */
4020         DrawLevelField(ExitX, ExitY);
4021       }
4022
4023       for (i = 0; i < MAX_PLAYERS; i++)
4024       {
4025         struct PlayerInfo *player = &stored_player[i];
4026
4027         if (player->present)
4028         {
4029           RemovePlayer(player);
4030
4031           /* player disappears */
4032           DrawLevelField(player->jx, player->jy);
4033         }
4034       }
4035     }
4036
4037     PlaySound(SND_GAME_WINNING);
4038   }
4039
4040   if (game_over_delay_1 > 0)
4041   {
4042     game_over_delay_1--;
4043
4044     return;
4045   }
4046
4047   if (time != time_final)
4048   {
4049     int time_to_go = ABS(time_final - time);
4050     int time_count_dir = (time < time_final ? +1 : -1);
4051     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4052
4053     time  += time_count_steps * time_count_dir;
4054     score += time_count_steps * level.score[SC_TIME_BONUS];
4055
4056 #if 1
4057     game_control_value[GAME_CONTROL_TIME] = time;
4058     game_control_value[GAME_CONTROL_SCORE] = score;
4059
4060     DisplayGameControlValues();
4061 #else
4062     DrawGameValue_Time(time);
4063     DrawGameValue_Score(score);
4064 #endif
4065
4066     if (time == time_final)
4067       StopSound(SND_GAME_LEVELTIME_BONUS);
4068     else if (setup.sound_loops)
4069       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4070     else
4071       PlaySound(SND_GAME_LEVELTIME_BONUS);
4072
4073     return;
4074   }
4075
4076   local_player->LevelSolved_PanelOff = TRUE;
4077
4078   if (game_over_delay_2 > 0)
4079   {
4080     game_over_delay_2--;
4081
4082     return;
4083   }
4084
4085 #if 1
4086   GameEnd();
4087 #endif
4088 }
4089
4090 void GameEnd()
4091 {
4092   int hi_pos;
4093   boolean raise_level = FALSE;
4094
4095   local_player->LevelSolved_GameEnd = TRUE;
4096
4097   CloseDoor(DOOR_CLOSE_1);
4098
4099   if (local_player->LevelSolved_SaveTape)
4100   {
4101 #if 0
4102     TapeStop();
4103 #endif
4104
4105 #if 1
4106     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4107 #else
4108     SaveTape(tape.level_nr);            /* ask to save tape */
4109 #endif
4110   }
4111
4112   if (level_editor_test_game)
4113   {
4114     game_status = GAME_MODE_MAIN;
4115
4116 #if 1
4117     DrawAndFadeInMainMenu(REDRAW_FIELD);
4118 #else
4119     DrawMainMenu();
4120 #endif
4121
4122     return;
4123   }
4124
4125   if (!local_player->LevelSolved_SaveScore)
4126   {
4127 #if 1
4128     FadeOut(REDRAW_FIELD);
4129 #endif
4130
4131     game_status = GAME_MODE_MAIN;
4132
4133     DrawAndFadeInMainMenu(REDRAW_FIELD);
4134
4135     return;
4136   }
4137
4138   if (level_nr == leveldir_current->handicap_level)
4139   {
4140     leveldir_current->handicap_level++;
4141     SaveLevelSetup_SeriesInfo();
4142   }
4143
4144   if (level_nr < leveldir_current->last_level)
4145     raise_level = TRUE;                 /* advance to next level */
4146
4147   if ((hi_pos = NewHiScore()) >= 0) 
4148   {
4149     game_status = GAME_MODE_SCORES;
4150
4151     DrawHallOfFame(hi_pos);
4152
4153     if (raise_level)
4154     {
4155       level_nr++;
4156       TapeErase();
4157     }
4158   }
4159   else
4160   {
4161 #if 1
4162     FadeOut(REDRAW_FIELD);
4163 #endif
4164
4165     game_status = GAME_MODE_MAIN;
4166
4167     if (raise_level)
4168     {
4169       level_nr++;
4170       TapeErase();
4171     }
4172
4173     DrawAndFadeInMainMenu(REDRAW_FIELD);
4174   }
4175 }
4176
4177 int NewHiScore()
4178 {
4179   int k, l;
4180   int position = -1;
4181
4182   LoadScore(level_nr);
4183
4184   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4185       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4186     return -1;
4187
4188   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4189   {
4190     if (local_player->score_final > highscore[k].Score)
4191     {
4192       /* player has made it to the hall of fame */
4193
4194       if (k < MAX_SCORE_ENTRIES - 1)
4195       {
4196         int m = MAX_SCORE_ENTRIES - 1;
4197
4198 #ifdef ONE_PER_NAME
4199         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4200           if (strEqual(setup.player_name, highscore[l].Name))
4201             m = l;
4202         if (m == k)     /* player's new highscore overwrites his old one */
4203           goto put_into_list;
4204 #endif
4205
4206         for (l = m; l > k; l--)
4207         {
4208           strcpy(highscore[l].Name, highscore[l - 1].Name);
4209           highscore[l].Score = highscore[l - 1].Score;
4210         }
4211       }
4212
4213 #ifdef ONE_PER_NAME
4214       put_into_list:
4215 #endif
4216       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4217       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4218       highscore[k].Score = local_player->score_final; 
4219       position = k;
4220       break;
4221     }
4222
4223 #ifdef ONE_PER_NAME
4224     else if (!strncmp(setup.player_name, highscore[k].Name,
4225                       MAX_PLAYER_NAME_LEN))
4226       break;    /* player already there with a higher score */
4227 #endif
4228
4229   }
4230
4231   if (position >= 0) 
4232     SaveScore(level_nr);
4233
4234   return position;
4235 }
4236
4237 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4238 {
4239   int element = Feld[x][y];
4240   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4241   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4242   int horiz_move = (dx != 0);
4243   int sign = (horiz_move ? dx : dy);
4244   int step = sign * element_info[element].move_stepsize;
4245
4246   /* special values for move stepsize for spring and things on conveyor belt */
4247   if (horiz_move)
4248   {
4249     if (CAN_FALL(element) &&
4250         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4251       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4252     else if (element == EL_SPRING)
4253       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4254   }
4255
4256   return step;
4257 }
4258
4259 inline static int getElementMoveStepsize(int x, int y)
4260 {
4261   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4262 }
4263
4264 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4265 {
4266   if (player->GfxAction != action || player->GfxDir != dir)
4267   {
4268 #if 0
4269     printf("Player frame reset! (%d => %d, %d => %d)\n",
4270            player->GfxAction, action, player->GfxDir, dir);
4271 #endif
4272
4273     player->GfxAction = action;
4274     player->GfxDir = dir;
4275     player->Frame = 0;
4276     player->StepFrame = 0;
4277   }
4278 }
4279
4280 #if USE_GFX_RESET_GFX_ANIMATION
4281 static void ResetGfxFrame(int x, int y, boolean redraw)
4282 {
4283   int element = Feld[x][y];
4284   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4285   int last_gfx_frame = GfxFrame[x][y];
4286
4287   if (graphic_info[graphic].anim_global_sync)
4288     GfxFrame[x][y] = FrameCounter;
4289   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4290     GfxFrame[x][y] = CustomValue[x][y];
4291   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4292     GfxFrame[x][y] = element_info[element].collect_score;
4293   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4294     GfxFrame[x][y] = ChangeDelay[x][y];
4295
4296   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4297     DrawLevelGraphicAnimation(x, y, graphic);
4298 }
4299 #endif
4300
4301 static void ResetGfxAnimation(int x, int y)
4302 {
4303   GfxAction[x][y] = ACTION_DEFAULT;
4304   GfxDir[x][y] = MovDir[x][y];
4305   GfxFrame[x][y] = 0;
4306
4307 #if USE_GFX_RESET_GFX_ANIMATION
4308   ResetGfxFrame(x, y, FALSE);
4309 #endif
4310 }
4311
4312 static void ResetRandomAnimationValue(int x, int y)
4313 {
4314   GfxRandom[x][y] = INIT_GFX_RANDOM();
4315 }
4316
4317 void InitMovingField(int x, int y, int direction)
4318 {
4319   int element = Feld[x][y];
4320   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4321   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4322   int newx = x + dx;
4323   int newy = y + dy;
4324   boolean is_moving_before, is_moving_after;
4325 #if 0
4326   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4327 #endif
4328
4329   /* check if element was/is moving or being moved before/after mode change */
4330 #if 1
4331 #if 1
4332   is_moving_before = (WasJustMoving[x][y] != 0);
4333 #else
4334   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4335   is_moving_before = WasJustMoving[x][y];
4336 #endif
4337 #else
4338   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4339 #endif
4340   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4341
4342   /* reset animation only for moving elements which change direction of moving
4343      or which just started or stopped moving
4344      (else CEs with property "can move" / "not moving" are reset each frame) */
4345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4346 #if 1
4347   if (is_moving_before != is_moving_after ||
4348       direction != MovDir[x][y])
4349     ResetGfxAnimation(x, y);
4350 #else
4351   if ((is_moving_before || is_moving_after) && !continues_moving)
4352     ResetGfxAnimation(x, y);
4353 #endif
4354 #else
4355   if (!continues_moving)
4356     ResetGfxAnimation(x, y);
4357 #endif
4358
4359   MovDir[x][y] = direction;
4360   GfxDir[x][y] = direction;
4361
4362 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4363   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4364                      direction == MV_DOWN && CAN_FALL(element) ?
4365                      ACTION_FALLING : ACTION_MOVING);
4366 #else
4367   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4368                      ACTION_FALLING : ACTION_MOVING);
4369 #endif
4370
4371   /* this is needed for CEs with property "can move" / "not moving" */
4372
4373   if (is_moving_after)
4374   {
4375     if (Feld[newx][newy] == EL_EMPTY)
4376       Feld[newx][newy] = EL_BLOCKED;
4377
4378     MovDir[newx][newy] = MovDir[x][y];
4379
4380 #if USE_NEW_CUSTOM_VALUE
4381     CustomValue[newx][newy] = CustomValue[x][y];
4382 #endif
4383
4384     GfxFrame[newx][newy] = GfxFrame[x][y];
4385     GfxRandom[newx][newy] = GfxRandom[x][y];
4386     GfxAction[newx][newy] = GfxAction[x][y];
4387     GfxDir[newx][newy] = GfxDir[x][y];
4388   }
4389 }
4390
4391 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4392 {
4393   int direction = MovDir[x][y];
4394   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4395   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4396
4397   *goes_to_x = newx;
4398   *goes_to_y = newy;
4399 }
4400
4401 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4402 {
4403   int oldx = x, oldy = y;
4404   int direction = MovDir[x][y];
4405
4406   if (direction == MV_LEFT)
4407     oldx++;
4408   else if (direction == MV_RIGHT)
4409     oldx--;
4410   else if (direction == MV_UP)
4411     oldy++;
4412   else if (direction == MV_DOWN)
4413     oldy--;
4414
4415   *comes_from_x = oldx;
4416   *comes_from_y = oldy;
4417 }
4418
4419 int MovingOrBlocked2Element(int x, int y)
4420 {
4421   int element = Feld[x][y];
4422
4423   if (element == EL_BLOCKED)
4424   {
4425     int oldx, oldy;
4426
4427     Blocked2Moving(x, y, &oldx, &oldy);
4428     return Feld[oldx][oldy];
4429   }
4430   else
4431     return element;
4432 }
4433
4434 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4435 {
4436   /* like MovingOrBlocked2Element(), but if element is moving
4437      and (x,y) is the field the moving element is just leaving,
4438      return EL_BLOCKED instead of the element value */
4439   int element = Feld[x][y];
4440
4441   if (IS_MOVING(x, y))
4442   {
4443     if (element == EL_BLOCKED)
4444     {
4445       int oldx, oldy;
4446
4447       Blocked2Moving(x, y, &oldx, &oldy);
4448       return Feld[oldx][oldy];
4449     }
4450     else
4451       return EL_BLOCKED;
4452   }
4453   else
4454     return element;
4455 }
4456
4457 static void RemoveField(int x, int y)
4458 {
4459   Feld[x][y] = EL_EMPTY;
4460
4461   MovPos[x][y] = 0;
4462   MovDir[x][y] = 0;
4463   MovDelay[x][y] = 0;
4464
4465 #if USE_NEW_CUSTOM_VALUE
4466   CustomValue[x][y] = 0;
4467 #endif
4468
4469   AmoebaNr[x][y] = 0;
4470   ChangeDelay[x][y] = 0;
4471   ChangePage[x][y] = -1;
4472   Pushed[x][y] = FALSE;
4473
4474 #if 0
4475   ExplodeField[x][y] = EX_TYPE_NONE;
4476 #endif
4477
4478   GfxElement[x][y] = EL_UNDEFINED;
4479   GfxAction[x][y] = ACTION_DEFAULT;
4480   GfxDir[x][y] = MV_NONE;
4481 }
4482
4483 void RemoveMovingField(int x, int y)
4484 {
4485   int oldx = x, oldy = y, newx = x, newy = y;
4486   int element = Feld[x][y];
4487   int next_element = EL_UNDEFINED;
4488
4489   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4490     return;
4491
4492   if (IS_MOVING(x, y))
4493   {
4494     Moving2Blocked(x, y, &newx, &newy);
4495
4496     if (Feld[newx][newy] != EL_BLOCKED)
4497     {
4498       /* element is moving, but target field is not free (blocked), but
4499          already occupied by something different (example: acid pool);
4500          in this case, only remove the moving field, but not the target */
4501
4502       RemoveField(oldx, oldy);
4503
4504       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4505
4506       DrawLevelField(oldx, oldy);
4507
4508       return;
4509     }
4510   }
4511   else if (element == EL_BLOCKED)
4512   {
4513     Blocked2Moving(x, y, &oldx, &oldy);
4514     if (!IS_MOVING(oldx, oldy))
4515       return;
4516   }
4517
4518   if (element == EL_BLOCKED &&
4519       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4520        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4521        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4522        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4523        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4524        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4525     next_element = get_next_element(Feld[oldx][oldy]);
4526
4527   RemoveField(oldx, oldy);
4528   RemoveField(newx, newy);
4529
4530   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4531
4532   if (next_element != EL_UNDEFINED)
4533     Feld[oldx][oldy] = next_element;
4534
4535   DrawLevelField(oldx, oldy);
4536   DrawLevelField(newx, newy);
4537 }
4538
4539 void DrawDynamite(int x, int y)
4540 {
4541   int sx = SCREENX(x), sy = SCREENY(y);
4542   int graphic = el2img(Feld[x][y]);
4543   int frame;
4544
4545   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4546     return;
4547
4548   if (IS_WALKABLE_INSIDE(Back[x][y]))
4549     return;
4550
4551   if (Back[x][y])
4552     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4553   else if (Store[x][y])
4554     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4555
4556   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4557
4558   if (Back[x][y] || Store[x][y])
4559     DrawGraphicThruMask(sx, sy, graphic, frame);
4560   else
4561     DrawGraphic(sx, sy, graphic, frame);
4562 }
4563
4564 void CheckDynamite(int x, int y)
4565 {
4566   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4567   {
4568     MovDelay[x][y]--;
4569
4570     if (MovDelay[x][y] != 0)
4571     {
4572       DrawDynamite(x, y);
4573       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4574
4575       return;
4576     }
4577   }
4578
4579   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4580
4581   Bang(x, y);
4582 }
4583
4584 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4585 {
4586   boolean num_checked_players = 0;
4587   int i;
4588
4589   for (i = 0; i < MAX_PLAYERS; i++)
4590   {
4591     if (stored_player[i].active)
4592     {
4593       int sx = stored_player[i].jx;
4594       int sy = stored_player[i].jy;
4595
4596       if (num_checked_players == 0)
4597       {
4598         *sx1 = *sx2 = sx;
4599         *sy1 = *sy2 = sy;
4600       }
4601       else
4602       {
4603         *sx1 = MIN(*sx1, sx);
4604         *sy1 = MIN(*sy1, sy);
4605         *sx2 = MAX(*sx2, sx);
4606         *sy2 = MAX(*sy2, sy);
4607       }
4608
4609       num_checked_players++;
4610     }
4611   }
4612 }
4613
4614 static boolean checkIfAllPlayersFitToScreen_RND()
4615 {
4616   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4617
4618   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4619
4620   return (sx2 - sx1 < SCR_FIELDX &&
4621           sy2 - sy1 < SCR_FIELDY);
4622 }
4623
4624 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4625 {
4626   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4627
4628   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4629
4630   *sx = (sx1 + sx2) / 2;
4631   *sy = (sy1 + sy2) / 2;
4632 }
4633
4634 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4635                         boolean center_screen, boolean quick_relocation)
4636 {
4637   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4638   boolean no_delay = (tape.warp_forward);
4639   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4640   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4641
4642   if (quick_relocation)
4643   {
4644     int offset = game.scroll_delay_value;
4645
4646     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4647     {
4648       if (!level.shifted_relocation || center_screen)
4649       {
4650         /* quick relocation (without scrolling), with centering of screen */
4651
4652         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4653                     x > SBX_Right + MIDPOSX ? SBX_Right :
4654                     x - MIDPOSX);
4655
4656         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4657                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4658                     y - MIDPOSY);
4659       }
4660       else
4661       {
4662         /* quick relocation (without scrolling), but do not center screen */
4663
4664         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4665                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4666                                old_x - MIDPOSX);
4667
4668         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4669                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4670                                old_y - MIDPOSY);
4671
4672         int offset_x = x + (scroll_x - center_scroll_x);
4673         int offset_y = y + (scroll_y - center_scroll_y);
4674
4675         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4676                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4677                     offset_x - MIDPOSX);
4678
4679         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4680                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4681                     offset_y - MIDPOSY);
4682       }
4683     }
4684     else
4685     {
4686       /* quick relocation (without scrolling), inside visible screen area */
4687
4688       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4689           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4690         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4691
4692       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4693           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4694         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4695
4696       /* don't scroll over playfield boundaries */
4697       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4698         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4699
4700       /* don't scroll over playfield boundaries */
4701       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4702         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4703     }
4704
4705     RedrawPlayfield(TRUE, 0,0,0,0);
4706   }
4707   else
4708   {
4709 #if 1
4710     int scroll_xx, scroll_yy;
4711
4712     if (!level.shifted_relocation || center_screen)
4713     {
4714       /* visible relocation (with scrolling), with centering of screen */
4715
4716       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4717                    x > SBX_Right + MIDPOSX ? SBX_Right :
4718                    x - MIDPOSX);
4719
4720       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4721                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4722                    y - MIDPOSY);
4723     }
4724     else
4725     {
4726       /* visible relocation (with scrolling), but do not center screen */
4727
4728       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4729                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4730                              old_x - MIDPOSX);
4731
4732       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4733                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4734                              old_y - MIDPOSY);
4735
4736       int offset_x = x + (scroll_x - center_scroll_x);
4737       int offset_y = y + (scroll_y - center_scroll_y);
4738
4739       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4740                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4741                    offset_x - MIDPOSX);
4742
4743       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4744                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4745                    offset_y - MIDPOSY);
4746     }
4747
4748 #else
4749
4750     /* visible relocation (with scrolling), with centering of screen */
4751
4752     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4753                      x > SBX_Right + MIDPOSX ? SBX_Right :
4754                      x - MIDPOSX);
4755
4756     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4757                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4758                      y - MIDPOSY);
4759 #endif
4760
4761     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4762
4763     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4764     {
4765       int dx = 0, dy = 0;
4766       int fx = FX, fy = FY;
4767
4768       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4769       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4770
4771       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4772         break;
4773
4774       scroll_x -= dx;
4775       scroll_y -= dy;
4776
4777       fx += dx * TILEX / 2;
4778       fy += dy * TILEY / 2;
4779
4780       ScrollLevel(dx, dy);
4781       DrawAllPlayers();
4782
4783       /* scroll in two steps of half tile size to make things smoother */
4784       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4785       FlushDisplay();
4786       Delay(wait_delay_value);
4787
4788       /* scroll second step to align at full tile size */
4789       BackToFront();
4790       Delay(wait_delay_value);
4791     }
4792
4793     DrawAllPlayers();
4794     BackToFront();
4795     Delay(wait_delay_value);
4796   }
4797 }
4798
4799 void RelocatePlayer(int jx, int jy, int el_player_raw)
4800 {
4801   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4802   int player_nr = GET_PLAYER_NR(el_player);
4803   struct PlayerInfo *player = &stored_player[player_nr];
4804   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4805   boolean no_delay = (tape.warp_forward);
4806   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4807   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4808   int old_jx = player->jx;
4809   int old_jy = player->jy;
4810   int old_element = Feld[old_jx][old_jy];
4811   int element = Feld[jx][jy];
4812   boolean player_relocated = (old_jx != jx || old_jy != jy);
4813
4814   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4815   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4816   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4817   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4818   int leave_side_horiz = move_dir_horiz;
4819   int leave_side_vert  = move_dir_vert;
4820   int enter_side = enter_side_horiz | enter_side_vert;
4821   int leave_side = leave_side_horiz | leave_side_vert;
4822
4823   if (player->GameOver)         /* do not reanimate dead player */
4824     return;
4825
4826   if (!player_relocated)        /* no need to relocate the player */
4827     return;
4828
4829   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4830   {
4831     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4832     DrawLevelField(jx, jy);
4833   }
4834
4835   if (player->present)
4836   {
4837     while (player->MovPos)
4838     {
4839       ScrollPlayer(player, SCROLL_GO_ON);
4840       ScrollScreen(NULL, SCROLL_GO_ON);
4841
4842       AdvanceFrameAndPlayerCounters(player->index_nr);
4843
4844       DrawPlayer(player);
4845
4846       BackToFront();
4847       Delay(wait_delay_value);
4848     }
4849
4850     DrawPlayer(player);         /* needed here only to cleanup last field */
4851     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4852
4853     player->is_moving = FALSE;
4854   }
4855
4856   if (IS_CUSTOM_ELEMENT(old_element))
4857     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4858                                CE_LEFT_BY_PLAYER,
4859                                player->index_bit, leave_side);
4860
4861   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4862                                       CE_PLAYER_LEAVES_X,
4863                                       player->index_bit, leave_side);
4864
4865   Feld[jx][jy] = el_player;
4866   InitPlayerField(jx, jy, el_player, TRUE);
4867
4868   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4869   {
4870     Feld[jx][jy] = element;
4871     InitField(jx, jy, FALSE);
4872   }
4873
4874   /* only visually relocate centered player */
4875   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4876                      FALSE, level.instant_relocation);
4877
4878   TestIfPlayerTouchesBadThing(jx, jy);
4879   TestIfPlayerTouchesCustomElement(jx, jy);
4880
4881   if (IS_CUSTOM_ELEMENT(element))
4882     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4883                                player->index_bit, enter_side);
4884
4885   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4886                                       player->index_bit, enter_side);
4887 }
4888
4889 void Explode(int ex, int ey, int phase, int mode)
4890 {
4891   int x, y;
4892   int last_phase;
4893   int border_element;
4894
4895   /* !!! eliminate this variable !!! */
4896   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4897
4898   if (game.explosions_delayed)
4899   {
4900     ExplodeField[ex][ey] = mode;
4901     return;
4902   }
4903
4904   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4905   {
4906     int center_element = Feld[ex][ey];
4907     int artwork_element, explosion_element;     /* set these values later */
4908
4909 #if 0
4910     /* --- This is only really needed (and now handled) in "Impact()". --- */
4911     /* do not explode moving elements that left the explode field in time */
4912     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4913         center_element == EL_EMPTY &&
4914         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4915       return;
4916 #endif
4917
4918 #if 0
4919     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4920     if (mode == EX_TYPE_NORMAL ||
4921         mode == EX_TYPE_CENTER ||
4922         mode == EX_TYPE_CROSS)
4923       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4924 #endif
4925
4926     /* remove things displayed in background while burning dynamite */
4927     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4928       Back[ex][ey] = 0;
4929
4930     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4931     {
4932       /* put moving element to center field (and let it explode there) */
4933       center_element = MovingOrBlocked2Element(ex, ey);
4934       RemoveMovingField(ex, ey);
4935       Feld[ex][ey] = center_element;
4936     }
4937
4938     /* now "center_element" is finally determined -- set related values now */
4939     artwork_element = center_element;           /* for custom player artwork */
4940     explosion_element = center_element;         /* for custom player artwork */
4941
4942     if (IS_PLAYER(ex, ey))
4943     {
4944       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4945
4946       artwork_element = stored_player[player_nr].artwork_element;
4947
4948       if (level.use_explosion_element[player_nr])
4949       {
4950         explosion_element = level.explosion_element[player_nr];
4951         artwork_element = explosion_element;
4952       }
4953     }
4954
4955 #if 1
4956     if (mode == EX_TYPE_NORMAL ||
4957         mode == EX_TYPE_CENTER ||
4958         mode == EX_TYPE_CROSS)
4959       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4960 #endif
4961
4962     last_phase = element_info[explosion_element].explosion_delay + 1;
4963
4964     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4965     {
4966       int xx = x - ex + 1;
4967       int yy = y - ey + 1;
4968       int element;
4969
4970       if (!IN_LEV_FIELD(x, y) ||
4971           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4972           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4973         continue;
4974
4975       element = Feld[x][y];
4976
4977       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4978       {
4979         element = MovingOrBlocked2Element(x, y);
4980
4981         if (!IS_EXPLOSION_PROOF(element))
4982           RemoveMovingField(x, y);
4983       }
4984
4985       /* indestructible elements can only explode in center (but not flames) */
4986       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4987                                            mode == EX_TYPE_BORDER)) ||
4988           element == EL_FLAMES)
4989         continue;
4990
4991       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4992          behaviour, for example when touching a yamyam that explodes to rocks
4993          with active deadly shield, a rock is created under the player !!! */
4994       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4995 #if 0
4996       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4997           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4998            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4999 #else
5000       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5001 #endif
5002       {
5003         if (IS_ACTIVE_BOMB(element))
5004         {
5005           /* re-activate things under the bomb like gate or penguin */
5006           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5007           Back[x][y] = 0;
5008         }
5009
5010         continue;
5011       }
5012
5013       /* save walkable background elements while explosion on same tile */
5014       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5015           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5016         Back[x][y] = element;
5017
5018       /* ignite explodable elements reached by other explosion */
5019       if (element == EL_EXPLOSION)
5020         element = Store2[x][y];
5021
5022       if (AmoebaNr[x][y] &&
5023           (element == EL_AMOEBA_FULL ||
5024            element == EL_BD_AMOEBA ||
5025            element == EL_AMOEBA_GROWING))
5026       {
5027         AmoebaCnt[AmoebaNr[x][y]]--;
5028         AmoebaCnt2[AmoebaNr[x][y]]--;
5029       }
5030
5031       RemoveField(x, y);
5032
5033       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5034       {
5035         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5036
5037         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5038
5039         if (PLAYERINFO(ex, ey)->use_murphy)
5040           Store[x][y] = EL_EMPTY;
5041       }
5042
5043       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5044          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5045       else if (ELEM_IS_PLAYER(center_element))
5046         Store[x][y] = EL_EMPTY;
5047       else if (center_element == EL_YAMYAM)
5048         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5049       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5050         Store[x][y] = element_info[center_element].content.e[xx][yy];
5051 #if 1
5052       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5053          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5054          otherwise) -- FIX THIS !!! */
5055       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5056         Store[x][y] = element_info[element].content.e[1][1];
5057 #else
5058       else if (!CAN_EXPLODE(element))
5059         Store[x][y] = element_info[element].content.e[1][1];
5060 #endif
5061       else
5062         Store[x][y] = EL_EMPTY;
5063
5064       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5065           center_element == EL_AMOEBA_TO_DIAMOND)
5066         Store2[x][y] = element;
5067
5068       Feld[x][y] = EL_EXPLOSION;
5069       GfxElement[x][y] = artwork_element;
5070
5071       ExplodePhase[x][y] = 1;
5072       ExplodeDelay[x][y] = last_phase;
5073
5074       Stop[x][y] = TRUE;
5075     }
5076
5077     if (center_element == EL_YAMYAM)
5078       game.yamyam_content_nr =
5079         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5080
5081     return;
5082   }
5083
5084   if (Stop[ex][ey])
5085     return;
5086
5087   x = ex;
5088   y = ey;
5089
5090   if (phase == 1)
5091     GfxFrame[x][y] = 0;         /* restart explosion animation */
5092
5093   last_phase = ExplodeDelay[x][y];
5094
5095   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5096
5097 #ifdef DEBUG
5098
5099   /* activate this even in non-DEBUG version until cause for crash in
5100      getGraphicAnimationFrame() (see below) is found and eliminated */
5101
5102 #endif
5103 #if 1
5104
5105 #if 1
5106   /* this can happen if the player leaves an explosion just in time */
5107   if (GfxElement[x][y] == EL_UNDEFINED)
5108     GfxElement[x][y] = EL_EMPTY;
5109 #else
5110   if (GfxElement[x][y] == EL_UNDEFINED)
5111   {
5112     printf("\n\n");
5113     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5114     printf("Explode(): This should never happen!\n");
5115     printf("\n\n");
5116
5117     GfxElement[x][y] = EL_EMPTY;
5118   }
5119 #endif
5120
5121 #endif
5122
5123   border_element = Store2[x][y];
5124   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5125     border_element = StorePlayer[x][y];
5126
5127   if (phase == element_info[border_element].ignition_delay ||
5128       phase == last_phase)
5129   {
5130     boolean border_explosion = FALSE;
5131
5132     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5133         !PLAYER_EXPLOSION_PROTECTED(x, y))
5134     {
5135       KillPlayerUnlessExplosionProtected(x, y);
5136       border_explosion = TRUE;
5137     }
5138     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5139     {
5140       Feld[x][y] = Store2[x][y];
5141       Store2[x][y] = 0;
5142       Bang(x, y);
5143       border_explosion = TRUE;
5144     }
5145     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5146     {
5147       AmoebeUmwandeln(x, y);
5148       Store2[x][y] = 0;
5149       border_explosion = TRUE;
5150     }
5151
5152     /* if an element just explodes due to another explosion (chain-reaction),
5153        do not immediately end the new explosion when it was the last frame of
5154        the explosion (as it would be done in the following "if"-statement!) */
5155     if (border_explosion && phase == last_phase)
5156       return;
5157   }
5158
5159   if (phase == last_phase)
5160   {
5161     int element;
5162
5163     element = Feld[x][y] = Store[x][y];
5164     Store[x][y] = Store2[x][y] = 0;
5165     GfxElement[x][y] = EL_UNDEFINED;
5166
5167     /* player can escape from explosions and might therefore be still alive */
5168     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5169         element <= EL_PLAYER_IS_EXPLODING_4)
5170     {
5171       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5172       int explosion_element = EL_PLAYER_1 + player_nr;
5173       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5174       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5175
5176       if (level.use_explosion_element[player_nr])
5177         explosion_element = level.explosion_element[player_nr];
5178
5179       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5180                     element_info[explosion_element].content.e[xx][yy]);
5181     }
5182
5183     /* restore probably existing indestructible background element */
5184     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5185       element = Feld[x][y] = Back[x][y];
5186     Back[x][y] = 0;
5187
5188     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5189     GfxDir[x][y] = MV_NONE;
5190     ChangeDelay[x][y] = 0;
5191     ChangePage[x][y] = -1;
5192
5193 #if USE_NEW_CUSTOM_VALUE
5194     CustomValue[x][y] = 0;
5195 #endif
5196
5197     InitField_WithBug2(x, y, FALSE);
5198
5199     DrawLevelField(x, y);
5200
5201     TestIfElementTouchesCustomElement(x, y);
5202
5203     if (GFX_CRUMBLED(element))
5204       DrawLevelFieldCrumbledSandNeighbours(x, y);
5205
5206     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5207       StorePlayer[x][y] = 0;
5208
5209     if (ELEM_IS_PLAYER(element))
5210       RelocatePlayer(x, y, element);
5211   }
5212   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5213   {
5214     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5215     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5216
5217     if (phase == delay)
5218       DrawLevelFieldCrumbledSand(x, y);
5219
5220     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5221     {
5222       DrawLevelElement(x, y, Back[x][y]);
5223       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5224     }
5225     else if (IS_WALKABLE_UNDER(Back[x][y]))
5226     {
5227       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5228       DrawLevelElementThruMask(x, y, Back[x][y]);
5229     }
5230     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5231       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5232   }
5233 }
5234
5235 void DynaExplode(int ex, int ey)
5236 {
5237   int i, j;
5238   int dynabomb_element = Feld[ex][ey];
5239   int dynabomb_size = 1;
5240   boolean dynabomb_xl = FALSE;
5241   struct PlayerInfo *player;
5242   static int xy[4][2] =
5243   {
5244     { 0, -1 },
5245     { -1, 0 },
5246     { +1, 0 },
5247     { 0, +1 }
5248   };
5249
5250   if (IS_ACTIVE_BOMB(dynabomb_element))
5251   {
5252     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5253     dynabomb_size = player->dynabomb_size;
5254     dynabomb_xl = player->dynabomb_xl;
5255     player->dynabombs_left++;
5256   }
5257
5258   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5259
5260   for (i = 0; i < NUM_DIRECTIONS; i++)
5261   {
5262     for (j = 1; j <= dynabomb_size; j++)
5263     {
5264       int x = ex + j * xy[i][0];
5265       int y = ey + j * xy[i][1];
5266       int element;
5267
5268       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5269         break;
5270
5271       element = Feld[x][y];
5272
5273       /* do not restart explosions of fields with active bombs */
5274       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5275         continue;
5276
5277       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5278
5279       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5280           !IS_DIGGABLE(element) && !dynabomb_xl)
5281         break;
5282     }
5283   }
5284 }
5285
5286 void Bang(int x, int y)
5287 {
5288   int element = MovingOrBlocked2Element(x, y);
5289   int explosion_type = EX_TYPE_NORMAL;
5290
5291   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5292   {
5293     struct PlayerInfo *player = PLAYERINFO(x, y);
5294
5295     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5296                             player->element_nr);
5297
5298     if (level.use_explosion_element[player->index_nr])
5299     {
5300       int explosion_element = level.explosion_element[player->index_nr];
5301
5302       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5303         explosion_type = EX_TYPE_CROSS;
5304       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5305         explosion_type = EX_TYPE_CENTER;
5306     }
5307   }
5308
5309   switch (element)
5310   {
5311     case EL_BUG:
5312     case EL_SPACESHIP:
5313     case EL_BD_BUTTERFLY:
5314     case EL_BD_FIREFLY:
5315     case EL_YAMYAM:
5316     case EL_DARK_YAMYAM:
5317     case EL_ROBOT:
5318     case EL_PACMAN:
5319     case EL_MOLE:
5320       RaiseScoreElement(element);
5321       break;
5322
5323     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5324     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5325     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5326     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5327     case EL_DYNABOMB_INCREASE_NUMBER:
5328     case EL_DYNABOMB_INCREASE_SIZE:
5329     case EL_DYNABOMB_INCREASE_POWER:
5330       explosion_type = EX_TYPE_DYNA;
5331       break;
5332
5333     case EL_DC_LANDMINE:
5334 #if 0
5335     case EL_EM_EXIT_OPEN:
5336     case EL_EM_STEEL_EXIT_OPEN:
5337 #endif
5338       explosion_type = EX_TYPE_CENTER;
5339       break;
5340
5341     case EL_PENGUIN:
5342     case EL_LAMP:
5343     case EL_LAMP_ACTIVE:
5344     case EL_AMOEBA_TO_DIAMOND:
5345       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5346         explosion_type = EX_TYPE_CENTER;
5347       break;
5348
5349     default:
5350       if (element_info[element].explosion_type == EXPLODES_CROSS)
5351         explosion_type = EX_TYPE_CROSS;
5352       else if (element_info[element].explosion_type == EXPLODES_1X1)
5353         explosion_type = EX_TYPE_CENTER;
5354       break;
5355   }
5356
5357   if (explosion_type == EX_TYPE_DYNA)
5358     DynaExplode(x, y);
5359   else
5360     Explode(x, y, EX_PHASE_START, explosion_type);
5361
5362   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5363 }
5364
5365 void SplashAcid(int x, int y)
5366 {
5367   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5368       (!IN_LEV_FIELD(x - 1, y - 2) ||
5369        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5370     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5371
5372   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5373       (!IN_LEV_FIELD(x + 1, y - 2) ||
5374        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5375     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5376
5377   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5378 }
5379
5380 static void InitBeltMovement()
5381 {
5382   static int belt_base_element[4] =
5383   {
5384     EL_CONVEYOR_BELT_1_LEFT,
5385     EL_CONVEYOR_BELT_2_LEFT,
5386     EL_CONVEYOR_BELT_3_LEFT,
5387     EL_CONVEYOR_BELT_4_LEFT
5388   };
5389   static int belt_base_active_element[4] =
5390   {
5391     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5392     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5393     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5394     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5395   };
5396
5397   int x, y, i, j;
5398
5399   /* set frame order for belt animation graphic according to belt direction */
5400   for (i = 0; i < NUM_BELTS; i++)
5401   {
5402     int belt_nr = i;
5403
5404     for (j = 0; j < NUM_BELT_PARTS; j++)
5405     {
5406       int element = belt_base_active_element[belt_nr] + j;
5407       int graphic = el2img(element);
5408
5409       if (game.belt_dir[i] == MV_LEFT)
5410         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5411       else
5412         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5413     }
5414   }
5415
5416   SCAN_PLAYFIELD(x, y)
5417   {
5418     int element = Feld[x][y];
5419
5420     for (i = 0; i < NUM_BELTS; i++)
5421     {
5422       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5423       {
5424         int e_belt_nr = getBeltNrFromBeltElement(element);
5425         int belt_nr = i;
5426
5427         if (e_belt_nr == belt_nr)
5428         {
5429           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5430
5431           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5432         }
5433       }
5434     }
5435   }
5436 }
5437
5438 static void ToggleBeltSwitch(int x, int y)
5439 {
5440   static int belt_base_element[4] =
5441   {
5442     EL_CONVEYOR_BELT_1_LEFT,
5443     EL_CONVEYOR_BELT_2_LEFT,
5444     EL_CONVEYOR_BELT_3_LEFT,
5445     EL_CONVEYOR_BELT_4_LEFT
5446   };
5447   static int belt_base_active_element[4] =
5448   {
5449     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5450     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5451     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5452     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5453   };
5454   static int belt_base_switch_element[4] =
5455   {
5456     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5457     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5458     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5459     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5460   };
5461   static int belt_move_dir[4] =
5462   {
5463     MV_LEFT,
5464     MV_NONE,
5465     MV_RIGHT,
5466     MV_NONE,
5467   };
5468
5469   int element = Feld[x][y];
5470   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5471   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5472   int belt_dir = belt_move_dir[belt_dir_nr];
5473   int xx, yy, i;
5474
5475   if (!IS_BELT_SWITCH(element))
5476     return;
5477
5478   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5479   game.belt_dir[belt_nr] = belt_dir;
5480
5481   if (belt_dir_nr == 3)
5482     belt_dir_nr = 1;
5483
5484   /* set frame order for belt animation graphic according to belt direction */
5485   for (i = 0; i < NUM_BELT_PARTS; i++)
5486   {
5487     int element = belt_base_active_element[belt_nr] + i;
5488     int graphic = el2img(element);
5489
5490     if (belt_dir == MV_LEFT)
5491       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5492     else
5493       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5494   }
5495
5496   SCAN_PLAYFIELD(xx, yy)
5497   {
5498     int element = Feld[xx][yy];
5499
5500     if (IS_BELT_SWITCH(element))
5501     {
5502       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5503
5504       if (e_belt_nr == belt_nr)
5505       {
5506         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5507         DrawLevelField(xx, yy);
5508       }
5509     }
5510     else if (IS_BELT(element) && belt_dir != MV_NONE)
5511     {
5512       int e_belt_nr = getBeltNrFromBeltElement(element);
5513
5514       if (e_belt_nr == belt_nr)
5515       {
5516         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5517
5518         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5519         DrawLevelField(xx, yy);
5520       }
5521     }
5522     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5523     {
5524       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5525
5526       if (e_belt_nr == belt_nr)
5527       {
5528         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5529
5530         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5531         DrawLevelField(xx, yy);
5532       }
5533     }
5534   }
5535 }
5536
5537 static void ToggleSwitchgateSwitch(int x, int y)
5538 {
5539   int xx, yy;
5540
5541   game.switchgate_pos = !game.switchgate_pos;
5542
5543   SCAN_PLAYFIELD(xx, yy)
5544   {
5545     int element = Feld[xx][yy];
5546
5547 #if !USE_BOTH_SWITCHGATE_SWITCHES
5548     if (element == EL_SWITCHGATE_SWITCH_UP ||
5549         element == EL_SWITCHGATE_SWITCH_DOWN)
5550     {
5551       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5552       DrawLevelField(xx, yy);
5553     }
5554     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5555              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5556     {
5557       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5558       DrawLevelField(xx, yy);
5559     }
5560 #else
5561     if (element == EL_SWITCHGATE_SWITCH_UP)
5562     {
5563       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5564       DrawLevelField(xx, yy);
5565     }
5566     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5567     {
5568       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5569       DrawLevelField(xx, yy);
5570     }
5571     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5572     {
5573       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5574       DrawLevelField(xx, yy);
5575     }
5576     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5577     {
5578       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5579       DrawLevelField(xx, yy);
5580     }
5581 #endif
5582     else if (element == EL_SWITCHGATE_OPEN ||
5583              element == EL_SWITCHGATE_OPENING)
5584     {
5585       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5586
5587       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5588     }
5589     else if (element == EL_SWITCHGATE_CLOSED ||
5590              element == EL_SWITCHGATE_CLOSING)
5591     {
5592       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5593
5594       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5595     }
5596   }
5597 }
5598
5599 static int getInvisibleActiveFromInvisibleElement(int element)
5600 {
5601   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5602           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5603           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5604           element);
5605 }
5606
5607 static int getInvisibleFromInvisibleActiveElement(int element)
5608 {
5609   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5610           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5611           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5612           element);
5613 }
5614
5615 static void RedrawAllLightSwitchesAndInvisibleElements()
5616 {
5617   int x, y;
5618
5619   SCAN_PLAYFIELD(x, y)
5620   {
5621     int element = Feld[x][y];
5622
5623     if (element == EL_LIGHT_SWITCH &&
5624         game.light_time_left > 0)
5625     {
5626       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5627       DrawLevelField(x, y);
5628     }
5629     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5630              game.light_time_left == 0)
5631     {
5632       Feld[x][y] = EL_LIGHT_SWITCH;
5633       DrawLevelField(x, y);
5634     }
5635     else if (element == EL_EMC_DRIPPER &&
5636              game.light_time_left > 0)
5637     {
5638       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5639       DrawLevelField(x, y);
5640     }
5641     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5642              game.light_time_left == 0)
5643     {
5644       Feld[x][y] = EL_EMC_DRIPPER;
5645       DrawLevelField(x, y);
5646     }
5647     else if (element == EL_INVISIBLE_STEELWALL ||
5648              element == EL_INVISIBLE_WALL ||
5649              element == EL_INVISIBLE_SAND)
5650     {
5651       if (game.light_time_left > 0)
5652         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5653
5654       DrawLevelField(x, y);
5655
5656       /* uncrumble neighbour fields, if needed */
5657       if (element == EL_INVISIBLE_SAND)
5658         DrawLevelFieldCrumbledSandNeighbours(x, y);
5659     }
5660     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5661              element == EL_INVISIBLE_WALL_ACTIVE ||
5662              element == EL_INVISIBLE_SAND_ACTIVE)
5663     {
5664       if (game.light_time_left == 0)
5665         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5666
5667       DrawLevelField(x, y);
5668
5669       /* re-crumble neighbour fields, if needed */
5670       if (element == EL_INVISIBLE_SAND)
5671         DrawLevelFieldCrumbledSandNeighbours(x, y);
5672     }
5673   }
5674 }
5675
5676 static void RedrawAllInvisibleElementsForLenses()
5677 {
5678   int x, y;
5679
5680   SCAN_PLAYFIELD(x, y)
5681   {
5682     int element = Feld[x][y];
5683
5684     if (element == EL_EMC_DRIPPER &&
5685         game.lenses_time_left > 0)
5686     {
5687       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5688       DrawLevelField(x, y);
5689     }
5690     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5691              game.lenses_time_left == 0)
5692     {
5693       Feld[x][y] = EL_EMC_DRIPPER;
5694       DrawLevelField(x, y);
5695     }
5696     else if (element == EL_INVISIBLE_STEELWALL ||
5697              element == EL_INVISIBLE_WALL ||
5698              element == EL_INVISIBLE_SAND)
5699     {
5700       if (game.lenses_time_left > 0)
5701         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5702
5703       DrawLevelField(x, y);
5704
5705       /* uncrumble neighbour fields, if needed */
5706       if (element == EL_INVISIBLE_SAND)
5707         DrawLevelFieldCrumbledSandNeighbours(x, y);
5708     }
5709     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5710              element == EL_INVISIBLE_WALL_ACTIVE ||
5711              element == EL_INVISIBLE_SAND_ACTIVE)
5712     {
5713       if (game.lenses_time_left == 0)
5714         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5715
5716       DrawLevelField(x, y);
5717
5718       /* re-crumble neighbour fields, if needed */
5719       if (element == EL_INVISIBLE_SAND)
5720         DrawLevelFieldCrumbledSandNeighbours(x, y);
5721     }
5722   }
5723 }
5724
5725 static void RedrawAllInvisibleElementsForMagnifier()
5726 {
5727   int x, y;
5728
5729   SCAN_PLAYFIELD(x, y)
5730   {
5731     int element = Feld[x][y];
5732
5733     if (element == EL_EMC_FAKE_GRASS &&
5734         game.magnify_time_left > 0)
5735     {
5736       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5737       DrawLevelField(x, y);
5738     }
5739     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5740              game.magnify_time_left == 0)
5741     {
5742       Feld[x][y] = EL_EMC_FAKE_GRASS;
5743       DrawLevelField(x, y);
5744     }
5745     else if (IS_GATE_GRAY(element) &&
5746              game.magnify_time_left > 0)
5747     {
5748       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5749                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5750                     IS_EM_GATE_GRAY(element) ?
5751                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5752                     IS_EMC_GATE_GRAY(element) ?
5753                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5754                     element);
5755       DrawLevelField(x, y);
5756     }
5757     else if (IS_GATE_GRAY_ACTIVE(element) &&
5758              game.magnify_time_left == 0)
5759     {
5760       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5761                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5762                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5763                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5764                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5765                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5766                     element);
5767       DrawLevelField(x, y);
5768     }
5769   }
5770 }
5771
5772 static void ToggleLightSwitch(int x, int y)
5773 {
5774   int element = Feld[x][y];
5775
5776   game.light_time_left =
5777     (element == EL_LIGHT_SWITCH ?
5778      level.time_light * FRAMES_PER_SECOND : 0);
5779
5780   RedrawAllLightSwitchesAndInvisibleElements();
5781 }
5782
5783 static void ActivateTimegateSwitch(int x, int y)
5784 {
5785   int xx, yy;
5786
5787   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5788
5789   SCAN_PLAYFIELD(xx, yy)
5790   {
5791     int element = Feld[xx][yy];
5792
5793     if (element == EL_TIMEGATE_CLOSED ||
5794         element == EL_TIMEGATE_CLOSING)
5795     {
5796       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5797       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5798     }
5799
5800     /*
5801     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5802     {
5803       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5804       DrawLevelField(xx, yy);
5805     }
5806     */
5807
5808   }
5809
5810 #if 1
5811   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5812                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5813 #else
5814   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5815 #endif
5816 }
5817
5818 void Impact(int x, int y)
5819 {
5820   boolean last_line = (y == lev_fieldy - 1);
5821   boolean object_hit = FALSE;
5822   boolean impact = (last_line || object_hit);
5823   int element = Feld[x][y];
5824   int smashed = EL_STEELWALL;
5825
5826   if (!last_line)       /* check if element below was hit */
5827   {
5828     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5829       return;
5830
5831     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5832                                          MovDir[x][y + 1] != MV_DOWN ||
5833                                          MovPos[x][y + 1] <= TILEY / 2));
5834
5835     /* do not smash moving elements that left the smashed field in time */
5836     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5837         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5838       object_hit = FALSE;
5839
5840 #if USE_QUICKSAND_IMPACT_BUGFIX
5841     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5842     {
5843       RemoveMovingField(x, y + 1);
5844       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5845       Feld[x][y + 2] = EL_ROCK;
5846       DrawLevelField(x, y + 2);
5847
5848       object_hit = TRUE;
5849     }
5850
5851     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5852     {
5853       RemoveMovingField(x, y + 1);
5854       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5855       Feld[x][y + 2] = EL_ROCK;
5856       DrawLevelField(x, y + 2);
5857
5858       object_hit = TRUE;
5859     }
5860 #endif
5861
5862     if (object_hit)
5863       smashed = MovingOrBlocked2Element(x, y + 1);
5864
5865     impact = (last_line || object_hit);
5866   }
5867
5868   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5869   {
5870     SplashAcid(x, y + 1);
5871     return;
5872   }
5873
5874   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5875   /* only reset graphic animation if graphic really changes after impact */
5876   if (impact &&
5877       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5878   {
5879     ResetGfxAnimation(x, y);
5880     DrawLevelField(x, y);
5881   }
5882
5883   if (impact && CAN_EXPLODE_IMPACT(element))
5884   {
5885     Bang(x, y);
5886     return;
5887   }
5888   else if (impact && element == EL_PEARL &&
5889            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5890   {
5891     ResetGfxAnimation(x, y);
5892
5893     Feld[x][y] = EL_PEARL_BREAKING;
5894     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5895     return;
5896   }
5897   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5898   {
5899     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5900
5901     return;
5902   }
5903
5904   if (impact && element == EL_AMOEBA_DROP)
5905   {
5906     if (object_hit && IS_PLAYER(x, y + 1))
5907       KillPlayerUnlessEnemyProtected(x, y + 1);
5908     else if (object_hit && smashed == EL_PENGUIN)
5909       Bang(x, y + 1);
5910     else
5911     {
5912       Feld[x][y] = EL_AMOEBA_GROWING;
5913       Store[x][y] = EL_AMOEBA_WET;
5914
5915       ResetRandomAnimationValue(x, y);
5916     }
5917     return;
5918   }
5919
5920   if (object_hit)               /* check which object was hit */
5921   {
5922     if ((CAN_PASS_MAGIC_WALL(element) && 
5923          (smashed == EL_MAGIC_WALL ||
5924           smashed == EL_BD_MAGIC_WALL)) ||
5925         (CAN_PASS_DC_MAGIC_WALL(element) &&
5926          smashed == EL_DC_MAGIC_WALL))
5927     {
5928       int xx, yy;
5929       int activated_magic_wall =
5930         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5931          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5932          EL_DC_MAGIC_WALL_ACTIVE);
5933
5934       /* activate magic wall / mill */
5935       SCAN_PLAYFIELD(xx, yy)
5936       {
5937         if (Feld[xx][yy] == smashed)
5938           Feld[xx][yy] = activated_magic_wall;
5939       }
5940
5941       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5942       game.magic_wall_active = TRUE;
5943
5944       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5945                             SND_MAGIC_WALL_ACTIVATING :
5946                             smashed == EL_BD_MAGIC_WALL ?
5947                             SND_BD_MAGIC_WALL_ACTIVATING :
5948                             SND_DC_MAGIC_WALL_ACTIVATING));
5949     }
5950
5951     if (IS_PLAYER(x, y + 1))
5952     {
5953       if (CAN_SMASH_PLAYER(element))
5954       {
5955         KillPlayerUnlessEnemyProtected(x, y + 1);
5956         return;
5957       }
5958     }
5959     else if (smashed == EL_PENGUIN)
5960     {
5961       if (CAN_SMASH_PLAYER(element))
5962       {
5963         Bang(x, y + 1);
5964         return;
5965       }
5966     }
5967     else if (element == EL_BD_DIAMOND)
5968     {
5969       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5970       {
5971         Bang(x, y + 1);
5972         return;
5973       }
5974     }
5975     else if (((element == EL_SP_INFOTRON ||
5976                element == EL_SP_ZONK) &&
5977               (smashed == EL_SP_SNIKSNAK ||
5978                smashed == EL_SP_ELECTRON ||
5979                smashed == EL_SP_DISK_ORANGE)) ||
5980              (element == EL_SP_INFOTRON &&
5981               smashed == EL_SP_DISK_YELLOW))
5982     {
5983       Bang(x, y + 1);
5984       return;
5985     }
5986     else if (CAN_SMASH_EVERYTHING(element))
5987     {
5988       if (IS_CLASSIC_ENEMY(smashed) ||
5989           CAN_EXPLODE_SMASHED(smashed))
5990       {
5991         Bang(x, y + 1);
5992         return;
5993       }
5994       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5995       {
5996         if (smashed == EL_LAMP ||
5997             smashed == EL_LAMP_ACTIVE)
5998         {
5999           Bang(x, y + 1);
6000           return;
6001         }
6002         else if (smashed == EL_NUT)
6003         {
6004           Feld[x][y + 1] = EL_NUT_BREAKING;
6005           PlayLevelSound(x, y, SND_NUT_BREAKING);
6006           RaiseScoreElement(EL_NUT);
6007           return;
6008         }
6009         else if (smashed == EL_PEARL)
6010         {
6011           ResetGfxAnimation(x, y);
6012
6013           Feld[x][y + 1] = EL_PEARL_BREAKING;
6014           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6015           return;
6016         }
6017         else if (smashed == EL_DIAMOND)
6018         {
6019           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6020           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6021           return;
6022         }
6023         else if (IS_BELT_SWITCH(smashed))
6024         {
6025           ToggleBeltSwitch(x, y + 1);
6026         }
6027         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6028                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6029                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6030                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6031         {
6032           ToggleSwitchgateSwitch(x, y + 1);
6033         }
6034         else if (smashed == EL_LIGHT_SWITCH ||
6035                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6036         {
6037           ToggleLightSwitch(x, y + 1);
6038         }
6039         else
6040         {
6041 #if 0
6042           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6043 #endif
6044
6045           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6046
6047           CheckElementChangeBySide(x, y + 1, smashed, element,
6048                                    CE_SWITCHED, CH_SIDE_TOP);
6049           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6050                                             CH_SIDE_TOP);
6051         }
6052       }
6053       else
6054       {
6055         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6056       }
6057     }
6058   }
6059
6060   /* play sound of magic wall / mill */
6061   if (!last_line &&
6062       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6063        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6064        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6065   {
6066     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6067       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6068     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6069       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6070     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6071       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6072
6073     return;
6074   }
6075
6076   /* play sound of object that hits the ground */
6077   if (last_line || object_hit)
6078     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6079 }
6080
6081 inline static void TurnRoundExt(int x, int y)
6082 {
6083   static struct
6084   {
6085     int dx, dy;
6086   } move_xy[] =
6087   {
6088     {  0,  0 },
6089     { -1,  0 },
6090     { +1,  0 },
6091     {  0,  0 },
6092     {  0, -1 },
6093     {  0,  0 }, { 0, 0 }, { 0, 0 },
6094     {  0, +1 }
6095   };
6096   static struct
6097   {
6098     int left, right, back;
6099   } turn[] =
6100   {
6101     { 0,        0,              0        },
6102     { MV_DOWN,  MV_UP,          MV_RIGHT },
6103     { MV_UP,    MV_DOWN,        MV_LEFT  },
6104     { 0,        0,              0        },
6105     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6106     { 0,        0,              0        },
6107     { 0,        0,              0        },
6108     { 0,        0,              0        },
6109     { MV_RIGHT, MV_LEFT,        MV_UP    }
6110   };
6111
6112   int element = Feld[x][y];
6113   int move_pattern = element_info[element].move_pattern;
6114
6115   int old_move_dir = MovDir[x][y];
6116   int left_dir  = turn[old_move_dir].left;
6117   int right_dir = turn[old_move_dir].right;
6118   int back_dir  = turn[old_move_dir].back;
6119
6120   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6121   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6122   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6123   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6124
6125   int left_x  = x + left_dx,  left_y  = y + left_dy;
6126   int right_x = x + right_dx, right_y = y + right_dy;
6127   int move_x  = x + move_dx,  move_y  = y + move_dy;
6128
6129   int xx, yy;
6130
6131   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6132   {
6133     TestIfBadThingTouchesOtherBadThing(x, y);
6134
6135     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6136       MovDir[x][y] = right_dir;
6137     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6138       MovDir[x][y] = left_dir;
6139
6140     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6141       MovDelay[x][y] = 9;
6142     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6143       MovDelay[x][y] = 1;
6144   }
6145   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6146   {
6147     TestIfBadThingTouchesOtherBadThing(x, y);
6148
6149     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6150       MovDir[x][y] = left_dir;
6151     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6152       MovDir[x][y] = right_dir;
6153
6154     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6155       MovDelay[x][y] = 9;
6156     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6157       MovDelay[x][y] = 1;
6158   }
6159   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6160   {
6161     TestIfBadThingTouchesOtherBadThing(x, y);
6162
6163     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6164       MovDir[x][y] = left_dir;
6165     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6166       MovDir[x][y] = right_dir;
6167
6168     if (MovDir[x][y] != old_move_dir)
6169       MovDelay[x][y] = 9;
6170   }
6171   else if (element == EL_YAMYAM)
6172   {
6173     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6174     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6175
6176     if (can_turn_left && can_turn_right)
6177       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6178     else if (can_turn_left)
6179       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6180     else if (can_turn_right)
6181       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6182     else
6183       MovDir[x][y] = back_dir;
6184
6185     MovDelay[x][y] = 16 + 16 * RND(3);
6186   }
6187   else if (element == EL_DARK_YAMYAM)
6188   {
6189     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6190                                                          left_x, left_y);
6191     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6192                                                          right_x, right_y);
6193
6194     if (can_turn_left && can_turn_right)
6195       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6196     else if (can_turn_left)
6197       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6198     else if (can_turn_right)
6199       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6200     else
6201       MovDir[x][y] = back_dir;
6202
6203     MovDelay[x][y] = 16 + 16 * RND(3);
6204   }
6205   else if (element == EL_PACMAN)
6206   {
6207     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6208     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6209
6210     if (can_turn_left && can_turn_right)
6211       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6212     else if (can_turn_left)
6213       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6214     else if (can_turn_right)
6215       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6216     else
6217       MovDir[x][y] = back_dir;
6218
6219     MovDelay[x][y] = 6 + RND(40);
6220   }
6221   else if (element == EL_PIG)
6222   {
6223     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6224     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6225     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6226     boolean should_turn_left, should_turn_right, should_move_on;
6227     int rnd_value = 24;
6228     int rnd = RND(rnd_value);
6229
6230     should_turn_left = (can_turn_left &&
6231                         (!can_move_on ||
6232                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6233                                                    y + back_dy + left_dy)));
6234     should_turn_right = (can_turn_right &&
6235                          (!can_move_on ||
6236                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6237                                                     y + back_dy + right_dy)));
6238     should_move_on = (can_move_on &&
6239                       (!can_turn_left ||
6240                        !can_turn_right ||
6241                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6242                                                  y + move_dy + left_dy) ||
6243                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6244                                                  y + move_dy + right_dy)));
6245
6246     if (should_turn_left || should_turn_right || should_move_on)
6247     {
6248       if (should_turn_left && should_turn_right && should_move_on)
6249         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6250                         rnd < 2 * rnd_value / 3 ? right_dir :
6251                         old_move_dir);
6252       else if (should_turn_left && should_turn_right)
6253         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6254       else if (should_turn_left && should_move_on)
6255         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6256       else if (should_turn_right && should_move_on)
6257         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6258       else if (should_turn_left)
6259         MovDir[x][y] = left_dir;
6260       else if (should_turn_right)
6261         MovDir[x][y] = right_dir;
6262       else if (should_move_on)
6263         MovDir[x][y] = old_move_dir;
6264     }
6265     else if (can_move_on && rnd > rnd_value / 8)
6266       MovDir[x][y] = old_move_dir;
6267     else if (can_turn_left && can_turn_right)
6268       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6269     else if (can_turn_left && rnd > rnd_value / 8)
6270       MovDir[x][y] = left_dir;
6271     else if (can_turn_right && rnd > rnd_value/8)
6272       MovDir[x][y] = right_dir;
6273     else
6274       MovDir[x][y] = back_dir;
6275
6276     xx = x + move_xy[MovDir[x][y]].dx;
6277     yy = y + move_xy[MovDir[x][y]].dy;
6278
6279     if (!IN_LEV_FIELD(xx, yy) ||
6280         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6281       MovDir[x][y] = old_move_dir;
6282
6283     MovDelay[x][y] = 0;
6284   }
6285   else if (element == EL_DRAGON)
6286   {
6287     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6288     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6289     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6290     int rnd_value = 24;
6291     int rnd = RND(rnd_value);
6292
6293     if (can_move_on && rnd > rnd_value / 8)
6294       MovDir[x][y] = old_move_dir;
6295     else if (can_turn_left && can_turn_right)
6296       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6297     else if (can_turn_left && rnd > rnd_value / 8)
6298       MovDir[x][y] = left_dir;
6299     else if (can_turn_right && rnd > rnd_value / 8)
6300       MovDir[x][y] = right_dir;
6301     else
6302       MovDir[x][y] = back_dir;
6303
6304     xx = x + move_xy[MovDir[x][y]].dx;
6305     yy = y + move_xy[MovDir[x][y]].dy;
6306
6307     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6308       MovDir[x][y] = old_move_dir;
6309
6310     MovDelay[x][y] = 0;
6311   }
6312   else if (element == EL_MOLE)
6313   {
6314     boolean can_move_on =
6315       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6316                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6317                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6318     if (!can_move_on)
6319     {
6320       boolean can_turn_left =
6321         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6322                               IS_AMOEBOID(Feld[left_x][left_y])));
6323
6324       boolean can_turn_right =
6325         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6326                               IS_AMOEBOID(Feld[right_x][right_y])));
6327
6328       if (can_turn_left && can_turn_right)
6329         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6330       else if (can_turn_left)
6331         MovDir[x][y] = left_dir;
6332       else
6333         MovDir[x][y] = right_dir;
6334     }
6335
6336     if (MovDir[x][y] != old_move_dir)
6337       MovDelay[x][y] = 9;
6338   }
6339   else if (element == EL_BALLOON)
6340   {
6341     MovDir[x][y] = game.wind_direction;
6342     MovDelay[x][y] = 0;
6343   }
6344   else if (element == EL_SPRING)
6345   {
6346 #if USE_NEW_SPRING_BUMPER
6347     if (MovDir[x][y] & MV_HORIZONTAL)
6348     {
6349       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6350           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6351       {
6352         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6353         ResetGfxAnimation(move_x, move_y);
6354         DrawLevelField(move_x, move_y);
6355
6356         MovDir[x][y] = back_dir;
6357       }
6358       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6359                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6360         MovDir[x][y] = MV_NONE;
6361     }
6362 #else
6363     if (MovDir[x][y] & MV_HORIZONTAL &&
6364         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6365          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6366       MovDir[x][y] = MV_NONE;
6367 #endif
6368
6369     MovDelay[x][y] = 0;
6370   }
6371   else if (element == EL_ROBOT ||
6372            element == EL_SATELLITE ||
6373            element == EL_PENGUIN ||
6374            element == EL_EMC_ANDROID)
6375   {
6376     int attr_x = -1, attr_y = -1;
6377
6378     if (AllPlayersGone)
6379     {
6380       attr_x = ExitX;
6381       attr_y = ExitY;
6382     }
6383     else
6384     {
6385       int i;
6386
6387       for (i = 0; i < MAX_PLAYERS; i++)
6388       {
6389         struct PlayerInfo *player = &stored_player[i];
6390         int jx = player->jx, jy = player->jy;
6391
6392         if (!player->active)
6393           continue;
6394
6395         if (attr_x == -1 ||
6396             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6397         {
6398           attr_x = jx;
6399           attr_y = jy;
6400         }
6401       }
6402     }
6403
6404     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6405         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6406          game.engine_version < VERSION_IDENT(3,1,0,0)))
6407     {
6408       attr_x = ZX;
6409       attr_y = ZY;
6410     }
6411
6412     if (element == EL_PENGUIN)
6413     {
6414       int i;
6415       static int xy[4][2] =
6416       {
6417         { 0, -1 },
6418         { -1, 0 },
6419         { +1, 0 },
6420         { 0, +1 }
6421       };
6422
6423       for (i = 0; i < NUM_DIRECTIONS; i++)
6424       {
6425         int ex = x + xy[i][0];
6426         int ey = y + xy[i][1];
6427
6428         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6429                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6430                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6431                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6432         {
6433           attr_x = ex;
6434           attr_y = ey;
6435           break;
6436         }
6437       }
6438     }
6439
6440     MovDir[x][y] = MV_NONE;
6441     if (attr_x < x)
6442       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6443     else if (attr_x > x)
6444       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6445     if (attr_y < y)
6446       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6447     else if (attr_y > y)
6448       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6449
6450     if (element == EL_ROBOT)
6451     {
6452       int newx, newy;
6453
6454       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6455         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6456       Moving2Blocked(x, y, &newx, &newy);
6457
6458       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6459         MovDelay[x][y] = 8 + 8 * !RND(3);
6460       else
6461         MovDelay[x][y] = 16;
6462     }
6463     else if (element == EL_PENGUIN)
6464     {
6465       int newx, newy;
6466
6467       MovDelay[x][y] = 1;
6468
6469       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6470       {
6471         boolean first_horiz = RND(2);
6472         int new_move_dir = MovDir[x][y];
6473
6474         MovDir[x][y] =
6475           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6476         Moving2Blocked(x, y, &newx, &newy);
6477
6478         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6479           return;
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] = old_move_dir;
6489         return;
6490       }
6491     }
6492     else if (element == EL_SATELLITE)
6493     {
6494       int newx, newy;
6495
6496       MovDelay[x][y] = 1;
6497
6498       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6499       {
6500         boolean first_horiz = RND(2);
6501         int new_move_dir = MovDir[x][y];
6502
6503         MovDir[x][y] =
6504           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6505         Moving2Blocked(x, y, &newx, &newy);
6506
6507         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6508           return;
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] = old_move_dir;
6518         return;
6519       }
6520     }
6521     else if (element == EL_EMC_ANDROID)
6522     {
6523       static int check_pos[16] =
6524       {
6525         -1,             /*  0 => (invalid)          */
6526         7,              /*  1 => MV_LEFT            */
6527         3,              /*  2 => MV_RIGHT           */
6528         -1,             /*  3 => (invalid)          */
6529         1,              /*  4 =>            MV_UP   */
6530         0,              /*  5 => MV_LEFT  | MV_UP   */
6531         2,              /*  6 => MV_RIGHT | MV_UP   */
6532         -1,             /*  7 => (invalid)          */
6533         5,              /*  8 =>            MV_DOWN */
6534         6,              /*  9 => MV_LEFT  | MV_DOWN */
6535         4,              /* 10 => MV_RIGHT | MV_DOWN */
6536         -1,             /* 11 => (invalid)          */
6537         -1,             /* 12 => (invalid)          */
6538         -1,             /* 13 => (invalid)          */
6539         -1,             /* 14 => (invalid)          */
6540         -1,             /* 15 => (invalid)          */
6541       };
6542       static struct
6543       {
6544         int dx, dy;
6545         int dir;
6546       } check_xy[8] =
6547       {
6548         { -1, -1,       MV_LEFT  | MV_UP   },
6549         {  0, -1,                  MV_UP   },
6550         { +1, -1,       MV_RIGHT | MV_UP   },
6551         { +1,  0,       MV_RIGHT           },
6552         { +1, +1,       MV_RIGHT | MV_DOWN },
6553         {  0, +1,                  MV_DOWN },
6554         { -1, +1,       MV_LEFT  | MV_DOWN },
6555         { -1,  0,       MV_LEFT            },
6556       };
6557       int start_pos, check_order;
6558       boolean can_clone = FALSE;
6559       int i;
6560
6561       /* check if there is any free field around current position */
6562       for (i = 0; i < 8; i++)
6563       {
6564         int newx = x + check_xy[i].dx;
6565         int newy = y + check_xy[i].dy;
6566
6567         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6568         {
6569           can_clone = TRUE;
6570
6571           break;
6572         }
6573       }
6574
6575       if (can_clone)            /* randomly find an element to clone */
6576       {
6577         can_clone = FALSE;
6578
6579         start_pos = check_pos[RND(8)];
6580         check_order = (RND(2) ? -1 : +1);
6581
6582         for (i = 0; i < 8; i++)
6583         {
6584           int pos_raw = start_pos + i * check_order;
6585           int pos = (pos_raw + 8) % 8;
6586           int newx = x + check_xy[pos].dx;
6587           int newy = y + check_xy[pos].dy;
6588
6589           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6590           {
6591             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6592             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6593
6594             Store[x][y] = Feld[newx][newy];
6595
6596             can_clone = TRUE;
6597
6598             break;
6599           }
6600         }
6601       }
6602
6603       if (can_clone)            /* randomly find a direction to move */
6604       {
6605         can_clone = FALSE;
6606
6607         start_pos = check_pos[RND(8)];
6608         check_order = (RND(2) ? -1 : +1);
6609
6610         for (i = 0; i < 8; i++)
6611         {
6612           int pos_raw = start_pos + i * check_order;
6613           int pos = (pos_raw + 8) % 8;
6614           int newx = x + check_xy[pos].dx;
6615           int newy = y + check_xy[pos].dy;
6616           int new_move_dir = check_xy[pos].dir;
6617
6618           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6619           {
6620             MovDir[x][y] = new_move_dir;
6621             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6622
6623             can_clone = TRUE;
6624
6625             break;
6626           }
6627         }
6628       }
6629
6630       if (can_clone)            /* cloning and moving successful */
6631         return;
6632
6633       /* cannot clone -- try to move towards player */
6634
6635       start_pos = check_pos[MovDir[x][y] & 0x0f];
6636       check_order = (RND(2) ? -1 : +1);
6637
6638       for (i = 0; i < 3; i++)
6639       {
6640         /* first check start_pos, then previous/next or (next/previous) pos */
6641         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6642         int pos = (pos_raw + 8) % 8;
6643         int newx = x + check_xy[pos].dx;
6644         int newy = y + check_xy[pos].dy;
6645         int new_move_dir = check_xy[pos].dir;
6646
6647         if (IS_PLAYER(newx, newy))
6648           break;
6649
6650         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6651         {
6652           MovDir[x][y] = new_move_dir;
6653           MovDelay[x][y] = level.android_move_time * 8 + 1;
6654
6655           break;
6656         }
6657       }
6658     }
6659   }
6660   else if (move_pattern == MV_TURNING_LEFT ||
6661            move_pattern == MV_TURNING_RIGHT ||
6662            move_pattern == MV_TURNING_LEFT_RIGHT ||
6663            move_pattern == MV_TURNING_RIGHT_LEFT ||
6664            move_pattern == MV_TURNING_RANDOM ||
6665            move_pattern == MV_ALL_DIRECTIONS)
6666   {
6667     boolean can_turn_left =
6668       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6669     boolean can_turn_right =
6670       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6671
6672     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6673       return;
6674
6675     if (move_pattern == MV_TURNING_LEFT)
6676       MovDir[x][y] = left_dir;
6677     else if (move_pattern == MV_TURNING_RIGHT)
6678       MovDir[x][y] = right_dir;
6679     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6680       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6681     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6682       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6683     else if (move_pattern == MV_TURNING_RANDOM)
6684       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6685                       can_turn_right && !can_turn_left ? right_dir :
6686                       RND(2) ? left_dir : right_dir);
6687     else if (can_turn_left && can_turn_right)
6688       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6689     else if (can_turn_left)
6690       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6691     else if (can_turn_right)
6692       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6693     else
6694       MovDir[x][y] = back_dir;
6695
6696     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6697   }
6698   else if (move_pattern == MV_HORIZONTAL ||
6699            move_pattern == MV_VERTICAL)
6700   {
6701     if (move_pattern & old_move_dir)
6702       MovDir[x][y] = back_dir;
6703     else if (move_pattern == MV_HORIZONTAL)
6704       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6705     else if (move_pattern == MV_VERTICAL)
6706       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6707
6708     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6709   }
6710   else if (move_pattern & MV_ANY_DIRECTION)
6711   {
6712     MovDir[x][y] = move_pattern;
6713     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6714   }
6715   else if (move_pattern & MV_WIND_DIRECTION)
6716   {
6717     MovDir[x][y] = game.wind_direction;
6718     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6719   }
6720   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6721   {
6722     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6723       MovDir[x][y] = left_dir;
6724     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6725       MovDir[x][y] = right_dir;
6726
6727     if (MovDir[x][y] != old_move_dir)
6728       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6729   }
6730   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6731   {
6732     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6733       MovDir[x][y] = right_dir;
6734     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6735       MovDir[x][y] = left_dir;
6736
6737     if (MovDir[x][y] != old_move_dir)
6738       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6739   }
6740   else if (move_pattern == MV_TOWARDS_PLAYER ||
6741            move_pattern == MV_AWAY_FROM_PLAYER)
6742   {
6743     int attr_x = -1, attr_y = -1;
6744     int newx, newy;
6745     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6746
6747     if (AllPlayersGone)
6748     {
6749       attr_x = ExitX;
6750       attr_y = ExitY;
6751     }
6752     else
6753     {
6754       int i;
6755
6756       for (i = 0; i < MAX_PLAYERS; i++)
6757       {
6758         struct PlayerInfo *player = &stored_player[i];
6759         int jx = player->jx, jy = player->jy;
6760
6761         if (!player->active)
6762           continue;
6763
6764         if (attr_x == -1 ||
6765             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6766         {
6767           attr_x = jx;
6768           attr_y = jy;
6769         }
6770       }
6771     }
6772
6773     MovDir[x][y] = MV_NONE;
6774     if (attr_x < x)
6775       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6776     else if (attr_x > x)
6777       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6778     if (attr_y < y)
6779       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6780     else if (attr_y > y)
6781       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6782
6783     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6784
6785     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6786     {
6787       boolean first_horiz = RND(2);
6788       int new_move_dir = MovDir[x][y];
6789
6790       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6791       {
6792         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6793         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6794
6795         return;
6796       }
6797
6798       MovDir[x][y] =
6799         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6800       Moving2Blocked(x, y, &newx, &newy);
6801
6802       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6803         return;
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] = old_move_dir;
6813     }
6814   }
6815   else if (move_pattern == MV_WHEN_PUSHED ||
6816            move_pattern == MV_WHEN_DROPPED)
6817   {
6818     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6819       MovDir[x][y] = MV_NONE;
6820
6821     MovDelay[x][y] = 0;
6822   }
6823   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6824   {
6825     static int test_xy[7][2] =
6826     {
6827       { 0, -1 },
6828       { -1, 0 },
6829       { +1, 0 },
6830       { 0, +1 },
6831       { 0, -1 },
6832       { -1, 0 },
6833       { +1, 0 },
6834     };
6835     static int test_dir[7] =
6836     {
6837       MV_UP,
6838       MV_LEFT,
6839       MV_RIGHT,
6840       MV_DOWN,
6841       MV_UP,
6842       MV_LEFT,
6843       MV_RIGHT,
6844     };
6845     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6846     int move_preference = -1000000;     /* start with very low preference */
6847     int new_move_dir = MV_NONE;
6848     int start_test = RND(4);
6849     int i;
6850
6851     for (i = 0; i < NUM_DIRECTIONS; i++)
6852     {
6853       int move_dir = test_dir[start_test + i];
6854       int move_dir_preference;
6855
6856       xx = x + test_xy[start_test + i][0];
6857       yy = y + test_xy[start_test + i][1];
6858
6859       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6860           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6861       {
6862         new_move_dir = move_dir;
6863
6864         break;
6865       }
6866
6867       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6868         continue;
6869
6870       move_dir_preference = -1 * RunnerVisit[xx][yy];
6871       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6872         move_dir_preference = PlayerVisit[xx][yy];
6873
6874       if (move_dir_preference > move_preference)
6875       {
6876         /* prefer field that has not been visited for the longest time */
6877         move_preference = move_dir_preference;
6878         new_move_dir = move_dir;
6879       }
6880       else if (move_dir_preference == move_preference &&
6881                move_dir == old_move_dir)
6882       {
6883         /* prefer last direction when all directions are preferred equally */
6884         move_preference = move_dir_preference;
6885         new_move_dir = move_dir;
6886       }
6887     }
6888
6889     MovDir[x][y] = new_move_dir;
6890     if (old_move_dir != new_move_dir)
6891       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6892   }
6893 }
6894
6895 static void TurnRound(int x, int y)
6896 {
6897   int direction = MovDir[x][y];
6898
6899   TurnRoundExt(x, y);
6900
6901   GfxDir[x][y] = MovDir[x][y];
6902
6903   if (direction != MovDir[x][y])
6904     GfxFrame[x][y] = 0;
6905
6906   if (MovDelay[x][y])
6907     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6908
6909   ResetGfxFrame(x, y, FALSE);
6910 }
6911
6912 static boolean JustBeingPushed(int x, int y)
6913 {
6914   int i;
6915
6916   for (i = 0; i < MAX_PLAYERS; i++)
6917   {
6918     struct PlayerInfo *player = &stored_player[i];
6919
6920     if (player->active && player->is_pushing && player->MovPos)
6921     {
6922       int next_jx = player->jx + (player->jx - player->last_jx);
6923       int next_jy = player->jy + (player->jy - player->last_jy);
6924
6925       if (x == next_jx && y == next_jy)
6926         return TRUE;
6927     }
6928   }
6929
6930   return FALSE;
6931 }
6932
6933 void StartMoving(int x, int y)
6934 {
6935   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6936   int element = Feld[x][y];
6937
6938   if (Stop[x][y])
6939     return;
6940
6941   if (MovDelay[x][y] == 0)
6942     GfxAction[x][y] = ACTION_DEFAULT;
6943
6944   if (CAN_FALL(element) && y < lev_fieldy - 1)
6945   {
6946     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6947         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6948       if (JustBeingPushed(x, y))
6949         return;
6950
6951     if (element == EL_QUICKSAND_FULL)
6952     {
6953       if (IS_FREE(x, y + 1))
6954       {
6955         InitMovingField(x, y, MV_DOWN);
6956         started_moving = TRUE;
6957
6958         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6959 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6960         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6961           Store[x][y] = EL_ROCK;
6962 #else
6963         Store[x][y] = EL_ROCK;
6964 #endif
6965
6966         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6967       }
6968       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6969       {
6970         if (!MovDelay[x][y])
6971           MovDelay[x][y] = TILEY + 1;
6972
6973         if (MovDelay[x][y])
6974         {
6975           MovDelay[x][y]--;
6976           if (MovDelay[x][y])
6977             return;
6978         }
6979
6980         Feld[x][y] = EL_QUICKSAND_EMPTY;
6981         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6982         Store[x][y + 1] = Store[x][y];
6983         Store[x][y] = 0;
6984
6985         PlayLevelSoundAction(x, y, ACTION_FILLING);
6986       }
6987     }
6988     else if (element == EL_QUICKSAND_FAST_FULL)
6989     {
6990       if (IS_FREE(x, y + 1))
6991       {
6992         InitMovingField(x, y, MV_DOWN);
6993         started_moving = TRUE;
6994
6995         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6996 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6997         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6998           Store[x][y] = EL_ROCK;
6999 #else
7000         Store[x][y] = EL_ROCK;
7001 #endif
7002
7003         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7004       }
7005       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7006       {
7007         if (!MovDelay[x][y])
7008           MovDelay[x][y] = TILEY + 1;
7009
7010         if (MovDelay[x][y])
7011         {
7012           MovDelay[x][y]--;
7013           if (MovDelay[x][y])
7014             return;
7015         }
7016
7017         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7018         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7019         Store[x][y + 1] = Store[x][y];
7020         Store[x][y] = 0;
7021
7022         PlayLevelSoundAction(x, y, ACTION_FILLING);
7023       }
7024     }
7025     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7026              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7027     {
7028       InitMovingField(x, y, MV_DOWN);
7029       started_moving = TRUE;
7030
7031       Feld[x][y] = EL_QUICKSAND_FILLING;
7032       Store[x][y] = element;
7033
7034       PlayLevelSoundAction(x, y, ACTION_FILLING);
7035     }
7036     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7037              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7038     {
7039       InitMovingField(x, y, MV_DOWN);
7040       started_moving = TRUE;
7041
7042       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7043       Store[x][y] = element;
7044
7045       PlayLevelSoundAction(x, y, ACTION_FILLING);
7046     }
7047     else if (element == EL_MAGIC_WALL_FULL)
7048     {
7049       if (IS_FREE(x, y + 1))
7050       {
7051         InitMovingField(x, y, MV_DOWN);
7052         started_moving = TRUE;
7053
7054         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7055         Store[x][y] = EL_CHANGED(Store[x][y]);
7056       }
7057       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7058       {
7059         if (!MovDelay[x][y])
7060           MovDelay[x][y] = TILEY/4 + 1;
7061
7062         if (MovDelay[x][y])
7063         {
7064           MovDelay[x][y]--;
7065           if (MovDelay[x][y])
7066             return;
7067         }
7068
7069         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7070         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7071         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7072         Store[x][y] = 0;
7073       }
7074     }
7075     else if (element == EL_BD_MAGIC_WALL_FULL)
7076     {
7077       if (IS_FREE(x, y + 1))
7078       {
7079         InitMovingField(x, y, MV_DOWN);
7080         started_moving = TRUE;
7081
7082         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7083         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7084       }
7085       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7086       {
7087         if (!MovDelay[x][y])
7088           MovDelay[x][y] = TILEY/4 + 1;
7089
7090         if (MovDelay[x][y])
7091         {
7092           MovDelay[x][y]--;
7093           if (MovDelay[x][y])
7094             return;
7095         }
7096
7097         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7098         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7099         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7100         Store[x][y] = 0;
7101       }
7102     }
7103     else if (element == EL_DC_MAGIC_WALL_FULL)
7104     {
7105       if (IS_FREE(x, y + 1))
7106       {
7107         InitMovingField(x, y, MV_DOWN);
7108         started_moving = TRUE;
7109
7110         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7111         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7112       }
7113       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7114       {
7115         if (!MovDelay[x][y])
7116           MovDelay[x][y] = TILEY/4 + 1;
7117
7118         if (MovDelay[x][y])
7119         {
7120           MovDelay[x][y]--;
7121           if (MovDelay[x][y])
7122             return;
7123         }
7124
7125         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7126         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7127         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7128         Store[x][y] = 0;
7129       }
7130     }
7131     else if ((CAN_PASS_MAGIC_WALL(element) &&
7132               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7133                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7134              (CAN_PASS_DC_MAGIC_WALL(element) &&
7135               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7136
7137     {
7138       InitMovingField(x, y, MV_DOWN);
7139       started_moving = TRUE;
7140
7141       Feld[x][y] =
7142         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7143          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7144          EL_DC_MAGIC_WALL_FILLING);
7145       Store[x][y] = element;
7146     }
7147     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7148     {
7149       SplashAcid(x, y + 1);
7150
7151       InitMovingField(x, y, MV_DOWN);
7152       started_moving = TRUE;
7153
7154       Store[x][y] = EL_ACID;
7155     }
7156     else if (
7157 #if USE_FIX_IMPACT_COLLISION
7158              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7159               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7160 #else
7161              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7162               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7163 #endif
7164              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7165               CAN_FALL(element) && WasJustFalling[x][y] &&
7166               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7167
7168              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7169               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7170               (Feld[x][y + 1] == EL_BLOCKED)))
7171     {
7172       /* this is needed for a special case not covered by calling "Impact()"
7173          from "ContinueMoving()": if an element moves to a tile directly below
7174          another element which was just falling on that tile (which was empty
7175          in the previous frame), the falling element above would just stop
7176          instead of smashing the element below (in previous version, the above
7177          element was just checked for "moving" instead of "falling", resulting
7178          in incorrect smashes caused by horizontal movement of the above
7179          element; also, the case of the player being the element to smash was
7180          simply not covered here... :-/ ) */
7181
7182       CheckCollision[x][y] = 0;
7183       CheckImpact[x][y] = 0;
7184
7185       Impact(x, y);
7186     }
7187     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7188     {
7189       if (MovDir[x][y] == MV_NONE)
7190       {
7191         InitMovingField(x, y, MV_DOWN);
7192         started_moving = TRUE;
7193       }
7194     }
7195     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7196     {
7197       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7198         MovDir[x][y] = MV_DOWN;
7199
7200       InitMovingField(x, y, MV_DOWN);
7201       started_moving = TRUE;
7202     }
7203     else if (element == EL_AMOEBA_DROP)
7204     {
7205       Feld[x][y] = EL_AMOEBA_GROWING;
7206       Store[x][y] = EL_AMOEBA_WET;
7207     }
7208     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7209               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7210              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7211              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7212     {
7213       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7214                                 (IS_FREE(x - 1, y + 1) ||
7215                                  Feld[x - 1][y + 1] == EL_ACID));
7216       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7217                                 (IS_FREE(x + 1, y + 1) ||
7218                                  Feld[x + 1][y + 1] == EL_ACID));
7219       boolean can_fall_any  = (can_fall_left || can_fall_right);
7220       boolean can_fall_both = (can_fall_left && can_fall_right);
7221       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7222
7223 #if USE_NEW_ALL_SLIPPERY
7224       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7225       {
7226         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7227           can_fall_right = FALSE;
7228         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7229           can_fall_left = FALSE;
7230         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7231           can_fall_right = FALSE;
7232         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7233           can_fall_left = FALSE;
7234
7235         can_fall_any  = (can_fall_left || can_fall_right);
7236         can_fall_both = FALSE;
7237       }
7238 #else
7239       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7240       {
7241         if (slippery_type == SLIPPERY_ONLY_LEFT)
7242           can_fall_right = FALSE;
7243         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7244           can_fall_left = FALSE;
7245         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7246           can_fall_right = FALSE;
7247         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7248           can_fall_left = FALSE;
7249
7250         can_fall_any  = (can_fall_left || can_fall_right);
7251         can_fall_both = (can_fall_left && can_fall_right);
7252       }
7253 #endif
7254
7255 #if USE_NEW_ALL_SLIPPERY
7256 #else
7257 #if USE_NEW_SP_SLIPPERY
7258       /* !!! better use the same properties as for custom elements here !!! */
7259       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7260                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7261       {
7262         can_fall_right = FALSE;         /* slip down on left side */
7263         can_fall_both = FALSE;
7264       }
7265 #endif
7266 #endif
7267
7268 #if USE_NEW_ALL_SLIPPERY
7269       if (can_fall_both)
7270       {
7271         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7272           can_fall_right = FALSE;       /* slip down on left side */
7273         else
7274           can_fall_left = !(can_fall_right = RND(2));
7275
7276         can_fall_both = FALSE;
7277       }
7278 #else
7279       if (can_fall_both)
7280       {
7281         if (game.emulation == EMU_BOULDERDASH ||
7282             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7283           can_fall_right = FALSE;       /* slip down on left side */
7284         else
7285           can_fall_left = !(can_fall_right = RND(2));
7286
7287         can_fall_both = FALSE;
7288       }
7289 #endif
7290
7291       if (can_fall_any)
7292       {
7293         /* if not determined otherwise, prefer left side for slipping down */
7294         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7295         started_moving = TRUE;
7296       }
7297     }
7298 #if 0
7299     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7300 #else
7301     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7302 #endif
7303     {
7304       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7305       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7306       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7307       int belt_dir = game.belt_dir[belt_nr];
7308
7309       if ((belt_dir == MV_LEFT  && left_is_free) ||
7310           (belt_dir == MV_RIGHT && right_is_free))
7311       {
7312         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7313
7314         InitMovingField(x, y, belt_dir);
7315         started_moving = TRUE;
7316
7317         Pushed[x][y] = TRUE;
7318         Pushed[nextx][y] = TRUE;
7319
7320         GfxAction[x][y] = ACTION_DEFAULT;
7321       }
7322       else
7323       {
7324         MovDir[x][y] = 0;       /* if element was moving, stop it */
7325       }
7326     }
7327   }
7328
7329   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7330 #if 0
7331   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7332 #else
7333   if (CAN_MOVE(element) && !started_moving)
7334 #endif
7335   {
7336     int move_pattern = element_info[element].move_pattern;
7337     int newx, newy;
7338
7339 #if 0
7340 #if DEBUG
7341     if (MovDir[x][y] == MV_NONE)
7342     {
7343       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7344              x, y, element, element_info[element].token_name);
7345       printf("StartMoving(): This should never happen!\n");
7346     }
7347 #endif
7348 #endif
7349
7350     Moving2Blocked(x, y, &newx, &newy);
7351
7352     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7353       return;
7354
7355     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7356         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7357     {
7358       WasJustMoving[x][y] = 0;
7359       CheckCollision[x][y] = 0;
7360
7361       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7362
7363       if (Feld[x][y] != element)        /* element has changed */
7364         return;
7365     }
7366
7367     if (!MovDelay[x][y])        /* start new movement phase */
7368     {
7369       /* all objects that can change their move direction after each step
7370          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7371
7372       if (element != EL_YAMYAM &&
7373           element != EL_DARK_YAMYAM &&
7374           element != EL_PACMAN &&
7375           !(move_pattern & MV_ANY_DIRECTION) &&
7376           move_pattern != MV_TURNING_LEFT &&
7377           move_pattern != MV_TURNING_RIGHT &&
7378           move_pattern != MV_TURNING_LEFT_RIGHT &&
7379           move_pattern != MV_TURNING_RIGHT_LEFT &&
7380           move_pattern != MV_TURNING_RANDOM)
7381       {
7382         TurnRound(x, y);
7383
7384         if (MovDelay[x][y] && (element == EL_BUG ||
7385                                element == EL_SPACESHIP ||
7386                                element == EL_SP_SNIKSNAK ||
7387                                element == EL_SP_ELECTRON ||
7388                                element == EL_MOLE))
7389           DrawLevelField(x, y);
7390       }
7391     }
7392
7393     if (MovDelay[x][y])         /* wait some time before next movement */
7394     {
7395       MovDelay[x][y]--;
7396
7397       if (element == EL_ROBOT ||
7398           element == EL_YAMYAM ||
7399           element == EL_DARK_YAMYAM)
7400       {
7401         DrawLevelElementAnimationIfNeeded(x, y, element);
7402         PlayLevelSoundAction(x, y, ACTION_WAITING);
7403       }
7404       else if (element == EL_SP_ELECTRON)
7405         DrawLevelElementAnimationIfNeeded(x, y, element);
7406       else if (element == EL_DRAGON)
7407       {
7408         int i;
7409         int dir = MovDir[x][y];
7410         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7411         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7412         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7413                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7414                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7415                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7416         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7417
7418         GfxAction[x][y] = ACTION_ATTACKING;
7419
7420         if (IS_PLAYER(x, y))
7421           DrawPlayerField(x, y);
7422         else
7423           DrawLevelField(x, y);
7424
7425         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7426
7427         for (i = 1; i <= 3; i++)
7428         {
7429           int xx = x + i * dx;
7430           int yy = y + i * dy;
7431           int sx = SCREENX(xx);
7432           int sy = SCREENY(yy);
7433           int flame_graphic = graphic + (i - 1);
7434
7435           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7436             break;
7437
7438           if (MovDelay[x][y])
7439           {
7440             int flamed = MovingOrBlocked2Element(xx, yy);
7441
7442             /* !!! */
7443 #if 0
7444             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7445               Bang(xx, yy);
7446             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7447               RemoveMovingField(xx, yy);
7448             else
7449               RemoveField(xx, yy);
7450 #else
7451             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7452               Bang(xx, yy);
7453             else
7454               RemoveMovingField(xx, yy);
7455 #endif
7456
7457             ChangeDelay[xx][yy] = 0;
7458
7459             Feld[xx][yy] = EL_FLAMES;
7460
7461             if (IN_SCR_FIELD(sx, sy))
7462             {
7463               DrawLevelFieldCrumbledSand(xx, yy);
7464               DrawGraphic(sx, sy, flame_graphic, frame);
7465             }
7466           }
7467           else
7468           {
7469             if (Feld[xx][yy] == EL_FLAMES)
7470               Feld[xx][yy] = EL_EMPTY;
7471             DrawLevelField(xx, yy);
7472           }
7473         }
7474       }
7475
7476       if (MovDelay[x][y])       /* element still has to wait some time */
7477       {
7478         PlayLevelSoundAction(x, y, ACTION_WAITING);
7479
7480         return;
7481       }
7482     }
7483
7484     /* now make next step */
7485
7486     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7487
7488     if (DONT_COLLIDE_WITH(element) &&
7489         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7490         !PLAYER_ENEMY_PROTECTED(newx, newy))
7491     {
7492       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7493
7494       return;
7495     }
7496
7497     else if (CAN_MOVE_INTO_ACID(element) &&
7498              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7499              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7500              (MovDir[x][y] == MV_DOWN ||
7501               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7502     {
7503       SplashAcid(newx, newy);
7504       Store[x][y] = EL_ACID;
7505     }
7506     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7507     {
7508       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7509           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7510           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7511           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7512       {
7513         RemoveField(x, y);
7514         DrawLevelField(x, y);
7515
7516         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7517         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7518           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7519
7520         local_player->friends_still_needed--;
7521         if (!local_player->friends_still_needed &&
7522             !local_player->GameOver && AllPlayersGone)
7523           PlayerWins(local_player);
7524
7525         return;
7526       }
7527       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7528       {
7529         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7530           DrawLevelField(newx, newy);
7531         else
7532           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7533       }
7534       else if (!IS_FREE(newx, newy))
7535       {
7536         GfxAction[x][y] = ACTION_WAITING;
7537
7538         if (IS_PLAYER(x, y))
7539           DrawPlayerField(x, y);
7540         else
7541           DrawLevelField(x, y);
7542
7543         return;
7544       }
7545     }
7546     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7547     {
7548       if (IS_FOOD_PIG(Feld[newx][newy]))
7549       {
7550         if (IS_MOVING(newx, newy))
7551           RemoveMovingField(newx, newy);
7552         else
7553         {
7554           Feld[newx][newy] = EL_EMPTY;
7555           DrawLevelField(newx, newy);
7556         }
7557
7558         PlayLevelSound(x, y, SND_PIG_DIGGING);
7559       }
7560       else if (!IS_FREE(newx, newy))
7561       {
7562         if (IS_PLAYER(x, y))
7563           DrawPlayerField(x, y);
7564         else
7565           DrawLevelField(x, y);
7566
7567         return;
7568       }
7569     }
7570     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7571     {
7572       if (Store[x][y] != EL_EMPTY)
7573       {
7574         boolean can_clone = FALSE;
7575         int xx, yy;
7576
7577         /* check if element to clone is still there */
7578         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7579         {
7580           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7581           {
7582             can_clone = TRUE;
7583
7584             break;
7585           }
7586         }
7587
7588         /* cannot clone or target field not free anymore -- do not clone */
7589         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7590           Store[x][y] = EL_EMPTY;
7591       }
7592
7593       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7594       {
7595         if (IS_MV_DIAGONAL(MovDir[x][y]))
7596         {
7597           int diagonal_move_dir = MovDir[x][y];
7598           int stored = Store[x][y];
7599           int change_delay = 8;
7600           int graphic;
7601
7602           /* android is moving diagonally */
7603
7604           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7605
7606           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7607           GfxElement[x][y] = EL_EMC_ANDROID;
7608           GfxAction[x][y] = ACTION_SHRINKING;
7609           GfxDir[x][y] = diagonal_move_dir;
7610           ChangeDelay[x][y] = change_delay;
7611
7612           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7613                                    GfxDir[x][y]);
7614
7615           DrawLevelGraphicAnimation(x, y, graphic);
7616           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7617
7618           if (Feld[newx][newy] == EL_ACID)
7619           {
7620             SplashAcid(newx, newy);
7621
7622             return;
7623           }
7624
7625           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7626
7627           Store[newx][newy] = EL_EMC_ANDROID;
7628           GfxElement[newx][newy] = EL_EMC_ANDROID;
7629           GfxAction[newx][newy] = ACTION_GROWING;
7630           GfxDir[newx][newy] = diagonal_move_dir;
7631           ChangeDelay[newx][newy] = change_delay;
7632
7633           graphic = el_act_dir2img(GfxElement[newx][newy],
7634                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7635
7636           DrawLevelGraphicAnimation(newx, newy, graphic);
7637           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7638
7639           return;
7640         }
7641         else
7642         {
7643           Feld[newx][newy] = EL_EMPTY;
7644           DrawLevelField(newx, newy);
7645
7646           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7647         }
7648       }
7649       else if (!IS_FREE(newx, newy))
7650       {
7651 #if 0
7652         if (IS_PLAYER(x, y))
7653           DrawPlayerField(x, y);
7654         else
7655           DrawLevelField(x, y);
7656 #endif
7657
7658         return;
7659       }
7660     }
7661     else if (IS_CUSTOM_ELEMENT(element) &&
7662              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7663     {
7664       int new_element = Feld[newx][newy];
7665
7666       if (!IS_FREE(newx, newy))
7667       {
7668         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7669                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7670                       ACTION_BREAKING);
7671
7672         /* no element can dig solid indestructible elements */
7673         if (IS_INDESTRUCTIBLE(new_element) &&
7674             !IS_DIGGABLE(new_element) &&
7675             !IS_COLLECTIBLE(new_element))
7676           return;
7677
7678         if (AmoebaNr[newx][newy] &&
7679             (new_element == EL_AMOEBA_FULL ||
7680              new_element == EL_BD_AMOEBA ||
7681              new_element == EL_AMOEBA_GROWING))
7682         {
7683           AmoebaCnt[AmoebaNr[newx][newy]]--;
7684           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7685         }
7686
7687         if (IS_MOVING(newx, newy))
7688           RemoveMovingField(newx, newy);
7689         else
7690         {
7691           RemoveField(newx, newy);
7692           DrawLevelField(newx, newy);
7693         }
7694
7695         /* if digged element was about to explode, prevent the explosion */
7696         ExplodeField[newx][newy] = EX_TYPE_NONE;
7697
7698         PlayLevelSoundAction(x, y, action);
7699       }
7700
7701       Store[newx][newy] = EL_EMPTY;
7702 #if 1
7703       /* this makes it possible to leave the removed element again */
7704       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7705         Store[newx][newy] = new_element;
7706 #else
7707       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7708       {
7709         int move_leave_element = element_info[element].move_leave_element;
7710
7711         /* this makes it possible to leave the removed element again */
7712         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7713                              new_element : move_leave_element);
7714       }
7715 #endif
7716
7717       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7718       {
7719         RunnerVisit[x][y] = FrameCounter;
7720         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7721       }
7722     }
7723     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7724     {
7725       if (!IS_FREE(newx, newy))
7726       {
7727         if (IS_PLAYER(x, y))
7728           DrawPlayerField(x, y);
7729         else
7730           DrawLevelField(x, y);
7731
7732         return;
7733       }
7734       else
7735       {
7736         boolean wanna_flame = !RND(10);
7737         int dx = newx - x, dy = newy - y;
7738         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7739         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7740         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7741                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7742         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7743                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7744
7745         if ((wanna_flame ||
7746              IS_CLASSIC_ENEMY(element1) ||
7747              IS_CLASSIC_ENEMY(element2)) &&
7748             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7749             element1 != EL_FLAMES && element2 != EL_FLAMES)
7750         {
7751           ResetGfxAnimation(x, y);
7752           GfxAction[x][y] = ACTION_ATTACKING;
7753
7754           if (IS_PLAYER(x, y))
7755             DrawPlayerField(x, y);
7756           else
7757             DrawLevelField(x, y);
7758
7759           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7760
7761           MovDelay[x][y] = 50;
7762
7763           /* !!! */
7764 #if 0
7765           RemoveField(newx, newy);
7766 #endif
7767           Feld[newx][newy] = EL_FLAMES;
7768           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7769           {
7770 #if 0
7771             RemoveField(newx1, newy1);
7772 #endif
7773             Feld[newx1][newy1] = EL_FLAMES;
7774           }
7775           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7776           {
7777 #if 0
7778             RemoveField(newx2, newy2);
7779 #endif
7780             Feld[newx2][newy2] = EL_FLAMES;
7781           }
7782
7783           return;
7784         }
7785       }
7786     }
7787     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7788              Feld[newx][newy] == EL_DIAMOND)
7789     {
7790       if (IS_MOVING(newx, newy))
7791         RemoveMovingField(newx, newy);
7792       else
7793       {
7794         Feld[newx][newy] = EL_EMPTY;
7795         DrawLevelField(newx, newy);
7796       }
7797
7798       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7799     }
7800     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7801              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7802     {
7803       if (AmoebaNr[newx][newy])
7804       {
7805         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7806         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7807             Feld[newx][newy] == EL_BD_AMOEBA)
7808           AmoebaCnt[AmoebaNr[newx][newy]]--;
7809       }
7810
7811 #if 0
7812       /* !!! test !!! */
7813       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7814       {
7815         RemoveMovingField(newx, newy);
7816       }
7817 #else
7818       if (IS_MOVING(newx, newy))
7819       {
7820         RemoveMovingField(newx, newy);
7821       }
7822 #endif
7823       else
7824       {
7825         Feld[newx][newy] = EL_EMPTY;
7826         DrawLevelField(newx, newy);
7827       }
7828
7829       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7830     }
7831     else if ((element == EL_PACMAN || element == EL_MOLE)
7832              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7833     {
7834       if (AmoebaNr[newx][newy])
7835       {
7836         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7837         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7838             Feld[newx][newy] == EL_BD_AMOEBA)
7839           AmoebaCnt[AmoebaNr[newx][newy]]--;
7840       }
7841
7842       if (element == EL_MOLE)
7843       {
7844         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7845         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7846
7847         ResetGfxAnimation(x, y);
7848         GfxAction[x][y] = ACTION_DIGGING;
7849         DrawLevelField(x, y);
7850
7851         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7852
7853         return;                         /* wait for shrinking amoeba */
7854       }
7855       else      /* element == EL_PACMAN */
7856       {
7857         Feld[newx][newy] = EL_EMPTY;
7858         DrawLevelField(newx, newy);
7859         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7860       }
7861     }
7862     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7863              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7864               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7865     {
7866       /* wait for shrinking amoeba to completely disappear */
7867       return;
7868     }
7869     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7870     {
7871       /* object was running against a wall */
7872
7873       TurnRound(x, y);
7874
7875 #if 0
7876       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7877       if (move_pattern & MV_ANY_DIRECTION &&
7878           move_pattern == MovDir[x][y])
7879       {
7880         int blocking_element =
7881           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7882
7883         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7884                                  MovDir[x][y]);
7885
7886         element = Feld[x][y];   /* element might have changed */
7887       }
7888 #endif
7889
7890       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7891         DrawLevelElementAnimation(x, y, element);
7892
7893       if (DONT_TOUCH(element))
7894         TestIfBadThingTouchesPlayer(x, y);
7895
7896       return;
7897     }
7898
7899     InitMovingField(x, y, MovDir[x][y]);
7900
7901     PlayLevelSoundAction(x, y, ACTION_MOVING);
7902   }
7903
7904   if (MovDir[x][y])
7905     ContinueMoving(x, y);
7906 }
7907
7908 void ContinueMoving(int x, int y)
7909 {
7910   int element = Feld[x][y];
7911   struct ElementInfo *ei = &element_info[element];
7912   int direction = MovDir[x][y];
7913   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7914   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7915   int newx = x + dx, newy = y + dy;
7916   int stored = Store[x][y];
7917   int stored_new = Store[newx][newy];
7918   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7919   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7920   boolean last_line = (newy == lev_fieldy - 1);
7921
7922   MovPos[x][y] += getElementMoveStepsize(x, y);
7923
7924   if (pushed_by_player) /* special case: moving object pushed by player */
7925     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7926
7927   if (ABS(MovPos[x][y]) < TILEX)
7928   {
7929 #if 0
7930     int ee = Feld[x][y];
7931     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7932     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7933
7934     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7935            x, y, ABS(MovPos[x][y]),
7936            ee, gg, ff,
7937            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7938 #endif
7939
7940     DrawLevelField(x, y);
7941
7942     return;     /* element is still moving */
7943   }
7944
7945   /* element reached destination field */
7946
7947   Feld[x][y] = EL_EMPTY;
7948   Feld[newx][newy] = element;
7949   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7950
7951   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7952   {
7953     element = Feld[newx][newy] = EL_ACID;
7954   }
7955   else if (element == EL_MOLE)
7956   {
7957     Feld[x][y] = EL_SAND;
7958
7959     DrawLevelFieldCrumbledSandNeighbours(x, y);
7960   }
7961   else if (element == EL_QUICKSAND_FILLING)
7962   {
7963     element = Feld[newx][newy] = get_next_element(element);
7964     Store[newx][newy] = Store[x][y];
7965   }
7966   else if (element == EL_QUICKSAND_EMPTYING)
7967   {
7968     Feld[x][y] = get_next_element(element);
7969     element = Feld[newx][newy] = Store[x][y];
7970   }
7971   else if (element == EL_QUICKSAND_FAST_FILLING)
7972   {
7973     element = Feld[newx][newy] = get_next_element(element);
7974     Store[newx][newy] = Store[x][y];
7975   }
7976   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7977   {
7978     Feld[x][y] = get_next_element(element);
7979     element = Feld[newx][newy] = Store[x][y];
7980   }
7981   else if (element == EL_MAGIC_WALL_FILLING)
7982   {
7983     element = Feld[newx][newy] = get_next_element(element);
7984     if (!game.magic_wall_active)
7985       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7986     Store[newx][newy] = Store[x][y];
7987   }
7988   else if (element == EL_MAGIC_WALL_EMPTYING)
7989   {
7990     Feld[x][y] = get_next_element(element);
7991     if (!game.magic_wall_active)
7992       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7993     element = Feld[newx][newy] = Store[x][y];
7994
7995 #if USE_NEW_CUSTOM_VALUE
7996     InitField(newx, newy, FALSE);
7997 #endif
7998   }
7999   else if (element == EL_BD_MAGIC_WALL_FILLING)
8000   {
8001     element = Feld[newx][newy] = get_next_element(element);
8002     if (!game.magic_wall_active)
8003       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8004     Store[newx][newy] = Store[x][y];
8005   }
8006   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8007   {
8008     Feld[x][y] = get_next_element(element);
8009     if (!game.magic_wall_active)
8010       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8011     element = Feld[newx][newy] = Store[x][y];
8012
8013 #if USE_NEW_CUSTOM_VALUE
8014     InitField(newx, newy, FALSE);
8015 #endif
8016   }
8017   else if (element == EL_DC_MAGIC_WALL_FILLING)
8018   {
8019     element = Feld[newx][newy] = get_next_element(element);
8020     if (!game.magic_wall_active)
8021       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8022     Store[newx][newy] = Store[x][y];
8023   }
8024   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8025   {
8026     Feld[x][y] = get_next_element(element);
8027     if (!game.magic_wall_active)
8028       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8029     element = Feld[newx][newy] = Store[x][y];
8030
8031 #if USE_NEW_CUSTOM_VALUE
8032     InitField(newx, newy, FALSE);
8033 #endif
8034   }
8035   else if (element == EL_AMOEBA_DROPPING)
8036   {
8037     Feld[x][y] = get_next_element(element);
8038     element = Feld[newx][newy] = Store[x][y];
8039   }
8040   else if (element == EL_SOKOBAN_OBJECT)
8041   {
8042     if (Back[x][y])
8043       Feld[x][y] = Back[x][y];
8044
8045     if (Back[newx][newy])
8046       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8047
8048     Back[x][y] = Back[newx][newy] = 0;
8049   }
8050
8051   Store[x][y] = EL_EMPTY;
8052   MovPos[x][y] = 0;
8053   MovDir[x][y] = 0;
8054   MovDelay[x][y] = 0;
8055
8056   MovDelay[newx][newy] = 0;
8057
8058   if (CAN_CHANGE_OR_HAS_ACTION(element))
8059   {
8060     /* copy element change control values to new field */
8061     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8062     ChangePage[newx][newy]  = ChangePage[x][y];
8063     ChangeCount[newx][newy] = ChangeCount[x][y];
8064     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8065   }
8066
8067 #if USE_NEW_CUSTOM_VALUE
8068     CustomValue[newx][newy] = CustomValue[x][y];
8069 #endif
8070
8071   ChangeDelay[x][y] = 0;
8072   ChangePage[x][y] = -1;
8073   ChangeCount[x][y] = 0;
8074   ChangeEvent[x][y] = -1;
8075
8076 #if USE_NEW_CUSTOM_VALUE
8077   CustomValue[x][y] = 0;
8078 #endif
8079
8080   /* copy animation control values to new field */
8081   GfxFrame[newx][newy]  = GfxFrame[x][y];
8082   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8083   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8084   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8085
8086   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8087
8088   /* some elements can leave other elements behind after moving */
8089 #if 1
8090   if (ei->move_leave_element != EL_EMPTY &&
8091       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8092       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8093 #else
8094   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8095       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8096       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8097 #endif
8098   {
8099     int move_leave_element = ei->move_leave_element;
8100
8101 #if 1
8102 #if 1
8103     /* this makes it possible to leave the removed element again */
8104     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8105       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8106 #else
8107     /* this makes it possible to leave the removed element again */
8108     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8109       move_leave_element = stored;
8110 #endif
8111 #else
8112     /* this makes it possible to leave the removed element again */
8113     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8114         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8115       move_leave_element = stored;
8116 #endif
8117
8118     Feld[x][y] = move_leave_element;
8119
8120     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8121       MovDir[x][y] = direction;
8122
8123     InitField(x, y, FALSE);
8124
8125     if (GFX_CRUMBLED(Feld[x][y]))
8126       DrawLevelFieldCrumbledSandNeighbours(x, y);
8127
8128     if (ELEM_IS_PLAYER(move_leave_element))
8129       RelocatePlayer(x, y, move_leave_element);
8130   }
8131
8132   /* do this after checking for left-behind element */
8133   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8134
8135   if (!CAN_MOVE(element) ||
8136       (CAN_FALL(element) && direction == MV_DOWN &&
8137        (element == EL_SPRING ||
8138         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8139         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8140     GfxDir[x][y] = MovDir[newx][newy] = 0;
8141
8142   DrawLevelField(x, y);
8143   DrawLevelField(newx, newy);
8144
8145   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8146
8147   /* prevent pushed element from moving on in pushed direction */
8148   if (pushed_by_player && CAN_MOVE(element) &&
8149       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8150       !(element_info[element].move_pattern & direction))
8151     TurnRound(newx, newy);
8152
8153   /* prevent elements on conveyor belt from moving on in last direction */
8154   if (pushed_by_conveyor && CAN_FALL(element) &&
8155       direction & MV_HORIZONTAL)
8156     MovDir[newx][newy] = 0;
8157
8158   if (!pushed_by_player)
8159   {
8160     int nextx = newx + dx, nexty = newy + dy;
8161     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8162
8163     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8164
8165     if (CAN_FALL(element) && direction == MV_DOWN)
8166       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8167
8168     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8169       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8170
8171 #if USE_FIX_IMPACT_COLLISION
8172     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8173       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8174 #endif
8175   }
8176
8177   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8178   {
8179     TestIfBadThingTouchesPlayer(newx, newy);
8180     TestIfBadThingTouchesFriend(newx, newy);
8181
8182     if (!IS_CUSTOM_ELEMENT(element))
8183       TestIfBadThingTouchesOtherBadThing(newx, newy);
8184   }
8185   else if (element == EL_PENGUIN)
8186     TestIfFriendTouchesBadThing(newx, newy);
8187
8188   /* give the player one last chance (one more frame) to move away */
8189   if (CAN_FALL(element) && direction == MV_DOWN &&
8190       (last_line || (!IS_FREE(x, newy + 1) &&
8191                      (!IS_PLAYER(x, newy + 1) ||
8192                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8193     Impact(x, newy);
8194
8195   if (pushed_by_player && !game.use_change_when_pushing_bug)
8196   {
8197     int push_side = MV_DIR_OPPOSITE(direction);
8198     struct PlayerInfo *player = PLAYERINFO(x, y);
8199
8200     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8201                                player->index_bit, push_side);
8202     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8203                                         player->index_bit, push_side);
8204   }
8205
8206   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8207     MovDelay[newx][newy] = 1;
8208
8209   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8210
8211   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8212
8213 #if 0
8214   if (ChangePage[newx][newy] != -1)             /* delayed change */
8215   {
8216     int page = ChangePage[newx][newy];
8217     struct ElementChangeInfo *change = &ei->change_page[page];
8218
8219     ChangePage[newx][newy] = -1;
8220
8221     if (change->can_change)
8222     {
8223       if (ChangeElement(newx, newy, element, page))
8224       {
8225         if (change->post_change_function)
8226           change->post_change_function(newx, newy);
8227       }
8228     }
8229
8230     if (change->has_action)
8231       ExecuteCustomElementAction(newx, newy, element, page);
8232   }
8233 #endif
8234
8235   TestIfElementHitsCustomElement(newx, newy, direction);
8236   TestIfPlayerTouchesCustomElement(newx, newy);
8237   TestIfElementTouchesCustomElement(newx, newy);
8238
8239   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8240       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8241     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8242                              MV_DIR_OPPOSITE(direction));
8243 }
8244
8245 int AmoebeNachbarNr(int ax, int ay)
8246 {
8247   int i;
8248   int element = Feld[ax][ay];
8249   int group_nr = 0;
8250   static int xy[4][2] =
8251   {
8252     { 0, -1 },
8253     { -1, 0 },
8254     { +1, 0 },
8255     { 0, +1 }
8256   };
8257
8258   for (i = 0; i < NUM_DIRECTIONS; i++)
8259   {
8260     int x = ax + xy[i][0];
8261     int y = ay + xy[i][1];
8262
8263     if (!IN_LEV_FIELD(x, y))
8264       continue;
8265
8266     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8267       group_nr = AmoebaNr[x][y];
8268   }
8269
8270   return group_nr;
8271 }
8272
8273 void AmoebenVereinigen(int ax, int ay)
8274 {
8275   int i, x, y, xx, yy;
8276   int new_group_nr = AmoebaNr[ax][ay];
8277   static int xy[4][2] =
8278   {
8279     { 0, -1 },
8280     { -1, 0 },
8281     { +1, 0 },
8282     { 0, +1 }
8283   };
8284
8285   if (new_group_nr == 0)
8286     return;
8287
8288   for (i = 0; i < NUM_DIRECTIONS; i++)
8289   {
8290     x = ax + xy[i][0];
8291     y = ay + xy[i][1];
8292
8293     if (!IN_LEV_FIELD(x, y))
8294       continue;
8295
8296     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8297          Feld[x][y] == EL_BD_AMOEBA ||
8298          Feld[x][y] == EL_AMOEBA_DEAD) &&
8299         AmoebaNr[x][y] != new_group_nr)
8300     {
8301       int old_group_nr = AmoebaNr[x][y];
8302
8303       if (old_group_nr == 0)
8304         return;
8305
8306       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8307       AmoebaCnt[old_group_nr] = 0;
8308       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8309       AmoebaCnt2[old_group_nr] = 0;
8310
8311       SCAN_PLAYFIELD(xx, yy)
8312       {
8313         if (AmoebaNr[xx][yy] == old_group_nr)
8314           AmoebaNr[xx][yy] = new_group_nr;
8315       }
8316     }
8317   }
8318 }
8319
8320 void AmoebeUmwandeln(int ax, int ay)
8321 {
8322   int i, x, y;
8323
8324   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8325   {
8326     int group_nr = AmoebaNr[ax][ay];
8327
8328 #ifdef DEBUG
8329     if (group_nr == 0)
8330     {
8331       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8332       printf("AmoebeUmwandeln(): This should never happen!\n");
8333       return;
8334     }
8335 #endif
8336
8337     SCAN_PLAYFIELD(x, y)
8338     {
8339       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8340       {
8341         AmoebaNr[x][y] = 0;
8342         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8343       }
8344     }
8345
8346     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8347                             SND_AMOEBA_TURNING_TO_GEM :
8348                             SND_AMOEBA_TURNING_TO_ROCK));
8349     Bang(ax, ay);
8350   }
8351   else
8352   {
8353     static int xy[4][2] =
8354     {
8355       { 0, -1 },
8356       { -1, 0 },
8357       { +1, 0 },
8358       { 0, +1 }
8359     };
8360
8361     for (i = 0; i < NUM_DIRECTIONS; i++)
8362     {
8363       x = ax + xy[i][0];
8364       y = ay + xy[i][1];
8365
8366       if (!IN_LEV_FIELD(x, y))
8367         continue;
8368
8369       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8370       {
8371         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8372                               SND_AMOEBA_TURNING_TO_GEM :
8373                               SND_AMOEBA_TURNING_TO_ROCK));
8374         Bang(x, y);
8375       }
8376     }
8377   }
8378 }
8379
8380 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8381 {
8382   int x, y;
8383   int group_nr = AmoebaNr[ax][ay];
8384   boolean done = FALSE;
8385
8386 #ifdef DEBUG
8387   if (group_nr == 0)
8388   {
8389     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8390     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8391     return;
8392   }
8393 #endif
8394
8395   SCAN_PLAYFIELD(x, y)
8396   {
8397     if (AmoebaNr[x][y] == group_nr &&
8398         (Feld[x][y] == EL_AMOEBA_DEAD ||
8399          Feld[x][y] == EL_BD_AMOEBA ||
8400          Feld[x][y] == EL_AMOEBA_GROWING))
8401     {
8402       AmoebaNr[x][y] = 0;
8403       Feld[x][y] = new_element;
8404       InitField(x, y, FALSE);
8405       DrawLevelField(x, y);
8406       done = TRUE;
8407     }
8408   }
8409
8410   if (done)
8411     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8412                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8413                             SND_BD_AMOEBA_TURNING_TO_GEM));
8414 }
8415
8416 void AmoebeWaechst(int x, int y)
8417 {
8418   static unsigned long sound_delay = 0;
8419   static unsigned long sound_delay_value = 0;
8420
8421   if (!MovDelay[x][y])          /* start new growing cycle */
8422   {
8423     MovDelay[x][y] = 7;
8424
8425     if (DelayReached(&sound_delay, sound_delay_value))
8426     {
8427       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8428       sound_delay_value = 30;
8429     }
8430   }
8431
8432   if (MovDelay[x][y])           /* wait some time before growing bigger */
8433   {
8434     MovDelay[x][y]--;
8435     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8436     {
8437       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8438                                            6 - MovDelay[x][y]);
8439
8440       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8441     }
8442
8443     if (!MovDelay[x][y])
8444     {
8445       Feld[x][y] = Store[x][y];
8446       Store[x][y] = 0;
8447       DrawLevelField(x, y);
8448     }
8449   }
8450 }
8451
8452 void AmoebaDisappearing(int x, int y)
8453 {
8454   static unsigned long sound_delay = 0;
8455   static unsigned long sound_delay_value = 0;
8456
8457   if (!MovDelay[x][y])          /* start new shrinking cycle */
8458   {
8459     MovDelay[x][y] = 7;
8460
8461     if (DelayReached(&sound_delay, sound_delay_value))
8462       sound_delay_value = 30;
8463   }
8464
8465   if (MovDelay[x][y])           /* wait some time before shrinking */
8466   {
8467     MovDelay[x][y]--;
8468     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8469     {
8470       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8471                                            6 - MovDelay[x][y]);
8472
8473       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8474     }
8475
8476     if (!MovDelay[x][y])
8477     {
8478       Feld[x][y] = EL_EMPTY;
8479       DrawLevelField(x, y);
8480
8481       /* don't let mole enter this field in this cycle;
8482          (give priority to objects falling to this field from above) */
8483       Stop[x][y] = TRUE;
8484     }
8485   }
8486 }
8487
8488 void AmoebeAbleger(int ax, int ay)
8489 {
8490   int i;
8491   int element = Feld[ax][ay];
8492   int graphic = el2img(element);
8493   int newax = ax, neway = ay;
8494   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8495   static int xy[4][2] =
8496   {
8497     { 0, -1 },
8498     { -1, 0 },
8499     { +1, 0 },
8500     { 0, +1 }
8501   };
8502
8503   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8504   {
8505     Feld[ax][ay] = EL_AMOEBA_DEAD;
8506     DrawLevelField(ax, ay);
8507     return;
8508   }
8509
8510   if (IS_ANIMATED(graphic))
8511     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8512
8513   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8514     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8515
8516   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8517   {
8518     MovDelay[ax][ay]--;
8519     if (MovDelay[ax][ay])
8520       return;
8521   }
8522
8523   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8524   {
8525     int start = RND(4);
8526     int x = ax + xy[start][0];
8527     int y = ay + xy[start][1];
8528
8529     if (!IN_LEV_FIELD(x, y))
8530       return;
8531
8532     if (IS_FREE(x, y) ||
8533         CAN_GROW_INTO(Feld[x][y]) ||
8534         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8535         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8536     {
8537       newax = x;
8538       neway = y;
8539     }
8540
8541     if (newax == ax && neway == ay)
8542       return;
8543   }
8544   else                          /* normal or "filled" (BD style) amoeba */
8545   {
8546     int start = RND(4);
8547     boolean waiting_for_player = FALSE;
8548
8549     for (i = 0; i < NUM_DIRECTIONS; i++)
8550     {
8551       int j = (start + i) % 4;
8552       int x = ax + xy[j][0];
8553       int y = ay + xy[j][1];
8554
8555       if (!IN_LEV_FIELD(x, y))
8556         continue;
8557
8558       if (IS_FREE(x, y) ||
8559           CAN_GROW_INTO(Feld[x][y]) ||
8560           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8561           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8562       {
8563         newax = x;
8564         neway = y;
8565         break;
8566       }
8567       else if (IS_PLAYER(x, y))
8568         waiting_for_player = TRUE;
8569     }
8570
8571     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8572     {
8573       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8574       {
8575         Feld[ax][ay] = EL_AMOEBA_DEAD;
8576         DrawLevelField(ax, ay);
8577         AmoebaCnt[AmoebaNr[ax][ay]]--;
8578
8579         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8580         {
8581           if (element == EL_AMOEBA_FULL)
8582             AmoebeUmwandeln(ax, ay);
8583           else if (element == EL_BD_AMOEBA)
8584             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8585         }
8586       }
8587       return;
8588     }
8589     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8590     {
8591       /* amoeba gets larger by growing in some direction */
8592
8593       int new_group_nr = AmoebaNr[ax][ay];
8594
8595 #ifdef DEBUG
8596   if (new_group_nr == 0)
8597   {
8598     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8599     printf("AmoebeAbleger(): This should never happen!\n");
8600     return;
8601   }
8602 #endif
8603
8604       AmoebaNr[newax][neway] = new_group_nr;
8605       AmoebaCnt[new_group_nr]++;
8606       AmoebaCnt2[new_group_nr]++;
8607
8608       /* if amoeba touches other amoeba(s) after growing, unify them */
8609       AmoebenVereinigen(newax, neway);
8610
8611       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8612       {
8613         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8614         return;
8615       }
8616     }
8617   }
8618
8619   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8620       (neway == lev_fieldy - 1 && newax != ax))
8621   {
8622     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8623     Store[newax][neway] = element;
8624   }
8625   else if (neway == ay || element == EL_EMC_DRIPPER)
8626   {
8627     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8628
8629     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8630   }
8631   else
8632   {
8633     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8634     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8635     Store[ax][ay] = EL_AMOEBA_DROP;
8636     ContinueMoving(ax, ay);
8637     return;
8638   }
8639
8640   DrawLevelField(newax, neway);
8641 }
8642
8643 void Life(int ax, int ay)
8644 {
8645   int x1, y1, x2, y2;
8646   int life_time = 40;
8647   int element = Feld[ax][ay];
8648   int graphic = el2img(element);
8649   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8650                          level.biomaze);
8651   boolean changed = FALSE;
8652
8653   if (IS_ANIMATED(graphic))
8654     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8655
8656   if (Stop[ax][ay])
8657     return;
8658
8659   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8660     MovDelay[ax][ay] = life_time;
8661
8662   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8663   {
8664     MovDelay[ax][ay]--;
8665     if (MovDelay[ax][ay])
8666       return;
8667   }
8668
8669   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8670   {
8671     int xx = ax+x1, yy = ay+y1;
8672     int nachbarn = 0;
8673
8674     if (!IN_LEV_FIELD(xx, yy))
8675       continue;
8676
8677     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8678     {
8679       int x = xx+x2, y = yy+y2;
8680
8681       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8682         continue;
8683
8684       if (((Feld[x][y] == element ||
8685             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8686            !Stop[x][y]) ||
8687           (IS_FREE(x, y) && Stop[x][y]))
8688         nachbarn++;
8689     }
8690
8691     if (xx == ax && yy == ay)           /* field in the middle */
8692     {
8693       if (nachbarn < life_parameter[0] ||
8694           nachbarn > life_parameter[1])
8695       {
8696         Feld[xx][yy] = EL_EMPTY;
8697         if (!Stop[xx][yy])
8698           DrawLevelField(xx, yy);
8699         Stop[xx][yy] = TRUE;
8700         changed = TRUE;
8701       }
8702     }
8703     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8704     {                                   /* free border field */
8705       if (nachbarn >= life_parameter[2] &&
8706           nachbarn <= life_parameter[3])
8707       {
8708         Feld[xx][yy] = element;
8709         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8710         if (!Stop[xx][yy])
8711           DrawLevelField(xx, yy);
8712         Stop[xx][yy] = TRUE;
8713         changed = TRUE;
8714       }
8715     }
8716   }
8717
8718   if (changed)
8719     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8720                    SND_GAME_OF_LIFE_GROWING);
8721 }
8722
8723 static void InitRobotWheel(int x, int y)
8724 {
8725   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8726 }
8727
8728 static void RunRobotWheel(int x, int y)
8729 {
8730   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8731 }
8732
8733 static void StopRobotWheel(int x, int y)
8734 {
8735   if (ZX == x && ZY == y)
8736     ZX = ZY = -1;
8737 }
8738
8739 static void InitTimegateWheel(int x, int y)
8740 {
8741   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8742 }
8743
8744 static void RunTimegateWheel(int x, int y)
8745 {
8746   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8747 }
8748
8749 static void InitMagicBallDelay(int x, int y)
8750 {
8751 #if 1
8752   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8753 #else
8754   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8755 #endif
8756 }
8757
8758 static void ActivateMagicBall(int bx, int by)
8759 {
8760   int x, y;
8761
8762   if (level.ball_random)
8763   {
8764     int pos_border = RND(8);    /* select one of the eight border elements */
8765     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8766     int xx = pos_content % 3;
8767     int yy = pos_content / 3;
8768
8769     x = bx - 1 + xx;
8770     y = by - 1 + yy;
8771
8772     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8773       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8774   }
8775   else
8776   {
8777     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8778     {
8779       int xx = x - bx + 1;
8780       int yy = y - by + 1;
8781
8782       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8783         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8784     }
8785   }
8786
8787   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8788 }
8789
8790 void CheckExit(int x, int y)
8791 {
8792   if (local_player->gems_still_needed > 0 ||
8793       local_player->sokobanfields_still_needed > 0 ||
8794       local_player->lights_still_needed > 0)
8795   {
8796     int element = Feld[x][y];
8797     int graphic = el2img(element);
8798
8799     if (IS_ANIMATED(graphic))
8800       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8801
8802     return;
8803   }
8804
8805   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8806     return;
8807
8808   Feld[x][y] = EL_EXIT_OPENING;
8809
8810   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8811 }
8812
8813 void CheckExitEM(int x, int y)
8814 {
8815   if (local_player->gems_still_needed > 0 ||
8816       local_player->sokobanfields_still_needed > 0 ||
8817       local_player->lights_still_needed > 0)
8818   {
8819     int element = Feld[x][y];
8820     int graphic = el2img(element);
8821
8822     if (IS_ANIMATED(graphic))
8823       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8824
8825     return;
8826   }
8827
8828   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8829     return;
8830
8831   Feld[x][y] = EL_EM_EXIT_OPENING;
8832
8833   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8834 }
8835
8836 void CheckExitSteel(int x, int y)
8837 {
8838   if (local_player->gems_still_needed > 0 ||
8839       local_player->sokobanfields_still_needed > 0 ||
8840       local_player->lights_still_needed > 0)
8841   {
8842     int element = Feld[x][y];
8843     int graphic = el2img(element);
8844
8845     if (IS_ANIMATED(graphic))
8846       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8847
8848     return;
8849   }
8850
8851   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8852     return;
8853
8854   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8855
8856   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8857 }
8858
8859 void CheckExitSteelEM(int x, int y)
8860 {
8861   if (local_player->gems_still_needed > 0 ||
8862       local_player->sokobanfields_still_needed > 0 ||
8863       local_player->lights_still_needed > 0)
8864   {
8865     int element = Feld[x][y];
8866     int graphic = el2img(element);
8867
8868     if (IS_ANIMATED(graphic))
8869       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8870
8871     return;
8872   }
8873
8874   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8875     return;
8876
8877   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8878
8879   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8880 }
8881
8882 void CheckExitSP(int x, int y)
8883 {
8884   if (local_player->gems_still_needed > 0)
8885   {
8886     int element = Feld[x][y];
8887     int graphic = el2img(element);
8888
8889     if (IS_ANIMATED(graphic))
8890       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8891
8892     return;
8893   }
8894
8895   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8896     return;
8897
8898   Feld[x][y] = EL_SP_EXIT_OPENING;
8899
8900   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8901 }
8902
8903 static void CloseAllOpenTimegates()
8904 {
8905   int x, y;
8906
8907   SCAN_PLAYFIELD(x, y)
8908   {
8909     int element = Feld[x][y];
8910
8911     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8912     {
8913       Feld[x][y] = EL_TIMEGATE_CLOSING;
8914
8915       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8916     }
8917   }
8918 }
8919
8920 void DrawTwinkleOnField(int x, int y)
8921 {
8922   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8923     return;
8924
8925   if (Feld[x][y] == EL_BD_DIAMOND)
8926     return;
8927
8928   if (MovDelay[x][y] == 0)      /* next animation frame */
8929     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8930
8931   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8932   {
8933     MovDelay[x][y]--;
8934
8935     if (setup.direct_draw && MovDelay[x][y])
8936       SetDrawtoField(DRAW_BUFFERED);
8937
8938     DrawLevelElementAnimation(x, y, Feld[x][y]);
8939
8940     if (MovDelay[x][y] != 0)
8941     {
8942       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8943                                            10 - MovDelay[x][y]);
8944
8945       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8946
8947       if (setup.direct_draw)
8948       {
8949         int dest_x, dest_y;
8950
8951         dest_x = FX + SCREENX(x) * TILEX;
8952         dest_y = FY + SCREENY(y) * TILEY;
8953
8954         BlitBitmap(drawto_field, window,
8955                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8956         SetDrawtoField(DRAW_DIRECT);
8957       }
8958     }
8959   }
8960 }
8961
8962 void MauerWaechst(int x, int y)
8963 {
8964   int delay = 6;
8965
8966   if (!MovDelay[x][y])          /* next animation frame */
8967     MovDelay[x][y] = 3 * delay;
8968
8969   if (MovDelay[x][y])           /* wait some time before next frame */
8970   {
8971     MovDelay[x][y]--;
8972
8973     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8974     {
8975       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8976       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8977
8978       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8979     }
8980
8981     if (!MovDelay[x][y])
8982     {
8983       if (MovDir[x][y] == MV_LEFT)
8984       {
8985         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8986           DrawLevelField(x - 1, y);
8987       }
8988       else if (MovDir[x][y] == MV_RIGHT)
8989       {
8990         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8991           DrawLevelField(x + 1, y);
8992       }
8993       else if (MovDir[x][y] == MV_UP)
8994       {
8995         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8996           DrawLevelField(x, y - 1);
8997       }
8998       else
8999       {
9000         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9001           DrawLevelField(x, y + 1);
9002       }
9003
9004       Feld[x][y] = Store[x][y];
9005       Store[x][y] = 0;
9006       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9007       DrawLevelField(x, y);
9008     }
9009   }
9010 }
9011
9012 void MauerAbleger(int ax, int ay)
9013 {
9014   int element = Feld[ax][ay];
9015   int graphic = el2img(element);
9016   boolean oben_frei = FALSE, unten_frei = FALSE;
9017   boolean links_frei = FALSE, rechts_frei = FALSE;
9018   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9019   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9020   boolean new_wall = FALSE;
9021
9022   if (IS_ANIMATED(graphic))
9023     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9024
9025   if (!MovDelay[ax][ay])        /* start building new wall */
9026     MovDelay[ax][ay] = 6;
9027
9028   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9029   {
9030     MovDelay[ax][ay]--;
9031     if (MovDelay[ax][ay])
9032       return;
9033   }
9034
9035   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9036     oben_frei = TRUE;
9037   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9038     unten_frei = TRUE;
9039   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9040     links_frei = TRUE;
9041   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9042     rechts_frei = TRUE;
9043
9044   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9045       element == EL_EXPANDABLE_WALL_ANY)
9046   {
9047     if (oben_frei)
9048     {
9049       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9050       Store[ax][ay-1] = element;
9051       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9052       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9053         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9054                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9055       new_wall = TRUE;
9056     }
9057     if (unten_frei)
9058     {
9059       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9060       Store[ax][ay+1] = element;
9061       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9062       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9063         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9064                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9065       new_wall = TRUE;
9066     }
9067   }
9068
9069   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9070       element == EL_EXPANDABLE_WALL_ANY ||
9071       element == EL_EXPANDABLE_WALL ||
9072       element == EL_BD_EXPANDABLE_WALL)
9073   {
9074     if (links_frei)
9075     {
9076       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9077       Store[ax-1][ay] = element;
9078       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9079       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9080         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9081                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9082       new_wall = TRUE;
9083     }
9084
9085     if (rechts_frei)
9086     {
9087       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9088       Store[ax+1][ay] = element;
9089       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9090       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9091         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9092                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9093       new_wall = TRUE;
9094     }
9095   }
9096
9097   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9098     DrawLevelField(ax, ay);
9099
9100   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9101     oben_massiv = TRUE;
9102   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9103     unten_massiv = TRUE;
9104   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9105     links_massiv = TRUE;
9106   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9107     rechts_massiv = TRUE;
9108
9109   if (((oben_massiv && unten_massiv) ||
9110        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9111        element == EL_EXPANDABLE_WALL) &&
9112       ((links_massiv && rechts_massiv) ||
9113        element == EL_EXPANDABLE_WALL_VERTICAL))
9114     Feld[ax][ay] = EL_WALL;
9115
9116   if (new_wall)
9117     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9118 }
9119
9120 void MauerAblegerStahl(int ax, int ay)
9121 {
9122   int element = Feld[ax][ay];
9123   int graphic = el2img(element);
9124   boolean oben_frei = FALSE, unten_frei = FALSE;
9125   boolean links_frei = FALSE, rechts_frei = FALSE;
9126   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9127   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9128   boolean new_wall = FALSE;
9129
9130   if (IS_ANIMATED(graphic))
9131     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9132
9133   if (!MovDelay[ax][ay])        /* start building new wall */
9134     MovDelay[ax][ay] = 6;
9135
9136   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9137   {
9138     MovDelay[ax][ay]--;
9139     if (MovDelay[ax][ay])
9140       return;
9141   }
9142
9143   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9144     oben_frei = TRUE;
9145   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9146     unten_frei = TRUE;
9147   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9148     links_frei = TRUE;
9149   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9150     rechts_frei = TRUE;
9151
9152   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9153       element == EL_EXPANDABLE_STEELWALL_ANY)
9154   {
9155     if (oben_frei)
9156     {
9157       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9158       Store[ax][ay-1] = element;
9159       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9160       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9161         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9162                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9163       new_wall = TRUE;
9164     }
9165     if (unten_frei)
9166     {
9167       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9168       Store[ax][ay+1] = element;
9169       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9170       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9171         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9172                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9173       new_wall = TRUE;
9174     }
9175   }
9176
9177   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9178       element == EL_EXPANDABLE_STEELWALL_ANY)
9179   {
9180     if (links_frei)
9181     {
9182       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9183       Store[ax-1][ay] = element;
9184       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9185       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9186         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9187                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9188       new_wall = TRUE;
9189     }
9190
9191     if (rechts_frei)
9192     {
9193       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9194       Store[ax+1][ay] = element;
9195       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9196       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9197         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9198                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9199       new_wall = TRUE;
9200     }
9201   }
9202
9203   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9204     oben_massiv = TRUE;
9205   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9206     unten_massiv = TRUE;
9207   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9208     links_massiv = TRUE;
9209   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9210     rechts_massiv = TRUE;
9211
9212   if (((oben_massiv && unten_massiv) ||
9213        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9214       ((links_massiv && rechts_massiv) ||
9215        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9216     Feld[ax][ay] = EL_WALL;
9217
9218   if (new_wall)
9219     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9220 }
9221
9222 void CheckForDragon(int x, int y)
9223 {
9224   int i, j;
9225   boolean dragon_found = FALSE;
9226   static int xy[4][2] =
9227   {
9228     { 0, -1 },
9229     { -1, 0 },
9230     { +1, 0 },
9231     { 0, +1 }
9232   };
9233
9234   for (i = 0; i < NUM_DIRECTIONS; i++)
9235   {
9236     for (j = 0; j < 4; j++)
9237     {
9238       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9239
9240       if (IN_LEV_FIELD(xx, yy) &&
9241           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9242       {
9243         if (Feld[xx][yy] == EL_DRAGON)
9244           dragon_found = TRUE;
9245       }
9246       else
9247         break;
9248     }
9249   }
9250
9251   if (!dragon_found)
9252   {
9253     for (i = 0; i < NUM_DIRECTIONS; i++)
9254     {
9255       for (j = 0; j < 3; j++)
9256       {
9257         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9258   
9259         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9260         {
9261           Feld[xx][yy] = EL_EMPTY;
9262           DrawLevelField(xx, yy);
9263         }
9264         else
9265           break;
9266       }
9267     }
9268   }
9269 }
9270
9271 static void InitBuggyBase(int x, int y)
9272 {
9273   int element = Feld[x][y];
9274   int activating_delay = FRAMES_PER_SECOND / 4;
9275
9276   ChangeDelay[x][y] =
9277     (element == EL_SP_BUGGY_BASE ?
9278      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9279      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9280      activating_delay :
9281      element == EL_SP_BUGGY_BASE_ACTIVE ?
9282      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9283 }
9284
9285 static void WarnBuggyBase(int x, int y)
9286 {
9287   int i;
9288   static int xy[4][2] =
9289   {
9290     { 0, -1 },
9291     { -1, 0 },
9292     { +1, 0 },
9293     { 0, +1 }
9294   };
9295
9296   for (i = 0; i < NUM_DIRECTIONS; i++)
9297   {
9298     int xx = x + xy[i][0];
9299     int yy = y + xy[i][1];
9300
9301     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9302     {
9303       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9304
9305       break;
9306     }
9307   }
9308 }
9309
9310 static void InitTrap(int x, int y)
9311 {
9312   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9313 }
9314
9315 static void ActivateTrap(int x, int y)
9316 {
9317   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9318 }
9319
9320 static void ChangeActiveTrap(int x, int y)
9321 {
9322   int graphic = IMG_TRAP_ACTIVE;
9323
9324   /* if new animation frame was drawn, correct crumbled sand border */
9325   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9326     DrawLevelFieldCrumbledSand(x, y);
9327 }
9328
9329 static int getSpecialActionElement(int element, int number, int base_element)
9330 {
9331   return (element != EL_EMPTY ? element :
9332           number != -1 ? base_element + number - 1 :
9333           EL_EMPTY);
9334 }
9335
9336 static int getModifiedActionNumber(int value_old, int operator, int operand,
9337                                    int value_min, int value_max)
9338 {
9339   int value_new = (operator == CA_MODE_SET      ? operand :
9340                    operator == CA_MODE_ADD      ? value_old + operand :
9341                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9342                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9343                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9344                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9345                    value_old);
9346
9347   return (value_new < value_min ? value_min :
9348           value_new > value_max ? value_max :
9349           value_new);
9350 }
9351
9352 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9353 {
9354   struct ElementInfo *ei = &element_info[element];
9355   struct ElementChangeInfo *change = &ei->change_page[page];
9356   int target_element = change->target_element;
9357   int action_type = change->action_type;
9358   int action_mode = change->action_mode;
9359   int action_arg = change->action_arg;
9360   int i;
9361
9362   if (!change->has_action)
9363     return;
9364
9365   /* ---------- determine action paramater values -------------------------- */
9366
9367   int level_time_value =
9368     (level.time > 0 ? TimeLeft :
9369      TimePlayed);
9370
9371   int action_arg_element =
9372     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9373      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9374      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9375      EL_EMPTY);
9376
9377   int action_arg_direction =
9378     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9379      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9380      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9381      change->actual_trigger_side :
9382      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9383      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9384      MV_NONE);
9385
9386   int action_arg_number_min =
9387     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9388      CA_ARG_MIN);
9389
9390   int action_arg_number_max =
9391     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9392      action_type == CA_SET_LEVEL_GEMS ? 999 :
9393      action_type == CA_SET_LEVEL_TIME ? 9999 :
9394      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9395      action_type == CA_SET_CE_VALUE ? 9999 :
9396      action_type == CA_SET_CE_SCORE ? 9999 :
9397      CA_ARG_MAX);
9398
9399   int action_arg_number_reset =
9400     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9401      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9402      action_type == CA_SET_LEVEL_TIME ? level.time :
9403      action_type == CA_SET_LEVEL_SCORE ? 0 :
9404 #if USE_NEW_CUSTOM_VALUE
9405      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9406 #else
9407      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9408 #endif
9409      action_type == CA_SET_CE_SCORE ? 0 :
9410      0);
9411
9412   int action_arg_number =
9413     (action_arg <= CA_ARG_MAX ? action_arg :
9414      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420 #if USE_NEW_CUSTOM_VALUE
9421      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9422 #else
9423      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9424 #endif
9425      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9426      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9427      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9428      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9429      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9430      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9431      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9432      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9433      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9434      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9435      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9436      -1);
9437
9438   int action_arg_number_old =
9439     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9440      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9441      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9442      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9443      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9444      0);
9445
9446   int action_arg_number_new =
9447     getModifiedActionNumber(action_arg_number_old,
9448                             action_mode, action_arg_number,
9449                             action_arg_number_min, action_arg_number_max);
9450
9451   int trigger_player_bits =
9452     (change->actual_trigger_player >= EL_PLAYER_1 &&
9453      change->actual_trigger_player <= EL_PLAYER_4 ?
9454      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9455      PLAYER_BITS_ANY);
9456
9457   int action_arg_player_bits =
9458     (action_arg >= CA_ARG_PLAYER_1 &&
9459      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9460      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9461      PLAYER_BITS_ANY);
9462
9463   /* ---------- execute action  -------------------------------------------- */
9464
9465   switch (action_type)
9466   {
9467     case CA_NO_ACTION:
9468     {
9469       return;
9470     }
9471
9472     /* ---------- level actions  ------------------------------------------- */
9473
9474     case CA_RESTART_LEVEL:
9475     {
9476       game.restart_level = TRUE;
9477
9478       break;
9479     }
9480
9481     case CA_SHOW_ENVELOPE:
9482     {
9483       int element = getSpecialActionElement(action_arg_element,
9484                                             action_arg_number, EL_ENVELOPE_1);
9485
9486       if (IS_ENVELOPE(element))
9487         local_player->show_envelope = element;
9488
9489       break;
9490     }
9491
9492     case CA_SET_LEVEL_TIME:
9493     {
9494       if (level.time > 0)       /* only modify limited time value */
9495       {
9496         TimeLeft = action_arg_number_new;
9497
9498 #if 1
9499         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9500
9501         DisplayGameControlValues();
9502 #else
9503         DrawGameValue_Time(TimeLeft);
9504 #endif
9505
9506         if (!TimeLeft && setup.time_limit)
9507           for (i = 0; i < MAX_PLAYERS; i++)
9508             KillPlayer(&stored_player[i]);
9509       }
9510
9511       break;
9512     }
9513
9514     case CA_SET_LEVEL_SCORE:
9515     {
9516       local_player->score = action_arg_number_new;
9517
9518 #if 1
9519       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9520
9521       DisplayGameControlValues();
9522 #else
9523       DrawGameValue_Score(local_player->score);
9524 #endif
9525
9526       break;
9527     }
9528
9529     case CA_SET_LEVEL_GEMS:
9530     {
9531       local_player->gems_still_needed = action_arg_number_new;
9532
9533 #if 1
9534       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9535
9536       DisplayGameControlValues();
9537 #else
9538       DrawGameValue_Emeralds(local_player->gems_still_needed);
9539 #endif
9540
9541       break;
9542     }
9543
9544 #if !USE_PLAYER_GRAVITY
9545     case CA_SET_LEVEL_GRAVITY:
9546     {
9547       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9548                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9549                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9550                       game.gravity);
9551       break;
9552     }
9553 #endif
9554
9555     case CA_SET_LEVEL_WIND:
9556     {
9557       game.wind_direction = action_arg_direction;
9558
9559       break;
9560     }
9561
9562     /* ---------- player actions  ------------------------------------------ */
9563
9564     case CA_MOVE_PLAYER:
9565     {
9566       /* automatically move to the next field in specified direction */
9567       for (i = 0; i < MAX_PLAYERS; i++)
9568         if (trigger_player_bits & (1 << i))
9569           stored_player[i].programmed_action = action_arg_direction;
9570
9571       break;
9572     }
9573
9574     case CA_EXIT_PLAYER:
9575     {
9576       for (i = 0; i < MAX_PLAYERS; i++)
9577         if (action_arg_player_bits & (1 << i))
9578           PlayerWins(&stored_player[i]);
9579
9580       break;
9581     }
9582
9583     case CA_KILL_PLAYER:
9584     {
9585       for (i = 0; i < MAX_PLAYERS; i++)
9586         if (action_arg_player_bits & (1 << i))
9587           KillPlayer(&stored_player[i]);
9588
9589       break;
9590     }
9591
9592     case CA_SET_PLAYER_KEYS:
9593     {
9594       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9595       int element = getSpecialActionElement(action_arg_element,
9596                                             action_arg_number, EL_KEY_1);
9597
9598       if (IS_KEY(element))
9599       {
9600         for (i = 0; i < MAX_PLAYERS; i++)
9601         {
9602           if (trigger_player_bits & (1 << i))
9603           {
9604             stored_player[i].key[KEY_NR(element)] = key_state;
9605
9606             DrawGameDoorValues();
9607           }
9608         }
9609       }
9610
9611       break;
9612     }
9613
9614     case CA_SET_PLAYER_SPEED:
9615     {
9616       for (i = 0; i < MAX_PLAYERS; i++)
9617       {
9618         if (trigger_player_bits & (1 << i))
9619         {
9620           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9621
9622           if (action_arg == CA_ARG_SPEED_FASTER &&
9623               stored_player[i].cannot_move)
9624           {
9625             action_arg_number = STEPSIZE_VERY_SLOW;
9626           }
9627           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9628                    action_arg == CA_ARG_SPEED_FASTER)
9629           {
9630             action_arg_number = 2;
9631             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9632                            CA_MODE_MULTIPLY);
9633           }
9634           else if (action_arg == CA_ARG_NUMBER_RESET)
9635           {
9636             action_arg_number = level.initial_player_stepsize[i];
9637           }
9638
9639           move_stepsize =
9640             getModifiedActionNumber(move_stepsize,
9641                                     action_mode,
9642                                     action_arg_number,
9643                                     action_arg_number_min,
9644                                     action_arg_number_max);
9645
9646           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9647         }
9648       }
9649
9650       break;
9651     }
9652
9653     case CA_SET_PLAYER_SHIELD:
9654     {
9655       for (i = 0; i < MAX_PLAYERS; i++)
9656       {
9657         if (trigger_player_bits & (1 << i))
9658         {
9659           if (action_arg == CA_ARG_SHIELD_OFF)
9660           {
9661             stored_player[i].shield_normal_time_left = 0;
9662             stored_player[i].shield_deadly_time_left = 0;
9663           }
9664           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9665           {
9666             stored_player[i].shield_normal_time_left = 999999;
9667           }
9668           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9669           {
9670             stored_player[i].shield_normal_time_left = 999999;
9671             stored_player[i].shield_deadly_time_left = 999999;
9672           }
9673         }
9674       }
9675
9676       break;
9677     }
9678
9679 #if USE_PLAYER_GRAVITY
9680     case CA_SET_PLAYER_GRAVITY:
9681     {
9682       for (i = 0; i < MAX_PLAYERS; i++)
9683       {
9684         if (trigger_player_bits & (1 << i))
9685         {
9686           stored_player[i].gravity =
9687             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9688              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9689              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9690              stored_player[i].gravity);
9691         }
9692       }
9693
9694       break;
9695     }
9696 #endif
9697
9698     case CA_SET_PLAYER_ARTWORK:
9699     {
9700       for (i = 0; i < MAX_PLAYERS; i++)
9701       {
9702         if (trigger_player_bits & (1 << i))
9703         {
9704           int artwork_element = action_arg_element;
9705
9706           if (action_arg == CA_ARG_ELEMENT_RESET)
9707             artwork_element =
9708               (level.use_artwork_element[i] ? level.artwork_element[i] :
9709                stored_player[i].element_nr);
9710
9711 #if USE_GFX_RESET_PLAYER_ARTWORK
9712           if (stored_player[i].artwork_element != artwork_element)
9713             stored_player[i].Frame = 0;
9714 #endif
9715
9716           stored_player[i].artwork_element = artwork_element;
9717
9718           SetPlayerWaiting(&stored_player[i], FALSE);
9719
9720           /* set number of special actions for bored and sleeping animation */
9721           stored_player[i].num_special_action_bored =
9722             get_num_special_action(artwork_element,
9723                                    ACTION_BORING_1, ACTION_BORING_LAST);
9724           stored_player[i].num_special_action_sleeping =
9725             get_num_special_action(artwork_element,
9726                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9727         }
9728       }
9729
9730       break;
9731     }
9732
9733     /* ---------- CE actions  ---------------------------------------------- */
9734
9735     case CA_SET_CE_VALUE:
9736     {
9737 #if USE_NEW_CUSTOM_VALUE
9738       int last_ce_value = CustomValue[x][y];
9739
9740       CustomValue[x][y] = action_arg_number_new;
9741
9742       if (CustomValue[x][y] != last_ce_value)
9743       {
9744         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9745         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9746
9747         if (CustomValue[x][y] == 0)
9748         {
9749           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9750           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9751         }
9752       }
9753 #endif
9754
9755       break;
9756     }
9757
9758     case CA_SET_CE_SCORE:
9759     {
9760 #if USE_NEW_CUSTOM_VALUE
9761       int last_ce_score = ei->collect_score;
9762
9763       ei->collect_score = action_arg_number_new;
9764
9765       if (ei->collect_score != last_ce_score)
9766       {
9767         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9768         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9769
9770         if (ei->collect_score == 0)
9771         {
9772           int xx, yy;
9773
9774           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9775           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9776
9777           /*
9778             This is a very special case that seems to be a mixture between
9779             CheckElementChange() and CheckTriggeredElementChange(): while
9780             the first one only affects single elements that are triggered
9781             directly, the second one affects multiple elements in the playfield
9782             that are triggered indirectly by another element. This is a third
9783             case: Changing the CE score always affects multiple identical CEs,
9784             so every affected CE must be checked, not only the single CE for
9785             which the CE score was changed in the first place (as every instance
9786             of that CE shares the same CE score, and therefore also can change)!
9787           */
9788           SCAN_PLAYFIELD(xx, yy)
9789           {
9790             if (Feld[xx][yy] == element)
9791               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9792                                  CE_SCORE_GETS_ZERO);
9793           }
9794         }
9795       }
9796 #endif
9797
9798       break;
9799     }
9800
9801     /* ---------- engine actions  ------------------------------------------ */
9802
9803     case CA_SET_ENGINE_SCAN_MODE:
9804     {
9805       InitPlayfieldScanMode(action_arg);
9806
9807       break;
9808     }
9809
9810     default:
9811       break;
9812   }
9813 }
9814
9815 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9816 {
9817   int old_element = Feld[x][y];
9818   int new_element = GetElementFromGroupElement(element);
9819   int previous_move_direction = MovDir[x][y];
9820 #if USE_NEW_CUSTOM_VALUE
9821   int last_ce_value = CustomValue[x][y];
9822 #endif
9823   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9824   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9825   boolean add_player_onto_element = (new_element_is_player &&
9826 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9827                                      /* this breaks SnakeBite when a snake is
9828                                         halfway through a door that closes */
9829                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9830                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9831 #endif
9832                                      IS_WALKABLE(old_element));
9833
9834 #if 0
9835   /* check if element under the player changes from accessible to unaccessible
9836      (needed for special case of dropping element which then changes) */
9837   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9838       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9839   {
9840     Bang(x, y);
9841
9842     return;
9843   }
9844 #endif
9845
9846   if (!add_player_onto_element)
9847   {
9848     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9849       RemoveMovingField(x, y);
9850     else
9851       RemoveField(x, y);
9852
9853     Feld[x][y] = new_element;
9854
9855 #if !USE_GFX_RESET_GFX_ANIMATION
9856     ResetGfxAnimation(x, y);
9857     ResetRandomAnimationValue(x, y);
9858 #endif
9859
9860     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9861       MovDir[x][y] = previous_move_direction;
9862
9863 #if USE_NEW_CUSTOM_VALUE
9864     if (element_info[new_element].use_last_ce_value)
9865       CustomValue[x][y] = last_ce_value;
9866 #endif
9867
9868     InitField_WithBug1(x, y, FALSE);
9869
9870     new_element = Feld[x][y];   /* element may have changed */
9871
9872 #if USE_GFX_RESET_GFX_ANIMATION
9873     ResetGfxAnimation(x, y);
9874     ResetRandomAnimationValue(x, y);
9875 #endif
9876
9877     DrawLevelField(x, y);
9878
9879     if (GFX_CRUMBLED(new_element))
9880       DrawLevelFieldCrumbledSandNeighbours(x, y);
9881   }
9882
9883 #if 1
9884   /* check if element under the player changes from accessible to unaccessible
9885      (needed for special case of dropping element which then changes) */
9886   /* (must be checked after creating new element for walkable group elements) */
9887 #if USE_FIX_KILLED_BY_NON_WALKABLE
9888   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9889       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9890   {
9891     Bang(x, y);
9892
9893     return;
9894   }
9895 #else
9896   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9897       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9898   {
9899     Bang(x, y);
9900
9901     return;
9902   }
9903 #endif
9904 #endif
9905
9906   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9907   if (new_element_is_player)
9908     RelocatePlayer(x, y, new_element);
9909
9910   if (is_change)
9911     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9912
9913   TestIfBadThingTouchesPlayer(x, y);
9914   TestIfPlayerTouchesCustomElement(x, y);
9915   TestIfElementTouchesCustomElement(x, y);
9916 }
9917
9918 static void CreateField(int x, int y, int element)
9919 {
9920   CreateFieldExt(x, y, element, FALSE);
9921 }
9922
9923 static void CreateElementFromChange(int x, int y, int element)
9924 {
9925   element = GET_VALID_RUNTIME_ELEMENT(element);
9926
9927 #if USE_STOP_CHANGED_ELEMENTS
9928   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9929   {
9930     int old_element = Feld[x][y];
9931
9932     /* prevent changed element from moving in same engine frame
9933        unless both old and new element can either fall or move */
9934     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9935         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9936       Stop[x][y] = TRUE;
9937   }
9938 #endif
9939
9940   CreateFieldExt(x, y, element, TRUE);
9941 }
9942
9943 static boolean ChangeElement(int x, int y, int element, int page)
9944 {
9945   struct ElementInfo *ei = &element_info[element];
9946   struct ElementChangeInfo *change = &ei->change_page[page];
9947   int ce_value = CustomValue[x][y];
9948   int ce_score = ei->collect_score;
9949   int target_element;
9950   int old_element = Feld[x][y];
9951
9952   /* always use default change event to prevent running into a loop */
9953   if (ChangeEvent[x][y] == -1)
9954     ChangeEvent[x][y] = CE_DELAY;
9955
9956   if (ChangeEvent[x][y] == CE_DELAY)
9957   {
9958     /* reset actual trigger element, trigger player and action element */
9959     change->actual_trigger_element = EL_EMPTY;
9960     change->actual_trigger_player = EL_PLAYER_1;
9961     change->actual_trigger_side = CH_SIDE_NONE;
9962     change->actual_trigger_ce_value = 0;
9963     change->actual_trigger_ce_score = 0;
9964   }
9965
9966   /* do not change elements more than a specified maximum number of changes */
9967   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9968     return FALSE;
9969
9970   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9971
9972   if (change->explode)
9973   {
9974     Bang(x, y);
9975
9976     return TRUE;
9977   }
9978
9979   if (change->use_target_content)
9980   {
9981     boolean complete_replace = TRUE;
9982     boolean can_replace[3][3];
9983     int xx, yy;
9984
9985     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9986     {
9987       boolean is_empty;
9988       boolean is_walkable;
9989       boolean is_diggable;
9990       boolean is_collectible;
9991       boolean is_removable;
9992       boolean is_destructible;
9993       int ex = x + xx - 1;
9994       int ey = y + yy - 1;
9995       int content_element = change->target_content.e[xx][yy];
9996       int e;
9997
9998       can_replace[xx][yy] = TRUE;
9999
10000       if (ex == x && ey == y)   /* do not check changing element itself */
10001         continue;
10002
10003       if (content_element == EL_EMPTY_SPACE)
10004       {
10005         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10006
10007         continue;
10008       }
10009
10010       if (!IN_LEV_FIELD(ex, ey))
10011       {
10012         can_replace[xx][yy] = FALSE;
10013         complete_replace = FALSE;
10014
10015         continue;
10016       }
10017
10018       e = Feld[ex][ey];
10019
10020       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10021         e = MovingOrBlocked2Element(ex, ey);
10022
10023       is_empty = (IS_FREE(ex, ey) ||
10024                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10025
10026       is_walkable     = (is_empty || IS_WALKABLE(e));
10027       is_diggable     = (is_empty || IS_DIGGABLE(e));
10028       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10029       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10030       is_removable    = (is_diggable || is_collectible);
10031
10032       can_replace[xx][yy] =
10033         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10034           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10035           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10036           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10037           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10038           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10039          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10040
10041       if (!can_replace[xx][yy])
10042         complete_replace = FALSE;
10043     }
10044
10045     if (!change->only_if_complete || complete_replace)
10046     {
10047       boolean something_has_changed = FALSE;
10048
10049       if (change->only_if_complete && change->use_random_replace &&
10050           RND(100) < change->random_percentage)
10051         return FALSE;
10052
10053       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10054       {
10055         int ex = x + xx - 1;
10056         int ey = y + yy - 1;
10057         int content_element;
10058
10059         if (can_replace[xx][yy] && (!change->use_random_replace ||
10060                                     RND(100) < change->random_percentage))
10061         {
10062           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10063             RemoveMovingField(ex, ey);
10064
10065           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10066
10067           content_element = change->target_content.e[xx][yy];
10068           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10069                                               ce_value, ce_score);
10070
10071           CreateElementFromChange(ex, ey, target_element);
10072
10073           something_has_changed = TRUE;
10074
10075           /* for symmetry reasons, freeze newly created border elements */
10076           if (ex != x || ey != y)
10077             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10078         }
10079       }
10080
10081       if (something_has_changed)
10082       {
10083         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10084         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10085       }
10086     }
10087   }
10088   else
10089   {
10090     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10091                                         ce_value, ce_score);
10092
10093     if (element == EL_DIAGONAL_GROWING ||
10094         element == EL_DIAGONAL_SHRINKING)
10095     {
10096       target_element = Store[x][y];
10097
10098       Store[x][y] = EL_EMPTY;
10099     }
10100
10101     CreateElementFromChange(x, y, target_element);
10102
10103     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10104     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10105   }
10106
10107   /* this uses direct change before indirect change */
10108   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10109
10110   return TRUE;
10111 }
10112
10113 #if USE_NEW_DELAYED_ACTION
10114
10115 static void HandleElementChange(int x, int y, int page)
10116 {
10117   int element = MovingOrBlocked2Element(x, y);
10118   struct ElementInfo *ei = &element_info[element];
10119   struct ElementChangeInfo *change = &ei->change_page[page];
10120
10121 #ifdef DEBUG
10122   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10123       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10124   {
10125     printf("\n\n");
10126     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10127            x, y, element, element_info[element].token_name);
10128     printf("HandleElementChange(): This should never happen!\n");
10129     printf("\n\n");
10130   }
10131 #endif
10132
10133   /* this can happen with classic bombs on walkable, changing elements */
10134   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10135   {
10136 #if 0
10137     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10138       ChangeDelay[x][y] = 0;
10139 #endif
10140
10141     return;
10142   }
10143
10144   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10145   {
10146     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10147
10148     if (change->can_change)
10149     {
10150 #if 1
10151       /* !!! not clear why graphic animation should be reset at all here !!! */
10152       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10153 #if USE_GFX_RESET_WHEN_NOT_MOVING
10154       /* when a custom element is about to change (for example by change delay),
10155          do not reset graphic animation when the custom element is moving */
10156       if (!IS_MOVING(x, y))
10157 #endif
10158       {
10159         ResetGfxAnimation(x, y);
10160         ResetRandomAnimationValue(x, y);
10161       }
10162 #endif
10163
10164       if (change->pre_change_function)
10165         change->pre_change_function(x, y);
10166     }
10167   }
10168
10169   ChangeDelay[x][y]--;
10170
10171   if (ChangeDelay[x][y] != 0)           /* continue element change */
10172   {
10173     if (change->can_change)
10174     {
10175       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10176
10177       if (IS_ANIMATED(graphic))
10178         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10179
10180       if (change->change_function)
10181         change->change_function(x, y);
10182     }
10183   }
10184   else                                  /* finish element change */
10185   {
10186     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10187     {
10188       page = ChangePage[x][y];
10189       ChangePage[x][y] = -1;
10190
10191       change = &ei->change_page[page];
10192     }
10193
10194     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10195     {
10196       ChangeDelay[x][y] = 1;            /* try change after next move step */
10197       ChangePage[x][y] = page;          /* remember page to use for change */
10198
10199       return;
10200     }
10201
10202     if (change->can_change)
10203     {
10204       if (ChangeElement(x, y, element, page))
10205       {
10206         if (change->post_change_function)
10207           change->post_change_function(x, y);
10208       }
10209     }
10210
10211     if (change->has_action)
10212       ExecuteCustomElementAction(x, y, element, page);
10213   }
10214 }
10215
10216 #else
10217
10218 static void HandleElementChange(int x, int y, int page)
10219 {
10220   int element = MovingOrBlocked2Element(x, y);
10221   struct ElementInfo *ei = &element_info[element];
10222   struct ElementChangeInfo *change = &ei->change_page[page];
10223
10224 #ifdef DEBUG
10225   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10226   {
10227     printf("\n\n");
10228     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10229            x, y, element, element_info[element].token_name);
10230     printf("HandleElementChange(): This should never happen!\n");
10231     printf("\n\n");
10232   }
10233 #endif
10234
10235   /* this can happen with classic bombs on walkable, changing elements */
10236   if (!CAN_CHANGE(element))
10237   {
10238 #if 0
10239     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10240       ChangeDelay[x][y] = 0;
10241 #endif
10242
10243     return;
10244   }
10245
10246   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10247   {
10248     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10249
10250     ResetGfxAnimation(x, y);
10251     ResetRandomAnimationValue(x, y);
10252
10253     if (change->pre_change_function)
10254       change->pre_change_function(x, y);
10255   }
10256
10257   ChangeDelay[x][y]--;
10258
10259   if (ChangeDelay[x][y] != 0)           /* continue element change */
10260   {
10261     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10262
10263     if (IS_ANIMATED(graphic))
10264       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10265
10266     if (change->change_function)
10267       change->change_function(x, y);
10268   }
10269   else                                  /* finish element change */
10270   {
10271     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10272     {
10273       page = ChangePage[x][y];
10274       ChangePage[x][y] = -1;
10275
10276       change = &ei->change_page[page];
10277     }
10278
10279     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10280     {
10281       ChangeDelay[x][y] = 1;            /* try change after next move step */
10282       ChangePage[x][y] = page;          /* remember page to use for change */
10283
10284       return;
10285     }
10286
10287     if (ChangeElement(x, y, element, page))
10288     {
10289       if (change->post_change_function)
10290         change->post_change_function(x, y);
10291     }
10292   }
10293 }
10294
10295 #endif
10296
10297 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10298                                               int trigger_element,
10299                                               int trigger_event,
10300                                               int trigger_player,
10301                                               int trigger_side,
10302                                               int trigger_page)
10303 {
10304   boolean change_done_any = FALSE;
10305   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10306   int i;
10307
10308   if (!(trigger_events[trigger_element][trigger_event]))
10309     return FALSE;
10310
10311 #if 0
10312   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10313          trigger_event, recursion_loop_depth, recursion_loop_detected,
10314          recursion_loop_element, EL_NAME(recursion_loop_element));
10315 #endif
10316
10317   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10318
10319   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10320   {
10321     int element = EL_CUSTOM_START + i;
10322     boolean change_done = FALSE;
10323     int p;
10324
10325     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10326         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327       continue;
10328
10329     for (p = 0; p < element_info[element].num_change_pages; p++)
10330     {
10331       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10332
10333       if (change->can_change_or_has_action &&
10334           change->has_event[trigger_event] &&
10335           change->trigger_side & trigger_side &&
10336           change->trigger_player & trigger_player &&
10337           change->trigger_page & trigger_page_bits &&
10338           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10339       {
10340         change->actual_trigger_element = trigger_element;
10341         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10342         change->actual_trigger_side = trigger_side;
10343         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10344         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10345
10346         if ((change->can_change && !change_done) || change->has_action)
10347         {
10348           int x, y;
10349
10350           SCAN_PLAYFIELD(x, y)
10351           {
10352             if (Feld[x][y] == element)
10353             {
10354               if (change->can_change && !change_done)
10355               {
10356                 ChangeDelay[x][y] = 1;
10357                 ChangeEvent[x][y] = trigger_event;
10358
10359                 HandleElementChange(x, y, p);
10360               }
10361 #if USE_NEW_DELAYED_ACTION
10362               else if (change->has_action)
10363               {
10364                 ExecuteCustomElementAction(x, y, element, p);
10365                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10366               }
10367 #else
10368               if (change->has_action)
10369               {
10370                 ExecuteCustomElementAction(x, y, element, p);
10371                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10372               }
10373 #endif
10374             }
10375           }
10376
10377           if (change->can_change)
10378           {
10379             change_done = TRUE;
10380             change_done_any = TRUE;
10381           }
10382         }
10383       }
10384     }
10385   }
10386
10387   RECURSION_LOOP_DETECTION_END();
10388
10389   return change_done_any;
10390 }
10391
10392 static boolean CheckElementChangeExt(int x, int y,
10393                                      int element,
10394                                      int trigger_element,
10395                                      int trigger_event,
10396                                      int trigger_player,
10397                                      int trigger_side)
10398 {
10399   boolean change_done = FALSE;
10400   int p;
10401
10402   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10403       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10404     return FALSE;
10405
10406   if (Feld[x][y] == EL_BLOCKED)
10407   {
10408     Blocked2Moving(x, y, &x, &y);
10409     element = Feld[x][y];
10410   }
10411
10412 #if 0
10413   /* check if element has already changed */
10414   if (Feld[x][y] != element)
10415     return FALSE;
10416 #else
10417   /* check if element has already changed or is about to change after moving */
10418   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10419        Feld[x][y] != element) ||
10420
10421       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10422        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10423         ChangePage[x][y] != -1)))
10424     return FALSE;
10425 #endif
10426
10427 #if 0
10428   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10429          trigger_event, recursion_loop_depth, recursion_loop_detected,
10430          recursion_loop_element, EL_NAME(recursion_loop_element));
10431 #endif
10432
10433   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10434
10435   for (p = 0; p < element_info[element].num_change_pages; p++)
10436   {
10437     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10438
10439     /* check trigger element for all events where the element that is checked
10440        for changing interacts with a directly adjacent element -- this is
10441        different to element changes that affect other elements to change on the
10442        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10443     boolean check_trigger_element =
10444       (trigger_event == CE_TOUCHING_X ||
10445        trigger_event == CE_HITTING_X ||
10446        trigger_event == CE_HIT_BY_X ||
10447 #if 1
10448        /* this one was forgotten until 3.2.3 */
10449        trigger_event == CE_DIGGING_X);
10450 #endif
10451
10452     if (change->can_change_or_has_action &&
10453         change->has_event[trigger_event] &&
10454         change->trigger_side & trigger_side &&
10455         change->trigger_player & trigger_player &&
10456         (!check_trigger_element ||
10457          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10458     {
10459       change->actual_trigger_element = trigger_element;
10460       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10461       change->actual_trigger_side = trigger_side;
10462       change->actual_trigger_ce_value = CustomValue[x][y];
10463       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10464
10465       /* special case: trigger element not at (x,y) position for some events */
10466       if (check_trigger_element)
10467       {
10468         static struct
10469         {
10470           int dx, dy;
10471         } move_xy[] =
10472           {
10473             {  0,  0 },
10474             { -1,  0 },
10475             { +1,  0 },
10476             {  0,  0 },
10477             {  0, -1 },
10478             {  0,  0 }, { 0, 0 }, { 0, 0 },
10479             {  0, +1 }
10480           };
10481
10482         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10483         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10484
10485         change->actual_trigger_ce_value = CustomValue[xx][yy];
10486         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10487       }
10488
10489       if (change->can_change && !change_done)
10490       {
10491         ChangeDelay[x][y] = 1;
10492         ChangeEvent[x][y] = trigger_event;
10493
10494         HandleElementChange(x, y, p);
10495
10496         change_done = TRUE;
10497       }
10498 #if USE_NEW_DELAYED_ACTION
10499       else if (change->has_action)
10500       {
10501         ExecuteCustomElementAction(x, y, element, p);
10502         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10503       }
10504 #else
10505       if (change->has_action)
10506       {
10507         ExecuteCustomElementAction(x, y, element, p);
10508         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10509       }
10510 #endif
10511     }
10512   }
10513
10514   RECURSION_LOOP_DETECTION_END();
10515
10516   return change_done;
10517 }
10518
10519 static void PlayPlayerSound(struct PlayerInfo *player)
10520 {
10521   int jx = player->jx, jy = player->jy;
10522   int sound_element = player->artwork_element;
10523   int last_action = player->last_action_waiting;
10524   int action = player->action_waiting;
10525
10526   if (player->is_waiting)
10527   {
10528     if (action != last_action)
10529       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10530     else
10531       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10532   }
10533   else
10534   {
10535     if (action != last_action)
10536       StopSound(element_info[sound_element].sound[last_action]);
10537
10538     if (last_action == ACTION_SLEEPING)
10539       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10540   }
10541 }
10542
10543 static void PlayAllPlayersSound()
10544 {
10545   int i;
10546
10547   for (i = 0; i < MAX_PLAYERS; i++)
10548     if (stored_player[i].active)
10549       PlayPlayerSound(&stored_player[i]);
10550 }
10551
10552 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10553 {
10554   boolean last_waiting = player->is_waiting;
10555   int move_dir = player->MovDir;
10556
10557   player->dir_waiting = move_dir;
10558   player->last_action_waiting = player->action_waiting;
10559
10560   if (is_waiting)
10561   {
10562     if (!last_waiting)          /* not waiting -> waiting */
10563     {
10564       player->is_waiting = TRUE;
10565
10566       player->frame_counter_bored =
10567         FrameCounter +
10568         game.player_boring_delay_fixed +
10569         GetSimpleRandom(game.player_boring_delay_random);
10570       player->frame_counter_sleeping =
10571         FrameCounter +
10572         game.player_sleeping_delay_fixed +
10573         GetSimpleRandom(game.player_sleeping_delay_random);
10574
10575       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10576     }
10577
10578     if (game.player_sleeping_delay_fixed +
10579         game.player_sleeping_delay_random > 0 &&
10580         player->anim_delay_counter == 0 &&
10581         player->post_delay_counter == 0 &&
10582         FrameCounter >= player->frame_counter_sleeping)
10583       player->is_sleeping = TRUE;
10584     else if (game.player_boring_delay_fixed +
10585              game.player_boring_delay_random > 0 &&
10586              FrameCounter >= player->frame_counter_bored)
10587       player->is_bored = TRUE;
10588
10589     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10590                               player->is_bored ? ACTION_BORING :
10591                               ACTION_WAITING);
10592
10593     if (player->is_sleeping && player->use_murphy)
10594     {
10595       /* special case for sleeping Murphy when leaning against non-free tile */
10596
10597       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10598           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10599            !IS_MOVING(player->jx - 1, player->jy)))
10600         move_dir = MV_LEFT;
10601       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10602                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10603                 !IS_MOVING(player->jx + 1, player->jy)))
10604         move_dir = MV_RIGHT;
10605       else
10606         player->is_sleeping = FALSE;
10607
10608       player->dir_waiting = move_dir;
10609     }
10610
10611     if (player->is_sleeping)
10612     {
10613       if (player->num_special_action_sleeping > 0)
10614       {
10615         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10616         {
10617           int last_special_action = player->special_action_sleeping;
10618           int num_special_action = player->num_special_action_sleeping;
10619           int special_action =
10620             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10621              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10622              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10623              last_special_action + 1 : ACTION_SLEEPING);
10624           int special_graphic =
10625             el_act_dir2img(player->artwork_element, special_action, move_dir);
10626
10627           player->anim_delay_counter =
10628             graphic_info[special_graphic].anim_delay_fixed +
10629             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10630           player->post_delay_counter =
10631             graphic_info[special_graphic].post_delay_fixed +
10632             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10633
10634           player->special_action_sleeping = special_action;
10635         }
10636
10637         if (player->anim_delay_counter > 0)
10638         {
10639           player->action_waiting = player->special_action_sleeping;
10640           player->anim_delay_counter--;
10641         }
10642         else if (player->post_delay_counter > 0)
10643         {
10644           player->post_delay_counter--;
10645         }
10646       }
10647     }
10648     else if (player->is_bored)
10649     {
10650       if (player->num_special_action_bored > 0)
10651       {
10652         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10653         {
10654           int special_action =
10655             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10656           int special_graphic =
10657             el_act_dir2img(player->artwork_element, special_action, move_dir);
10658
10659           player->anim_delay_counter =
10660             graphic_info[special_graphic].anim_delay_fixed +
10661             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10662           player->post_delay_counter =
10663             graphic_info[special_graphic].post_delay_fixed +
10664             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10665
10666           player->special_action_bored = special_action;
10667         }
10668
10669         if (player->anim_delay_counter > 0)
10670         {
10671           player->action_waiting = player->special_action_bored;
10672           player->anim_delay_counter--;
10673         }
10674         else if (player->post_delay_counter > 0)
10675         {
10676           player->post_delay_counter--;
10677         }
10678       }
10679     }
10680   }
10681   else if (last_waiting)        /* waiting -> not waiting */
10682   {
10683     player->is_waiting = FALSE;
10684     player->is_bored = FALSE;
10685     player->is_sleeping = FALSE;
10686
10687     player->frame_counter_bored = -1;
10688     player->frame_counter_sleeping = -1;
10689
10690     player->anim_delay_counter = 0;
10691     player->post_delay_counter = 0;
10692
10693     player->dir_waiting = player->MovDir;
10694     player->action_waiting = ACTION_DEFAULT;
10695
10696     player->special_action_bored = ACTION_DEFAULT;
10697     player->special_action_sleeping = ACTION_DEFAULT;
10698   }
10699 }
10700
10701 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10702 {
10703   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10704   int left      = player_action & JOY_LEFT;
10705   int right     = player_action & JOY_RIGHT;
10706   int up        = player_action & JOY_UP;
10707   int down      = player_action & JOY_DOWN;
10708   int button1   = player_action & JOY_BUTTON_1;
10709   int button2   = player_action & JOY_BUTTON_2;
10710   int dx        = (left ? -1 : right ? 1 : 0);
10711   int dy        = (up   ? -1 : down  ? 1 : 0);
10712
10713   if (!player->active || tape.pausing)
10714     return 0;
10715
10716   if (player_action)
10717   {
10718     if (button1)
10719       snapped = SnapField(player, dx, dy);
10720     else
10721     {
10722       if (button2)
10723         dropped = DropElement(player);
10724
10725       moved = MovePlayer(player, dx, dy);
10726     }
10727
10728     if (tape.single_step && tape.recording && !tape.pausing)
10729     {
10730       if (button1 || (dropped && !moved))
10731       {
10732         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10733         SnapField(player, 0, 0);                /* stop snapping */
10734       }
10735     }
10736
10737     SetPlayerWaiting(player, FALSE);
10738
10739     return player_action;
10740   }
10741   else
10742   {
10743     /* no actions for this player (no input at player's configured device) */
10744
10745     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10746     SnapField(player, 0, 0);
10747     CheckGravityMovementWhenNotMoving(player);
10748
10749     if (player->MovPos == 0)
10750       SetPlayerWaiting(player, TRUE);
10751
10752     if (player->MovPos == 0)    /* needed for tape.playing */
10753       player->is_moving = FALSE;
10754
10755     player->is_dropping = FALSE;
10756     player->is_dropping_pressed = FALSE;
10757     player->drop_pressed_delay = 0;
10758
10759     return 0;
10760   }
10761 }
10762
10763 static void CheckLevelTime()
10764 {
10765   int i;
10766
10767   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10768   {
10769     if (level.native_em_level->lev->home == 0)  /* all players at home */
10770     {
10771       PlayerWins(local_player);
10772
10773       AllPlayersGone = TRUE;
10774
10775       level.native_em_level->lev->home = -1;
10776     }
10777
10778     if (level.native_em_level->ply[0]->alive == 0 &&
10779         level.native_em_level->ply[1]->alive == 0 &&
10780         level.native_em_level->ply[2]->alive == 0 &&
10781         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10782       AllPlayersGone = TRUE;
10783   }
10784
10785   if (TimeFrames >= FRAMES_PER_SECOND)
10786   {
10787     TimeFrames = 0;
10788     TapeTime++;
10789
10790     for (i = 0; i < MAX_PLAYERS; i++)
10791     {
10792       struct PlayerInfo *player = &stored_player[i];
10793
10794       if (SHIELD_ON(player))
10795       {
10796         player->shield_normal_time_left--;
10797
10798         if (player->shield_deadly_time_left > 0)
10799           player->shield_deadly_time_left--;
10800       }
10801     }
10802
10803     if (!local_player->LevelSolved && !level.use_step_counter)
10804     {
10805       TimePlayed++;
10806
10807       if (TimeLeft > 0)
10808       {
10809         TimeLeft--;
10810
10811         if (TimeLeft <= 10 && setup.time_limit)
10812           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10813
10814 #if 1
10815         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10816
10817         DisplayGameControlValues();
10818 #else
10819         DrawGameValue_Time(TimeLeft);
10820 #endif
10821
10822         if (!TimeLeft && setup.time_limit)
10823         {
10824           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10825             level.native_em_level->lev->killed_out_of_time = TRUE;
10826           else
10827             for (i = 0; i < MAX_PLAYERS; i++)
10828               KillPlayer(&stored_player[i]);
10829         }
10830       }
10831 #if 1
10832       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10833       {
10834         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10835
10836         DisplayGameControlValues();
10837       }
10838 #else
10839       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10840         DrawGameValue_Time(TimePlayed);
10841 #endif
10842
10843       level.native_em_level->lev->time =
10844         (level.time == 0 ? TimePlayed : TimeLeft);
10845     }
10846
10847     if (tape.recording || tape.playing)
10848       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10849   }
10850
10851   DrawGameDoorValues();
10852 }
10853
10854 void AdvanceFrameAndPlayerCounters(int player_nr)
10855 {
10856   int i;
10857
10858   /* advance frame counters (global frame counter and time frame counter) */
10859   FrameCounter++;
10860   TimeFrames++;
10861
10862   /* advance player counters (counters for move delay, move animation etc.) */
10863   for (i = 0; i < MAX_PLAYERS; i++)
10864   {
10865     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10866     int move_delay_value = stored_player[i].move_delay_value;
10867     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10868
10869     if (!advance_player_counters)       /* not all players may be affected */
10870       continue;
10871
10872 #if USE_NEW_PLAYER_ANIM
10873     if (move_frames == 0)       /* less than one move per game frame */
10874     {
10875       int stepsize = TILEX / move_delay_value;
10876       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10877       int count = (stored_player[i].is_moving ?
10878                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10879
10880       if (count % delay == 0)
10881         move_frames = 1;
10882     }
10883 #endif
10884
10885     stored_player[i].Frame += move_frames;
10886
10887     if (stored_player[i].MovPos != 0)
10888       stored_player[i].StepFrame += move_frames;
10889
10890     if (stored_player[i].move_delay > 0)
10891       stored_player[i].move_delay--;
10892
10893     /* due to bugs in previous versions, counter must count up, not down */
10894     if (stored_player[i].push_delay != -1)
10895       stored_player[i].push_delay++;
10896
10897     if (stored_player[i].drop_delay > 0)
10898       stored_player[i].drop_delay--;
10899
10900     if (stored_player[i].is_dropping_pressed)
10901       stored_player[i].drop_pressed_delay++;
10902   }
10903 }
10904
10905 void StartGameActions(boolean init_network_game, boolean record_tape,
10906                       long random_seed)
10907 {
10908   unsigned long new_random_seed = InitRND(random_seed);
10909
10910   if (record_tape)
10911     TapeStartRecording(new_random_seed);
10912
10913 #if defined(NETWORK_AVALIABLE)
10914   if (init_network_game)
10915   {
10916     SendToServer_StartPlaying();
10917
10918     return;
10919   }
10920 #endif
10921
10922   InitGame();
10923 }
10924
10925 void GameActions()
10926 {
10927   static unsigned long game_frame_delay = 0;
10928   unsigned long game_frame_delay_value;
10929   byte *recorded_player_action;
10930   byte summarized_player_action = 0;
10931   byte tape_action[MAX_PLAYERS];
10932   int i;
10933
10934   /* detect endless loops, caused by custom element programming */
10935   if (recursion_loop_detected && recursion_loop_depth == 0)
10936   {
10937     char *message = getStringCat3("Internal Error ! Element ",
10938                                   EL_NAME(recursion_loop_element),
10939                                   " caused endless loop ! Quit the game ?");
10940
10941     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10942           EL_NAME(recursion_loop_element));
10943
10944     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10945
10946     recursion_loop_detected = FALSE;    /* if game should be continued */
10947
10948     free(message);
10949
10950     return;
10951   }
10952
10953   if (game.restart_level)
10954     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10955
10956   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10957   {
10958     if (level.native_em_level->lev->home == 0)  /* all players at home */
10959     {
10960       PlayerWins(local_player);
10961
10962       AllPlayersGone = TRUE;
10963
10964       level.native_em_level->lev->home = -1;
10965     }
10966
10967     if (level.native_em_level->ply[0]->alive == 0 &&
10968         level.native_em_level->ply[1]->alive == 0 &&
10969         level.native_em_level->ply[2]->alive == 0 &&
10970         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10971       AllPlayersGone = TRUE;
10972   }
10973
10974   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10975     GameWon();
10976
10977   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10978     TapeStop();
10979
10980   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10981     return;
10982
10983   game_frame_delay_value =
10984     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10985
10986   if (tape.playing && tape.warp_forward && !tape.pausing)
10987     game_frame_delay_value = 0;
10988
10989   /* ---------- main game synchronization point ---------- */
10990
10991   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10992
10993   if (network_playing && !network_player_action_received)
10994   {
10995     /* try to get network player actions in time */
10996
10997 #if defined(NETWORK_AVALIABLE)
10998     /* last chance to get network player actions without main loop delay */
10999     HandleNetworking();
11000 #endif
11001
11002     /* game was quit by network peer */
11003     if (game_status != GAME_MODE_PLAYING)
11004       return;
11005
11006     if (!network_player_action_received)
11007       return;           /* failed to get network player actions in time */
11008
11009     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11010   }
11011
11012   if (tape.pausing)
11013     return;
11014
11015   /* at this point we know that we really continue executing the game */
11016
11017   network_player_action_received = FALSE;
11018
11019   /* when playing tape, read previously recorded player input from tape data */
11020   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11021
11022 #if 1
11023   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11024   if (tape.pausing)
11025     return;
11026 #endif
11027
11028   if (tape.set_centered_player)
11029   {
11030     game.centered_player_nr_next = tape.centered_player_nr_next;
11031     game.set_centered_player = TRUE;
11032   }
11033
11034   for (i = 0; i < MAX_PLAYERS; i++)
11035   {
11036     summarized_player_action |= stored_player[i].action;
11037
11038     if (!network_playing)
11039       stored_player[i].effective_action = stored_player[i].action;
11040   }
11041
11042 #if defined(NETWORK_AVALIABLE)
11043   if (network_playing)
11044     SendToServer_MovePlayer(summarized_player_action);
11045 #endif
11046
11047   if (!options.network && !setup.team_mode)
11048     local_player->effective_action = summarized_player_action;
11049
11050   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11051   {
11052     for (i = 0; i < MAX_PLAYERS; i++)
11053       stored_player[i].effective_action =
11054         (i == game.centered_player_nr ? summarized_player_action : 0);
11055   }
11056
11057   if (recorded_player_action != NULL)
11058     for (i = 0; i < MAX_PLAYERS; i++)
11059       stored_player[i].effective_action = recorded_player_action[i];
11060
11061   for (i = 0; i < MAX_PLAYERS; i++)
11062   {
11063     tape_action[i] = stored_player[i].effective_action;
11064
11065     /* (this can only happen in the R'n'D game engine) */
11066     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11067       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11068   }
11069
11070   /* only record actions from input devices, but not programmed actions */
11071   if (tape.recording)
11072     TapeRecordAction(tape_action);
11073
11074   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11075   {
11076     GameActions_EM_Main();
11077   }
11078   else
11079   {
11080     GameActions_RND();
11081   }
11082 }
11083
11084 void GameActions_EM_Main()
11085 {
11086   byte effective_action[MAX_PLAYERS];
11087   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11088   int i;
11089
11090   for (i = 0; i < MAX_PLAYERS; i++)
11091     effective_action[i] = stored_player[i].effective_action;
11092
11093   GameActions_EM(effective_action, warp_mode);
11094
11095   CheckLevelTime();
11096
11097   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11098 }
11099
11100 void GameActions_RND()
11101 {
11102   int magic_wall_x = 0, magic_wall_y = 0;
11103   int i, x, y, element, graphic;
11104
11105   InitPlayfieldScanModeVars();
11106
11107 #if USE_ONE_MORE_CHANGE_PER_FRAME
11108   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11109   {
11110     SCAN_PLAYFIELD(x, y)
11111     {
11112       ChangeCount[x][y] = 0;
11113       ChangeEvent[x][y] = -1;
11114     }
11115   }
11116 #endif
11117
11118   if (game.set_centered_player)
11119   {
11120     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11121
11122     /* switching to "all players" only possible if all players fit to screen */
11123     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11124     {
11125       game.centered_player_nr_next = game.centered_player_nr;
11126       game.set_centered_player = FALSE;
11127     }
11128
11129     /* do not switch focus to non-existing (or non-active) player */
11130     if (game.centered_player_nr_next >= 0 &&
11131         !stored_player[game.centered_player_nr_next].active)
11132     {
11133       game.centered_player_nr_next = game.centered_player_nr;
11134       game.set_centered_player = FALSE;
11135     }
11136   }
11137
11138   if (game.set_centered_player &&
11139       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11140   {
11141     int sx, sy;
11142
11143     if (game.centered_player_nr_next == -1)
11144     {
11145       setScreenCenteredToAllPlayers(&sx, &sy);
11146     }
11147     else
11148     {
11149       sx = stored_player[game.centered_player_nr_next].jx;
11150       sy = stored_player[game.centered_player_nr_next].jy;
11151     }
11152
11153     game.centered_player_nr = game.centered_player_nr_next;
11154     game.set_centered_player = FALSE;
11155
11156     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11157     DrawGameDoorValues();
11158   }
11159
11160   for (i = 0; i < MAX_PLAYERS; i++)
11161   {
11162     int actual_player_action = stored_player[i].effective_action;
11163
11164 #if 1
11165     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11166        - rnd_equinox_tetrachloride 048
11167        - rnd_equinox_tetrachloride_ii 096
11168        - rnd_emanuel_schmieg 002
11169        - doctor_sloan_ww 001, 020
11170     */
11171     if (stored_player[i].MovPos == 0)
11172       CheckGravityMovement(&stored_player[i]);
11173 #endif
11174
11175     /* overwrite programmed action with tape action */
11176     if (stored_player[i].programmed_action)
11177       actual_player_action = stored_player[i].programmed_action;
11178
11179     PlayerActions(&stored_player[i], actual_player_action);
11180
11181     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11182   }
11183
11184   ScrollScreen(NULL, SCROLL_GO_ON);
11185
11186   /* for backwards compatibility, the following code emulates a fixed bug that
11187      occured when pushing elements (causing elements that just made their last
11188      pushing step to already (if possible) make their first falling step in the
11189      same game frame, which is bad); this code is also needed to use the famous
11190      "spring push bug" which is used in older levels and might be wanted to be
11191      used also in newer levels, but in this case the buggy pushing code is only
11192      affecting the "spring" element and no other elements */
11193
11194   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11195   {
11196     for (i = 0; i < MAX_PLAYERS; i++)
11197     {
11198       struct PlayerInfo *player = &stored_player[i];
11199       int x = player->jx;
11200       int y = player->jy;
11201
11202       if (player->active && player->is_pushing && player->is_moving &&
11203           IS_MOVING(x, y) &&
11204           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11205            Feld[x][y] == EL_SPRING))
11206       {
11207         ContinueMoving(x, y);
11208
11209         /* continue moving after pushing (this is actually a bug) */
11210         if (!IS_MOVING(x, y))
11211           Stop[x][y] = FALSE;
11212       }
11213     }
11214   }
11215
11216 #if 0
11217   debug_print_timestamp(0, "start main loop profiling");
11218 #endif
11219
11220   SCAN_PLAYFIELD(x, y)
11221   {
11222     ChangeCount[x][y] = 0;
11223     ChangeEvent[x][y] = -1;
11224
11225     /* this must be handled before main playfield loop */
11226     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11227     {
11228       MovDelay[x][y]--;
11229       if (MovDelay[x][y] <= 0)
11230         RemoveField(x, y);
11231     }
11232
11233 #if USE_NEW_SNAP_DELAY
11234     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11235     {
11236       MovDelay[x][y]--;
11237       if (MovDelay[x][y] <= 0)
11238       {
11239         RemoveField(x, y);
11240         DrawLevelField(x, y);
11241
11242         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11243       }
11244     }
11245 #endif
11246
11247 #if DEBUG
11248     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11249     {
11250       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11251       printf("GameActions(): This should never happen!\n");
11252
11253       ChangePage[x][y] = -1;
11254     }
11255 #endif
11256
11257     Stop[x][y] = FALSE;
11258     if (WasJustMoving[x][y] > 0)
11259       WasJustMoving[x][y]--;
11260     if (WasJustFalling[x][y] > 0)
11261       WasJustFalling[x][y]--;
11262     if (CheckCollision[x][y] > 0)
11263       CheckCollision[x][y]--;
11264     if (CheckImpact[x][y] > 0)
11265       CheckImpact[x][y]--;
11266
11267     GfxFrame[x][y]++;
11268
11269     /* reset finished pushing action (not done in ContinueMoving() to allow
11270        continuous pushing animation for elements with zero push delay) */
11271     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11272     {
11273       ResetGfxAnimation(x, y);
11274       DrawLevelField(x, y);
11275     }
11276
11277 #if DEBUG
11278     if (IS_BLOCKED(x, y))
11279     {
11280       int oldx, oldy;
11281
11282       Blocked2Moving(x, y, &oldx, &oldy);
11283       if (!IS_MOVING(oldx, oldy))
11284       {
11285         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11286         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11287         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11288         printf("GameActions(): This should never happen!\n");
11289       }
11290     }
11291 #endif
11292   }
11293
11294 #if 0
11295   debug_print_timestamp(0, "- time for pre-main loop:");
11296 #endif
11297
11298 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11299   SCAN_PLAYFIELD(x, y)
11300   {
11301     element = Feld[x][y];
11302     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11303
11304 #if 1
11305     {
11306 #if 1
11307       int element2 = element;
11308       int graphic2 = graphic;
11309 #else
11310       int element2 = Feld[x][y];
11311       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11312 #endif
11313       int last_gfx_frame = GfxFrame[x][y];
11314
11315       if (graphic_info[graphic2].anim_global_sync)
11316         GfxFrame[x][y] = FrameCounter;
11317       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11318         GfxFrame[x][y] = CustomValue[x][y];
11319       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11320         GfxFrame[x][y] = element_info[element2].collect_score;
11321       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11322         GfxFrame[x][y] = ChangeDelay[x][y];
11323
11324       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11325         DrawLevelGraphicAnimation(x, y, graphic2);
11326     }
11327 #else
11328     ResetGfxFrame(x, y, TRUE);
11329 #endif
11330
11331 #if 1
11332     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11333         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11334       ResetRandomAnimationValue(x, y);
11335 #endif
11336
11337 #if 1
11338     SetRandomAnimationValue(x, y);
11339 #endif
11340
11341 #if 1
11342     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11343 #endif
11344   }
11345 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11346
11347 #if 0
11348   debug_print_timestamp(0, "- time for TEST loop:     -->");
11349 #endif
11350
11351   SCAN_PLAYFIELD(x, y)
11352   {
11353     element = Feld[x][y];
11354     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11355
11356     ResetGfxFrame(x, y, TRUE);
11357
11358     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11359         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11360       ResetRandomAnimationValue(x, y);
11361
11362     SetRandomAnimationValue(x, y);
11363
11364     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11365
11366     if (IS_INACTIVE(element))
11367     {
11368       if (IS_ANIMATED(graphic))
11369         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11370
11371       continue;
11372     }
11373
11374     /* this may take place after moving, so 'element' may have changed */
11375     if (IS_CHANGING(x, y) &&
11376         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11377     {
11378       int page = element_info[element].event_page_nr[CE_DELAY];
11379
11380 #if 1
11381       HandleElementChange(x, y, page);
11382 #else
11383       if (CAN_CHANGE(element))
11384         HandleElementChange(x, y, page);
11385
11386       if (HAS_ACTION(element))
11387         ExecuteCustomElementAction(x, y, element, page);
11388 #endif
11389
11390       element = Feld[x][y];
11391       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11392     }
11393
11394 #if 0   // ---------------------------------------------------------------------
11395
11396     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11397     {
11398       StartMoving(x, y);
11399
11400       element = Feld[x][y];
11401       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11402
11403       if (IS_ANIMATED(graphic) &&
11404           !IS_MOVING(x, y) &&
11405           !Stop[x][y])
11406         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11407
11408       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11409         DrawTwinkleOnField(x, y);
11410     }
11411     else if (IS_MOVING(x, y))
11412       ContinueMoving(x, y);
11413     else
11414     {
11415       switch (element)
11416       {
11417         case EL_ACID:
11418         case EL_EXIT_OPEN:
11419         case EL_EM_EXIT_OPEN:
11420         case EL_SP_EXIT_OPEN:
11421         case EL_STEEL_EXIT_OPEN:
11422         case EL_EM_STEEL_EXIT_OPEN:
11423         case EL_SP_TERMINAL:
11424         case EL_SP_TERMINAL_ACTIVE:
11425         case EL_EXTRA_TIME:
11426         case EL_SHIELD_NORMAL:
11427         case EL_SHIELD_DEADLY:
11428           if (IS_ANIMATED(graphic))
11429             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11430           break;
11431
11432         case EL_DYNAMITE_ACTIVE:
11433         case EL_EM_DYNAMITE_ACTIVE:
11434         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11435         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11436         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11437         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11438         case EL_SP_DISK_RED_ACTIVE:
11439           CheckDynamite(x, y);
11440           break;
11441
11442         case EL_AMOEBA_GROWING:
11443           AmoebeWaechst(x, y);
11444           break;
11445
11446         case EL_AMOEBA_SHRINKING:
11447           AmoebaDisappearing(x, y);
11448           break;
11449
11450 #if !USE_NEW_AMOEBA_CODE
11451         case EL_AMOEBA_WET:
11452         case EL_AMOEBA_DRY:
11453         case EL_AMOEBA_FULL:
11454         case EL_BD_AMOEBA:
11455         case EL_EMC_DRIPPER:
11456           AmoebeAbleger(x, y);
11457           break;
11458 #endif
11459
11460         case EL_GAME_OF_LIFE:
11461         case EL_BIOMAZE:
11462           Life(x, y);
11463           break;
11464
11465         case EL_EXIT_CLOSED:
11466           CheckExit(x, y);
11467           break;
11468
11469         case EL_EM_EXIT_CLOSED:
11470           CheckExitEM(x, y);
11471           break;
11472
11473         case EL_STEEL_EXIT_CLOSED:
11474           CheckExitSteel(x, y);
11475           break;
11476
11477         case EL_EM_STEEL_EXIT_CLOSED:
11478           CheckExitSteelEM(x, y);
11479           break;
11480
11481         case EL_SP_EXIT_CLOSED:
11482           CheckExitSP(x, y);
11483           break;
11484
11485         case EL_EXPANDABLE_WALL_GROWING:
11486         case EL_EXPANDABLE_STEELWALL_GROWING:
11487           MauerWaechst(x, y);
11488           break;
11489
11490         case EL_EXPANDABLE_WALL:
11491         case EL_EXPANDABLE_WALL_HORIZONTAL:
11492         case EL_EXPANDABLE_WALL_VERTICAL:
11493         case EL_EXPANDABLE_WALL_ANY:
11494         case EL_BD_EXPANDABLE_WALL:
11495           MauerAbleger(x, y);
11496           break;
11497
11498         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11499         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11500         case EL_EXPANDABLE_STEELWALL_ANY:
11501           MauerAblegerStahl(x, y);
11502           break;
11503
11504         case EL_FLAMES:
11505           CheckForDragon(x, y);
11506           break;
11507
11508         case EL_EXPLOSION:
11509           break;
11510
11511         case EL_ELEMENT_SNAPPING:
11512         case EL_DIAGONAL_SHRINKING:
11513         case EL_DIAGONAL_GROWING:
11514         {
11515           graphic =
11516             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11517
11518           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11519           break;
11520         }
11521
11522         default:
11523           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11524             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11525           break;
11526       }
11527     }
11528
11529 #else   // ---------------------------------------------------------------------
11530
11531     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11532     {
11533       StartMoving(x, y);
11534
11535       element = Feld[x][y];
11536       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11537
11538       if (IS_ANIMATED(graphic) &&
11539           !IS_MOVING(x, y) &&
11540           !Stop[x][y])
11541         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11542
11543       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11544         DrawTwinkleOnField(x, y);
11545     }
11546     else if ((element == EL_ACID ||
11547               element == EL_EXIT_OPEN ||
11548               element == EL_EM_EXIT_OPEN ||
11549               element == EL_SP_EXIT_OPEN ||
11550               element == EL_STEEL_EXIT_OPEN ||
11551               element == EL_EM_STEEL_EXIT_OPEN ||
11552               element == EL_SP_TERMINAL ||
11553               element == EL_SP_TERMINAL_ACTIVE ||
11554               element == EL_EXTRA_TIME ||
11555               element == EL_SHIELD_NORMAL ||
11556               element == EL_SHIELD_DEADLY) &&
11557              IS_ANIMATED(graphic))
11558       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11559     else if (IS_MOVING(x, y))
11560       ContinueMoving(x, y);
11561     else if (IS_ACTIVE_BOMB(element))
11562       CheckDynamite(x, y);
11563     else if (element == EL_AMOEBA_GROWING)
11564       AmoebeWaechst(x, y);
11565     else if (element == EL_AMOEBA_SHRINKING)
11566       AmoebaDisappearing(x, y);
11567
11568 #if !USE_NEW_AMOEBA_CODE
11569     else if (IS_AMOEBALIVE(element))
11570       AmoebeAbleger(x, y);
11571 #endif
11572
11573     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11574       Life(x, y);
11575     else if (element == EL_EXIT_CLOSED)
11576       CheckExit(x, y);
11577     else if (element == EL_EM_EXIT_CLOSED)
11578       CheckExitEM(x, y);
11579     else if (element == EL_STEEL_EXIT_CLOSED)
11580       CheckExitSteel(x, y);
11581     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11582       CheckExitSteelEM(x, y);
11583     else if (element == EL_SP_EXIT_CLOSED)
11584       CheckExitSP(x, y);
11585     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11586              element == EL_EXPANDABLE_STEELWALL_GROWING)
11587       MauerWaechst(x, y);
11588     else if (element == EL_EXPANDABLE_WALL ||
11589              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11590              element == EL_EXPANDABLE_WALL_VERTICAL ||
11591              element == EL_EXPANDABLE_WALL_ANY ||
11592              element == EL_BD_EXPANDABLE_WALL)
11593       MauerAbleger(x, y);
11594     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11595              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11596              element == EL_EXPANDABLE_STEELWALL_ANY)
11597       MauerAblegerStahl(x, y);
11598     else if (element == EL_FLAMES)
11599       CheckForDragon(x, y);
11600     else if (element == EL_EXPLOSION)
11601       ; /* drawing of correct explosion animation is handled separately */
11602     else if (element == EL_ELEMENT_SNAPPING ||
11603              element == EL_DIAGONAL_SHRINKING ||
11604              element == EL_DIAGONAL_GROWING)
11605     {
11606       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11607
11608       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11609     }
11610     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11611       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11612
11613 #endif  // ---------------------------------------------------------------------
11614
11615     if (IS_BELT_ACTIVE(element))
11616       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11617
11618     if (game.magic_wall_active)
11619     {
11620       int jx = local_player->jx, jy = local_player->jy;
11621
11622       /* play the element sound at the position nearest to the player */
11623       if ((element == EL_MAGIC_WALL_FULL ||
11624            element == EL_MAGIC_WALL_ACTIVE ||
11625            element == EL_MAGIC_WALL_EMPTYING ||
11626            element == EL_BD_MAGIC_WALL_FULL ||
11627            element == EL_BD_MAGIC_WALL_ACTIVE ||
11628            element == EL_BD_MAGIC_WALL_EMPTYING ||
11629            element == EL_DC_MAGIC_WALL_FULL ||
11630            element == EL_DC_MAGIC_WALL_ACTIVE ||
11631            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11632           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11633       {
11634         magic_wall_x = x;
11635         magic_wall_y = y;
11636       }
11637     }
11638   }
11639
11640 #if 0
11641   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11642 #endif
11643
11644 #if USE_NEW_AMOEBA_CODE
11645   /* new experimental amoeba growth stuff */
11646   if (!(FrameCounter % 8))
11647   {
11648     static unsigned long random = 1684108901;
11649
11650     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11651     {
11652       x = RND(lev_fieldx);
11653       y = RND(lev_fieldy);
11654       element = Feld[x][y];
11655
11656       if (!IS_PLAYER(x,y) &&
11657           (element == EL_EMPTY ||
11658            CAN_GROW_INTO(element) ||
11659            element == EL_QUICKSAND_EMPTY ||
11660            element == EL_QUICKSAND_FAST_EMPTY ||
11661            element == EL_ACID_SPLASH_LEFT ||
11662            element == EL_ACID_SPLASH_RIGHT))
11663       {
11664         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11665             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11666             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11667             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11668           Feld[x][y] = EL_AMOEBA_DROP;
11669       }
11670
11671       random = random * 129 + 1;
11672     }
11673   }
11674 #endif
11675
11676 #if 0
11677   if (game.explosions_delayed)
11678 #endif
11679   {
11680     game.explosions_delayed = FALSE;
11681
11682     SCAN_PLAYFIELD(x, y)
11683     {
11684       element = Feld[x][y];
11685
11686       if (ExplodeField[x][y])
11687         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11688       else if (element == EL_EXPLOSION)
11689         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11690
11691       ExplodeField[x][y] = EX_TYPE_NONE;
11692     }
11693
11694     game.explosions_delayed = TRUE;
11695   }
11696
11697   if (game.magic_wall_active)
11698   {
11699     if (!(game.magic_wall_time_left % 4))
11700     {
11701       int element = Feld[magic_wall_x][magic_wall_y];
11702
11703       if (element == EL_BD_MAGIC_WALL_FULL ||
11704           element == EL_BD_MAGIC_WALL_ACTIVE ||
11705           element == EL_BD_MAGIC_WALL_EMPTYING)
11706         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11707       else if (element == EL_DC_MAGIC_WALL_FULL ||
11708                element == EL_DC_MAGIC_WALL_ACTIVE ||
11709                element == EL_DC_MAGIC_WALL_EMPTYING)
11710         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11711       else
11712         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11713     }
11714
11715     if (game.magic_wall_time_left > 0)
11716     {
11717       game.magic_wall_time_left--;
11718       if (!game.magic_wall_time_left)
11719       {
11720         SCAN_PLAYFIELD(x, y)
11721         {
11722           element = Feld[x][y];
11723
11724           if (element == EL_MAGIC_WALL_ACTIVE ||
11725               element == EL_MAGIC_WALL_FULL)
11726           {
11727             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11728             DrawLevelField(x, y);
11729           }
11730           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11731                    element == EL_BD_MAGIC_WALL_FULL)
11732           {
11733             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11734             DrawLevelField(x, y);
11735           }
11736           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11737                    element == EL_DC_MAGIC_WALL_FULL)
11738           {
11739             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11740             DrawLevelField(x, y);
11741           }
11742         }
11743
11744         game.magic_wall_active = FALSE;
11745       }
11746     }
11747   }
11748
11749   if (game.light_time_left > 0)
11750   {
11751     game.light_time_left--;
11752
11753     if (game.light_time_left == 0)
11754       RedrawAllLightSwitchesAndInvisibleElements();
11755   }
11756
11757   if (game.timegate_time_left > 0)
11758   {
11759     game.timegate_time_left--;
11760
11761     if (game.timegate_time_left == 0)
11762       CloseAllOpenTimegates();
11763   }
11764
11765   if (game.lenses_time_left > 0)
11766   {
11767     game.lenses_time_left--;
11768
11769     if (game.lenses_time_left == 0)
11770       RedrawAllInvisibleElementsForLenses();
11771   }
11772
11773   if (game.magnify_time_left > 0)
11774   {
11775     game.magnify_time_left--;
11776
11777     if (game.magnify_time_left == 0)
11778       RedrawAllInvisibleElementsForMagnifier();
11779   }
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782   {
11783     struct PlayerInfo *player = &stored_player[i];
11784
11785     if (SHIELD_ON(player))
11786     {
11787       if (player->shield_deadly_time_left)
11788         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11789       else if (player->shield_normal_time_left)
11790         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11791     }
11792   }
11793
11794   CheckLevelTime();
11795
11796   DrawAllPlayers();
11797   PlayAllPlayersSound();
11798
11799   if (options.debug)                    /* calculate frames per second */
11800   {
11801     static unsigned long fps_counter = 0;
11802     static int fps_frames = 0;
11803     unsigned long fps_delay_ms = Counter() - fps_counter;
11804
11805     fps_frames++;
11806
11807     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11808     {
11809       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11810
11811       fps_frames = 0;
11812       fps_counter = Counter();
11813     }
11814
11815     redraw_mask |= REDRAW_FPS;
11816   }
11817
11818   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11819
11820   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11821   {
11822     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11823
11824     local_player->show_envelope = 0;
11825   }
11826
11827 #if 0
11828   debug_print_timestamp(0, "stop main loop profiling ");
11829   printf("----------------------------------------------------------\n");
11830 #endif
11831
11832   /* use random number generator in every frame to make it less predictable */
11833   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11834     RND(1);
11835 }
11836
11837 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11838 {
11839   int min_x = x, min_y = y, max_x = x, max_y = y;
11840   int i;
11841
11842   for (i = 0; i < MAX_PLAYERS; i++)
11843   {
11844     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11845
11846     if (!stored_player[i].active || &stored_player[i] == player)
11847       continue;
11848
11849     min_x = MIN(min_x, jx);
11850     min_y = MIN(min_y, jy);
11851     max_x = MAX(max_x, jx);
11852     max_y = MAX(max_y, jy);
11853   }
11854
11855   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11856 }
11857
11858 static boolean AllPlayersInVisibleScreen()
11859 {
11860   int i;
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863   {
11864     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11865
11866     if (!stored_player[i].active)
11867       continue;
11868
11869     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11870       return FALSE;
11871   }
11872
11873   return TRUE;
11874 }
11875
11876 void ScrollLevel(int dx, int dy)
11877 {
11878 #if 1
11879   static Bitmap *bitmap_db_field2 = NULL;
11880   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11881   int x, y;
11882 #else
11883   int i, x, y;
11884 #endif
11885
11886 #if 0
11887   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11888   /* only horizontal XOR vertical scroll direction allowed */
11889   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11890     return;
11891 #endif
11892
11893 #if 1
11894   if (bitmap_db_field2 == NULL)
11895     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11896
11897   /* needed when blitting directly to same bitmap -- should not be needed with
11898      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11899   BlitBitmap(drawto_field, bitmap_db_field2,
11900              FX + TILEX * (dx == -1) - softscroll_offset,
11901              FY + TILEY * (dy == -1) - softscroll_offset,
11902              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11903              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11904              FX + TILEX * (dx == 1) - softscroll_offset,
11905              FY + TILEY * (dy == 1) - softscroll_offset);
11906   BlitBitmap(bitmap_db_field2, drawto_field,
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
11914 #else
11915
11916 #if 1
11917   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11918   int xsize = (BX2 - BX1 + 1);
11919   int ysize = (BY2 - BY1 + 1);
11920   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11921   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11922   int step  = (start < end ? +1 : -1);
11923
11924   for (i = start; i != end; i += step)
11925   {
11926     BlitBitmap(drawto_field, drawto_field,
11927                FX + TILEX * (dx != 0 ? i + step : 0),
11928                FY + TILEY * (dy != 0 ? i + step : 0),
11929                TILEX * (dx != 0 ? 1 : xsize),
11930                TILEY * (dy != 0 ? 1 : ysize),
11931                FX + TILEX * (dx != 0 ? i : 0),
11932                FY + TILEY * (dy != 0 ? i : 0));
11933   }
11934
11935 #else
11936
11937   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11938
11939   BlitBitmap(drawto_field, drawto_field,
11940              FX + TILEX * (dx == -1) - softscroll_offset,
11941              FY + TILEY * (dy == -1) - softscroll_offset,
11942              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11943              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11944              FX + TILEX * (dx == 1) - softscroll_offset,
11945              FY + TILEY * (dy == 1) - softscroll_offset);
11946 #endif
11947 #endif
11948
11949   if (dx != 0)
11950   {
11951     x = (dx == 1 ? BX1 : BX2);
11952     for (y = BY1; y <= BY2; y++)
11953       DrawScreenField(x, y);
11954   }
11955
11956   if (dy != 0)
11957   {
11958     y = (dy == 1 ? BY1 : BY2);
11959     for (x = BX1; x <= BX2; x++)
11960       DrawScreenField(x, y);
11961   }
11962
11963   redraw_mask |= REDRAW_FIELD;
11964 }
11965
11966 static boolean canFallDown(struct PlayerInfo *player)
11967 {
11968   int jx = player->jx, jy = player->jy;
11969
11970   return (IN_LEV_FIELD(jx, jy + 1) &&
11971           (IS_FREE(jx, jy + 1) ||
11972            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11973           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11974           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11975 }
11976
11977 static boolean canPassField(int x, int y, int move_dir)
11978 {
11979   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11980   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11981   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11982   int nextx = x + dx;
11983   int nexty = y + dy;
11984   int element = Feld[x][y];
11985
11986   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11987           !CAN_MOVE(element) &&
11988           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11989           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11990           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11991 }
11992
11993 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11994 {
11995   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11996   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11997   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11998   int newx = x + dx;
11999   int newy = y + dy;
12000
12001   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12002           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12003           (IS_DIGGABLE(Feld[newx][newy]) ||
12004            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12005            canPassField(newx, newy, move_dir)));
12006 }
12007
12008 static void CheckGravityMovement(struct PlayerInfo *player)
12009 {
12010 #if USE_PLAYER_GRAVITY
12011   if (player->gravity && !player->programmed_action)
12012 #else
12013   if (game.gravity && !player->programmed_action)
12014 #endif
12015   {
12016     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12017     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12018     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12019     int jx = player->jx, jy = player->jy;
12020     boolean player_is_moving_to_valid_field =
12021       (!player_is_snapping &&
12022        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12023         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12024     boolean player_can_fall_down = canFallDown(player);
12025
12026     if (player_can_fall_down &&
12027         !player_is_moving_to_valid_field)
12028       player->programmed_action = MV_DOWN;
12029   }
12030 }
12031
12032 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12033 {
12034   return CheckGravityMovement(player);
12035
12036 #if USE_PLAYER_GRAVITY
12037   if (player->gravity && !player->programmed_action)
12038 #else
12039   if (game.gravity && !player->programmed_action)
12040 #endif
12041   {
12042     int jx = player->jx, jy = player->jy;
12043     boolean field_under_player_is_free =
12044       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12045     boolean player_is_standing_on_valid_field =
12046       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12047        (IS_WALKABLE(Feld[jx][jy]) &&
12048         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12049
12050     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12051       player->programmed_action = MV_DOWN;
12052   }
12053 }
12054
12055 /*
12056   MovePlayerOneStep()
12057   -----------------------------------------------------------------------------
12058   dx, dy:               direction (non-diagonal) to try to move the player to
12059   real_dx, real_dy:     direction as read from input device (can be diagonal)
12060 */
12061
12062 boolean MovePlayerOneStep(struct PlayerInfo *player,
12063                           int dx, int dy, int real_dx, int real_dy)
12064 {
12065   int jx = player->jx, jy = player->jy;
12066   int new_jx = jx + dx, new_jy = jy + dy;
12067 #if !USE_FIXED_DONT_RUN_INTO
12068   int element;
12069 #endif
12070   int can_move;
12071   boolean player_can_move = !player->cannot_move;
12072
12073   if (!player->active || (!dx && !dy))
12074     return MP_NO_ACTION;
12075
12076   player->MovDir = (dx < 0 ? MV_LEFT :
12077                     dx > 0 ? MV_RIGHT :
12078                     dy < 0 ? MV_UP :
12079                     dy > 0 ? MV_DOWN :  MV_NONE);
12080
12081   if (!IN_LEV_FIELD(new_jx, new_jy))
12082     return MP_NO_ACTION;
12083
12084   if (!player_can_move)
12085   {
12086     if (player->MovPos == 0)
12087     {
12088       player->is_moving = FALSE;
12089       player->is_digging = FALSE;
12090       player->is_collecting = FALSE;
12091       player->is_snapping = FALSE;
12092       player->is_pushing = FALSE;
12093     }
12094   }
12095
12096 #if 1
12097   if (!options.network && game.centered_player_nr == -1 &&
12098       !AllPlayersInSight(player, new_jx, new_jy))
12099     return MP_NO_ACTION;
12100 #else
12101   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12102     return MP_NO_ACTION;
12103 #endif
12104
12105 #if !USE_FIXED_DONT_RUN_INTO
12106   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12107
12108   /* (moved to DigField()) */
12109   if (player_can_move && DONT_RUN_INTO(element))
12110   {
12111     if (element == EL_ACID && dx == 0 && dy == 1)
12112     {
12113       SplashAcid(new_jx, new_jy);
12114       Feld[jx][jy] = EL_PLAYER_1;
12115       InitMovingField(jx, jy, MV_DOWN);
12116       Store[jx][jy] = EL_ACID;
12117       ContinueMoving(jx, jy);
12118       BuryPlayer(player);
12119     }
12120     else
12121       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12122
12123     return MP_MOVING;
12124   }
12125 #endif
12126
12127   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12128   if (can_move != MP_MOVING)
12129     return can_move;
12130
12131   /* check if DigField() has caused relocation of the player */
12132   if (player->jx != jx || player->jy != jy)
12133     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12134
12135   StorePlayer[jx][jy] = 0;
12136   player->last_jx = jx;
12137   player->last_jy = jy;
12138   player->jx = new_jx;
12139   player->jy = new_jy;
12140   StorePlayer[new_jx][new_jy] = player->element_nr;
12141
12142   if (player->move_delay_value_next != -1)
12143   {
12144     player->move_delay_value = player->move_delay_value_next;
12145     player->move_delay_value_next = -1;
12146   }
12147
12148   player->MovPos =
12149     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12150
12151   player->step_counter++;
12152
12153   PlayerVisit[jx][jy] = FrameCounter;
12154
12155 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12156   player->is_moving = TRUE;
12157 #endif
12158
12159 #if 1
12160   /* should better be called in MovePlayer(), but this breaks some tapes */
12161   ScrollPlayer(player, SCROLL_INIT);
12162 #endif
12163
12164   return MP_MOVING;
12165 }
12166
12167 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12168 {
12169   int jx = player->jx, jy = player->jy;
12170   int old_jx = jx, old_jy = jy;
12171   int moved = MP_NO_ACTION;
12172
12173   if (!player->active)
12174     return FALSE;
12175
12176   if (!dx && !dy)
12177   {
12178     if (player->MovPos == 0)
12179     {
12180       player->is_moving = FALSE;
12181       player->is_digging = FALSE;
12182       player->is_collecting = FALSE;
12183       player->is_snapping = FALSE;
12184       player->is_pushing = FALSE;
12185     }
12186
12187     return FALSE;
12188   }
12189
12190   if (player->move_delay > 0)
12191     return FALSE;
12192
12193   player->move_delay = -1;              /* set to "uninitialized" value */
12194
12195   /* store if player is automatically moved to next field */
12196   player->is_auto_moving = (player->programmed_action != MV_NONE);
12197
12198   /* remove the last programmed player action */
12199   player->programmed_action = 0;
12200
12201   if (player->MovPos)
12202   {
12203     /* should only happen if pre-1.2 tape recordings are played */
12204     /* this is only for backward compatibility */
12205
12206     int original_move_delay_value = player->move_delay_value;
12207
12208 #if DEBUG
12209     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12210            tape.counter);
12211 #endif
12212
12213     /* scroll remaining steps with finest movement resolution */
12214     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12215
12216     while (player->MovPos)
12217     {
12218       ScrollPlayer(player, SCROLL_GO_ON);
12219       ScrollScreen(NULL, SCROLL_GO_ON);
12220
12221       AdvanceFrameAndPlayerCounters(player->index_nr);
12222
12223       DrawAllPlayers();
12224       BackToFront();
12225     }
12226
12227     player->move_delay_value = original_move_delay_value;
12228   }
12229
12230   player->is_active = FALSE;
12231
12232   if (player->last_move_dir & MV_HORIZONTAL)
12233   {
12234     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12235       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12236   }
12237   else
12238   {
12239     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12240       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12241   }
12242
12243 #if USE_FIXED_BORDER_RUNNING_GFX
12244   if (!moved && !player->is_active)
12245   {
12246     player->is_moving = FALSE;
12247     player->is_digging = FALSE;
12248     player->is_collecting = FALSE;
12249     player->is_snapping = FALSE;
12250     player->is_pushing = FALSE;
12251   }
12252 #endif
12253
12254   jx = player->jx;
12255   jy = player->jy;
12256
12257 #if 1
12258   if (moved & MP_MOVING && !ScreenMovPos &&
12259       (player->index_nr == game.centered_player_nr ||
12260        game.centered_player_nr == -1))
12261 #else
12262   if (moved & MP_MOVING && !ScreenMovPos &&
12263       (player == local_player || !options.network))
12264 #endif
12265   {
12266     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12267     int offset = game.scroll_delay_value;
12268
12269     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12270     {
12271       /* actual player has left the screen -- scroll in that direction */
12272       if (jx != old_jx)         /* player has moved horizontally */
12273         scroll_x += (jx - old_jx);
12274       else                      /* player has moved vertically */
12275         scroll_y += (jy - old_jy);
12276     }
12277     else
12278     {
12279       if (jx != old_jx)         /* player has moved horizontally */
12280       {
12281         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12282             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12283           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12284
12285         /* don't scroll over playfield boundaries */
12286         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12287           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12288
12289         /* don't scroll more than one field at a time */
12290         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12291
12292         /* don't scroll against the player's moving direction */
12293         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12294             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12295           scroll_x = old_scroll_x;
12296       }
12297       else                      /* player has moved vertically */
12298       {
12299         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12300             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12301           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12302
12303         /* don't scroll over playfield boundaries */
12304         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12305           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12306
12307         /* don't scroll more than one field at a time */
12308         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12309
12310         /* don't scroll against the player's moving direction */
12311         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12312             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12313           scroll_y = old_scroll_y;
12314       }
12315     }
12316
12317     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12318     {
12319 #if 1
12320       if (!options.network && game.centered_player_nr == -1 &&
12321           !AllPlayersInVisibleScreen())
12322       {
12323         scroll_x = old_scroll_x;
12324         scroll_y = old_scroll_y;
12325       }
12326       else
12327 #else
12328       if (!options.network && !AllPlayersInVisibleScreen())
12329       {
12330         scroll_x = old_scroll_x;
12331         scroll_y = old_scroll_y;
12332       }
12333       else
12334 #endif
12335       {
12336         ScrollScreen(player, SCROLL_INIT);
12337         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12338       }
12339     }
12340   }
12341
12342   player->StepFrame = 0;
12343
12344   if (moved & MP_MOVING)
12345   {
12346     if (old_jx != jx && old_jy == jy)
12347       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12348     else if (old_jx == jx && old_jy != jy)
12349       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12350
12351     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12352
12353     player->last_move_dir = player->MovDir;
12354     player->is_moving = TRUE;
12355     player->is_snapping = FALSE;
12356     player->is_switching = FALSE;
12357     player->is_dropping = FALSE;
12358     player->is_dropping_pressed = FALSE;
12359     player->drop_pressed_delay = 0;
12360
12361 #if 0
12362     /* should better be called here than above, but this breaks some tapes */
12363     ScrollPlayer(player, SCROLL_INIT);
12364 #endif
12365   }
12366   else
12367   {
12368     CheckGravityMovementWhenNotMoving(player);
12369
12370     player->is_moving = FALSE;
12371
12372     /* at this point, the player is allowed to move, but cannot move right now
12373        (e.g. because of something blocking the way) -- ensure that the player
12374        is also allowed to move in the next frame (in old versions before 3.1.1,
12375        the player was forced to wait again for eight frames before next try) */
12376
12377     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12378       player->move_delay = 0;   /* allow direct movement in the next frame */
12379   }
12380
12381   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12382     player->move_delay = player->move_delay_value;
12383
12384   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12385   {
12386     TestIfPlayerTouchesBadThing(jx, jy);
12387     TestIfPlayerTouchesCustomElement(jx, jy);
12388   }
12389
12390   if (!player->active)
12391     RemovePlayer(player);
12392
12393   return moved;
12394 }
12395
12396 void ScrollPlayer(struct PlayerInfo *player, int mode)
12397 {
12398   int jx = player->jx, jy = player->jy;
12399   int last_jx = player->last_jx, last_jy = player->last_jy;
12400   int move_stepsize = TILEX / player->move_delay_value;
12401
12402 #if USE_NEW_PLAYER_SPEED
12403   if (!player->active)
12404     return;
12405
12406   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12407     return;
12408 #else
12409   if (!player->active || player->MovPos == 0)
12410     return;
12411 #endif
12412
12413   if (mode == SCROLL_INIT)
12414   {
12415     player->actual_frame_counter = FrameCounter;
12416     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12417
12418     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12419         Feld[last_jx][last_jy] == EL_EMPTY)
12420     {
12421       int last_field_block_delay = 0;   /* start with no blocking at all */
12422       int block_delay_adjustment = player->block_delay_adjustment;
12423
12424       /* if player blocks last field, add delay for exactly one move */
12425       if (player->block_last_field)
12426       {
12427         last_field_block_delay += player->move_delay_value;
12428
12429         /* when blocking enabled, prevent moving up despite gravity */
12430 #if USE_PLAYER_GRAVITY
12431         if (player->gravity && player->MovDir == MV_UP)
12432           block_delay_adjustment = -1;
12433 #else
12434         if (game.gravity && player->MovDir == MV_UP)
12435           block_delay_adjustment = -1;
12436 #endif
12437       }
12438
12439       /* add block delay adjustment (also possible when not blocking) */
12440       last_field_block_delay += block_delay_adjustment;
12441
12442       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12443       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12444     }
12445
12446 #if USE_NEW_PLAYER_SPEED
12447     if (player->MovPos != 0)    /* player has not yet reached destination */
12448       return;
12449 #else
12450     return;
12451 #endif
12452   }
12453   else if (!FrameReached(&player->actual_frame_counter, 1))
12454     return;
12455
12456 #if USE_NEW_PLAYER_SPEED
12457   if (player->MovPos != 0)
12458   {
12459     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12460     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12461
12462     /* before DrawPlayer() to draw correct player graphic for this case */
12463     if (player->MovPos == 0)
12464       CheckGravityMovement(player);
12465   }
12466 #else
12467   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12468   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12469
12470   /* before DrawPlayer() to draw correct player graphic for this case */
12471   if (player->MovPos == 0)
12472     CheckGravityMovement(player);
12473 #endif
12474
12475   if (player->MovPos == 0)      /* player reached destination field */
12476   {
12477     if (player->move_delay_reset_counter > 0)
12478     {
12479       player->move_delay_reset_counter--;
12480
12481       if (player->move_delay_reset_counter == 0)
12482       {
12483         /* continue with normal speed after quickly moving through gate */
12484         HALVE_PLAYER_SPEED(player);
12485
12486         /* be able to make the next move without delay */
12487         player->move_delay = 0;
12488       }
12489     }
12490
12491     player->last_jx = jx;
12492     player->last_jy = jy;
12493
12494     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12495         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12496         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12497         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12498         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12499         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12500     {
12501       DrawPlayer(player);       /* needed here only to cleanup last field */
12502       RemovePlayer(player);
12503
12504       if (local_player->friends_still_needed == 0 ||
12505           IS_SP_ELEMENT(Feld[jx][jy]))
12506         PlayerWins(player);
12507     }
12508
12509     /* this breaks one level: "machine", level 000 */
12510     {
12511       int move_direction = player->MovDir;
12512       int enter_side = MV_DIR_OPPOSITE(move_direction);
12513       int leave_side = move_direction;
12514       int old_jx = last_jx;
12515       int old_jy = last_jy;
12516       int old_element = Feld[old_jx][old_jy];
12517       int new_element = Feld[jx][jy];
12518
12519       if (IS_CUSTOM_ELEMENT(old_element))
12520         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12521                                    CE_LEFT_BY_PLAYER,
12522                                    player->index_bit, leave_side);
12523
12524       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12525                                           CE_PLAYER_LEAVES_X,
12526                                           player->index_bit, leave_side);
12527
12528       if (IS_CUSTOM_ELEMENT(new_element))
12529         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12530                                    player->index_bit, enter_side);
12531
12532       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12533                                           CE_PLAYER_ENTERS_X,
12534                                           player->index_bit, enter_side);
12535
12536       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12537                                         CE_MOVE_OF_X, move_direction);
12538     }
12539
12540     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12541     {
12542       TestIfPlayerTouchesBadThing(jx, jy);
12543       TestIfPlayerTouchesCustomElement(jx, jy);
12544
12545       /* needed because pushed element has not yet reached its destination,
12546          so it would trigger a change event at its previous field location */
12547       if (!player->is_pushing)
12548         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12549
12550       if (!player->active)
12551         RemovePlayer(player);
12552     }
12553
12554     if (!local_player->LevelSolved && level.use_step_counter)
12555     {
12556       int i;
12557
12558       TimePlayed++;
12559
12560       if (TimeLeft > 0)
12561       {
12562         TimeLeft--;
12563
12564         if (TimeLeft <= 10 && setup.time_limit)
12565           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12566
12567 #if 1
12568         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12569
12570         DisplayGameControlValues();
12571 #else
12572         DrawGameValue_Time(TimeLeft);
12573 #endif
12574
12575         if (!TimeLeft && setup.time_limit)
12576           for (i = 0; i < MAX_PLAYERS; i++)
12577             KillPlayer(&stored_player[i]);
12578       }
12579 #if 1
12580       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12581       {
12582         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12583
12584         DisplayGameControlValues();
12585       }
12586 #else
12587       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12588         DrawGameValue_Time(TimePlayed);
12589 #endif
12590     }
12591
12592     if (tape.single_step && tape.recording && !tape.pausing &&
12593         !player->programmed_action)
12594       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12595   }
12596 }
12597
12598 void ScrollScreen(struct PlayerInfo *player, int mode)
12599 {
12600   static unsigned long screen_frame_counter = 0;
12601
12602   if (mode == SCROLL_INIT)
12603   {
12604     /* set scrolling step size according to actual player's moving speed */
12605     ScrollStepSize = TILEX / player->move_delay_value;
12606
12607     screen_frame_counter = FrameCounter;
12608     ScreenMovDir = player->MovDir;
12609     ScreenMovPos = player->MovPos;
12610     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12611     return;
12612   }
12613   else if (!FrameReached(&screen_frame_counter, 1))
12614     return;
12615
12616   if (ScreenMovPos)
12617   {
12618     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12619     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12620     redraw_mask |= REDRAW_FIELD;
12621   }
12622   else
12623     ScreenMovDir = MV_NONE;
12624 }
12625
12626 void TestIfPlayerTouchesCustomElement(int x, int y)
12627 {
12628   static int xy[4][2] =
12629   {
12630     { 0, -1 },
12631     { -1, 0 },
12632     { +1, 0 },
12633     { 0, +1 }
12634   };
12635   static int trigger_sides[4][2] =
12636   {
12637     /* center side       border side */
12638     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12639     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12640     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12641     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12642   };
12643   static int touch_dir[4] =
12644   {
12645     MV_LEFT | MV_RIGHT,
12646     MV_UP   | MV_DOWN,
12647     MV_UP   | MV_DOWN,
12648     MV_LEFT | MV_RIGHT
12649   };
12650   int center_element = Feld[x][y];      /* should always be non-moving! */
12651   int i;
12652
12653   for (i = 0; i < NUM_DIRECTIONS; i++)
12654   {
12655     int xx = x + xy[i][0];
12656     int yy = y + xy[i][1];
12657     int center_side = trigger_sides[i][0];
12658     int border_side = trigger_sides[i][1];
12659     int border_element;
12660
12661     if (!IN_LEV_FIELD(xx, yy))
12662       continue;
12663
12664     if (IS_PLAYER(x, y))
12665     {
12666       struct PlayerInfo *player = PLAYERINFO(x, y);
12667
12668       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12669         border_element = Feld[xx][yy];          /* may be moving! */
12670       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12671         border_element = Feld[xx][yy];
12672       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12673         border_element = MovingOrBlocked2Element(xx, yy);
12674       else
12675         continue;               /* center and border element do not touch */
12676
12677       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12678                                  player->index_bit, border_side);
12679       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12680                                           CE_PLAYER_TOUCHES_X,
12681                                           player->index_bit, border_side);
12682     }
12683     else if (IS_PLAYER(xx, yy))
12684     {
12685       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12686
12687       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12688       {
12689         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12690           continue;             /* center and border element do not touch */
12691       }
12692
12693       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12694                                  player->index_bit, center_side);
12695       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12696                                           CE_PLAYER_TOUCHES_X,
12697                                           player->index_bit, center_side);
12698       break;
12699     }
12700   }
12701 }
12702
12703 #if USE_ELEMENT_TOUCHING_BUGFIX
12704
12705 void TestIfElementTouchesCustomElement(int x, int y)
12706 {
12707   static int xy[4][2] =
12708   {
12709     { 0, -1 },
12710     { -1, 0 },
12711     { +1, 0 },
12712     { 0, +1 }
12713   };
12714   static int trigger_sides[4][2] =
12715   {
12716     /* center side      border side */
12717     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12718     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12719     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12720     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12721   };
12722   static int touch_dir[4] =
12723   {
12724     MV_LEFT | MV_RIGHT,
12725     MV_UP   | MV_DOWN,
12726     MV_UP   | MV_DOWN,
12727     MV_LEFT | MV_RIGHT
12728   };
12729   boolean change_center_element = FALSE;
12730   int center_element = Feld[x][y];      /* should always be non-moving! */
12731   int border_element_old[NUM_DIRECTIONS];
12732   int i;
12733
12734   for (i = 0; i < NUM_DIRECTIONS; i++)
12735   {
12736     int xx = x + xy[i][0];
12737     int yy = y + xy[i][1];
12738     int border_element;
12739
12740     border_element_old[i] = -1;
12741
12742     if (!IN_LEV_FIELD(xx, yy))
12743       continue;
12744
12745     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12746       border_element = Feld[xx][yy];    /* may be moving! */
12747     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12748       border_element = Feld[xx][yy];
12749     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12750       border_element = MovingOrBlocked2Element(xx, yy);
12751     else
12752       continue;                 /* center and border element do not touch */
12753
12754     border_element_old[i] = border_element;
12755   }
12756
12757   for (i = 0; i < NUM_DIRECTIONS; i++)
12758   {
12759     int xx = x + xy[i][0];
12760     int yy = y + xy[i][1];
12761     int center_side = trigger_sides[i][0];
12762     int border_element = border_element_old[i];
12763
12764     if (border_element == -1)
12765       continue;
12766
12767     /* check for change of border element */
12768     CheckElementChangeBySide(xx, yy, border_element, center_element,
12769                              CE_TOUCHING_X, center_side);
12770   }
12771
12772   for (i = 0; i < NUM_DIRECTIONS; i++)
12773   {
12774     int border_side = trigger_sides[i][1];
12775     int border_element = border_element_old[i];
12776
12777     if (border_element == -1)
12778       continue;
12779
12780     /* check for change of center element (but change it only once) */
12781     if (!change_center_element)
12782       change_center_element =
12783         CheckElementChangeBySide(x, y, center_element, border_element,
12784                                  CE_TOUCHING_X, border_side);
12785   }
12786 }
12787
12788 #else
12789
12790 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12791 {
12792   static int xy[4][2] =
12793   {
12794     { 0, -1 },
12795     { -1, 0 },
12796     { +1, 0 },
12797     { 0, +1 }
12798   };
12799   static int trigger_sides[4][2] =
12800   {
12801     /* center side      border side */
12802     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12803     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12804     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12805     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12806   };
12807   static int touch_dir[4] =
12808   {
12809     MV_LEFT | MV_RIGHT,
12810     MV_UP   | MV_DOWN,
12811     MV_UP   | MV_DOWN,
12812     MV_LEFT | MV_RIGHT
12813   };
12814   boolean change_center_element = FALSE;
12815   int center_element = Feld[x][y];      /* should always be non-moving! */
12816   int i;
12817
12818   for (i = 0; i < NUM_DIRECTIONS; i++)
12819   {
12820     int xx = x + xy[i][0];
12821     int yy = y + xy[i][1];
12822     int center_side = trigger_sides[i][0];
12823     int border_side = trigger_sides[i][1];
12824     int border_element;
12825
12826     if (!IN_LEV_FIELD(xx, yy))
12827       continue;
12828
12829     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12830       border_element = Feld[xx][yy];    /* may be moving! */
12831     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12832       border_element = Feld[xx][yy];
12833     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12834       border_element = MovingOrBlocked2Element(xx, yy);
12835     else
12836       continue;                 /* center and border element do not touch */
12837
12838     /* check for change of center element (but change it only once) */
12839     if (!change_center_element)
12840       change_center_element =
12841         CheckElementChangeBySide(x, y, center_element, border_element,
12842                                  CE_TOUCHING_X, border_side);
12843
12844     /* check for change of border element */
12845     CheckElementChangeBySide(xx, yy, border_element, center_element,
12846                              CE_TOUCHING_X, center_side);
12847   }
12848 }
12849
12850 #endif
12851
12852 void TestIfElementHitsCustomElement(int x, int y, int direction)
12853 {
12854   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12855   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12856   int hitx = x + dx, hity = y + dy;
12857   int hitting_element = Feld[x][y];
12858   int touched_element;
12859
12860   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12861     return;
12862
12863   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12864                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12865
12866   if (IN_LEV_FIELD(hitx, hity))
12867   {
12868     int opposite_direction = MV_DIR_OPPOSITE(direction);
12869     int hitting_side = direction;
12870     int touched_side = opposite_direction;
12871     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12872                           MovDir[hitx][hity] != direction ||
12873                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12874
12875     object_hit = TRUE;
12876
12877     if (object_hit)
12878     {
12879       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12880                                CE_HITTING_X, touched_side);
12881
12882       CheckElementChangeBySide(hitx, hity, touched_element,
12883                                hitting_element, CE_HIT_BY_X, hitting_side);
12884
12885       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12886                                CE_HIT_BY_SOMETHING, opposite_direction);
12887     }
12888   }
12889
12890   /* "hitting something" is also true when hitting the playfield border */
12891   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12892                            CE_HITTING_SOMETHING, direction);
12893 }
12894
12895 #if 0
12896 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12897 {
12898   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12899   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12900   int hitx = x + dx, hity = y + dy;
12901   int hitting_element = Feld[x][y];
12902   int touched_element;
12903 #if 0
12904   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12905                         !IS_FREE(hitx, hity) &&
12906                         (!IS_MOVING(hitx, hity) ||
12907                          MovDir[hitx][hity] != direction ||
12908                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12909 #endif
12910
12911   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12912     return;
12913
12914 #if 0
12915   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12916     return;
12917 #endif
12918
12919   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12920                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12921
12922   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12923                            EP_CAN_SMASH_EVERYTHING, direction);
12924
12925   if (IN_LEV_FIELD(hitx, hity))
12926   {
12927     int opposite_direction = MV_DIR_OPPOSITE(direction);
12928     int hitting_side = direction;
12929     int touched_side = opposite_direction;
12930 #if 0
12931     int touched_element = MovingOrBlocked2Element(hitx, hity);
12932 #endif
12933 #if 1
12934     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12935                           MovDir[hitx][hity] != direction ||
12936                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12937
12938     object_hit = TRUE;
12939 #endif
12940
12941     if (object_hit)
12942     {
12943       int i;
12944
12945       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12946                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12947
12948       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12949                                CE_OTHER_IS_SMASHING, touched_side);
12950
12951       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12952                                CE_OTHER_GETS_SMASHED, hitting_side);
12953     }
12954   }
12955 }
12956 #endif
12957
12958 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12959 {
12960   int i, kill_x = -1, kill_y = -1;
12961
12962   int bad_element = -1;
12963   static int test_xy[4][2] =
12964   {
12965     { 0, -1 },
12966     { -1, 0 },
12967     { +1, 0 },
12968     { 0, +1 }
12969   };
12970   static int test_dir[4] =
12971   {
12972     MV_UP,
12973     MV_LEFT,
12974     MV_RIGHT,
12975     MV_DOWN
12976   };
12977
12978   for (i = 0; i < NUM_DIRECTIONS; i++)
12979   {
12980     int test_x, test_y, test_move_dir, test_element;
12981
12982     test_x = good_x + test_xy[i][0];
12983     test_y = good_y + test_xy[i][1];
12984
12985     if (!IN_LEV_FIELD(test_x, test_y))
12986       continue;
12987
12988     test_move_dir =
12989       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12990
12991     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12992
12993     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12994        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12995     */
12996     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12997         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12998     {
12999       kill_x = test_x;
13000       kill_y = test_y;
13001       bad_element = test_element;
13002
13003       break;
13004     }
13005   }
13006
13007   if (kill_x != -1 || kill_y != -1)
13008   {
13009     if (IS_PLAYER(good_x, good_y))
13010     {
13011       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13012
13013       if (player->shield_deadly_time_left > 0 &&
13014           !IS_INDESTRUCTIBLE(bad_element))
13015         Bang(kill_x, kill_y);
13016       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13017         KillPlayer(player);
13018     }
13019     else
13020       Bang(good_x, good_y);
13021   }
13022 }
13023
13024 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13025 {
13026   int i, kill_x = -1, kill_y = -1;
13027   int bad_element = Feld[bad_x][bad_y];
13028   static int test_xy[4][2] =
13029   {
13030     { 0, -1 },
13031     { -1, 0 },
13032     { +1, 0 },
13033     { 0, +1 }
13034   };
13035   static int touch_dir[4] =
13036   {
13037     MV_LEFT | MV_RIGHT,
13038     MV_UP   | MV_DOWN,
13039     MV_UP   | MV_DOWN,
13040     MV_LEFT | MV_RIGHT
13041   };
13042   static int test_dir[4] =
13043   {
13044     MV_UP,
13045     MV_LEFT,
13046     MV_RIGHT,
13047     MV_DOWN
13048   };
13049
13050   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13051     return;
13052
13053   for (i = 0; i < NUM_DIRECTIONS; i++)
13054   {
13055     int test_x, test_y, test_move_dir, test_element;
13056
13057     test_x = bad_x + test_xy[i][0];
13058     test_y = bad_y + test_xy[i][1];
13059     if (!IN_LEV_FIELD(test_x, test_y))
13060       continue;
13061
13062     test_move_dir =
13063       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13064
13065     test_element = Feld[test_x][test_y];
13066
13067     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13068        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13069     */
13070     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13071         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13072     {
13073       /* good thing is player or penguin that does not move away */
13074       if (IS_PLAYER(test_x, test_y))
13075       {
13076         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13077
13078         if (bad_element == EL_ROBOT && player->is_moving)
13079           continue;     /* robot does not kill player if he is moving */
13080
13081         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13082         {
13083           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13084             continue;           /* center and border element do not touch */
13085         }
13086
13087         kill_x = test_x;
13088         kill_y = test_y;
13089         break;
13090       }
13091       else if (test_element == EL_PENGUIN)
13092       {
13093         kill_x = test_x;
13094         kill_y = test_y;
13095         break;
13096       }
13097     }
13098   }
13099
13100   if (kill_x != -1 || kill_y != -1)
13101   {
13102     if (IS_PLAYER(kill_x, kill_y))
13103     {
13104       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13105
13106       if (player->shield_deadly_time_left > 0 &&
13107           !IS_INDESTRUCTIBLE(bad_element))
13108         Bang(bad_x, bad_y);
13109       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13110         KillPlayer(player);
13111     }
13112     else
13113       Bang(kill_x, kill_y);
13114   }
13115 }
13116
13117 void TestIfPlayerTouchesBadThing(int x, int y)
13118 {
13119   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13120 }
13121
13122 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13123 {
13124   TestIfGoodThingHitsBadThing(x, y, move_dir);
13125 }
13126
13127 void TestIfBadThingTouchesPlayer(int x, int y)
13128 {
13129   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13130 }
13131
13132 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13133 {
13134   TestIfBadThingHitsGoodThing(x, y, move_dir);
13135 }
13136
13137 void TestIfFriendTouchesBadThing(int x, int y)
13138 {
13139   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13140 }
13141
13142 void TestIfBadThingTouchesFriend(int x, int y)
13143 {
13144   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13145 }
13146
13147 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13148 {
13149   int i, kill_x = bad_x, kill_y = bad_y;
13150   static int xy[4][2] =
13151   {
13152     { 0, -1 },
13153     { -1, 0 },
13154     { +1, 0 },
13155     { 0, +1 }
13156   };
13157
13158   for (i = 0; i < NUM_DIRECTIONS; i++)
13159   {
13160     int x, y, element;
13161
13162     x = bad_x + xy[i][0];
13163     y = bad_y + xy[i][1];
13164     if (!IN_LEV_FIELD(x, y))
13165       continue;
13166
13167     element = Feld[x][y];
13168     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13169         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13170     {
13171       kill_x = x;
13172       kill_y = y;
13173       break;
13174     }
13175   }
13176
13177   if (kill_x != bad_x || kill_y != bad_y)
13178     Bang(bad_x, bad_y);
13179 }
13180
13181 void KillPlayer(struct PlayerInfo *player)
13182 {
13183   int jx = player->jx, jy = player->jy;
13184
13185   if (!player->active)
13186     return;
13187
13188   /* the following code was introduced to prevent an infinite loop when calling
13189      -> Bang()
13190      -> CheckTriggeredElementChangeExt()
13191      -> ExecuteCustomElementAction()
13192      -> KillPlayer()
13193      -> (infinitely repeating the above sequence of function calls)
13194      which occurs when killing the player while having a CE with the setting
13195      "kill player X when explosion of <player X>"; the solution using a new
13196      field "player->killed" was chosen for backwards compatibility, although
13197      clever use of the fields "player->active" etc. would probably also work */
13198 #if 1
13199   if (player->killed)
13200     return;
13201 #endif
13202
13203   player->killed = TRUE;
13204
13205   /* remove accessible field at the player's position */
13206   Feld[jx][jy] = EL_EMPTY;
13207
13208   /* deactivate shield (else Bang()/Explode() would not work right) */
13209   player->shield_normal_time_left = 0;
13210   player->shield_deadly_time_left = 0;
13211
13212   Bang(jx, jy);
13213   BuryPlayer(player);
13214 }
13215
13216 static void KillPlayerUnlessEnemyProtected(int x, int y)
13217 {
13218   if (!PLAYER_ENEMY_PROTECTED(x, y))
13219     KillPlayer(PLAYERINFO(x, y));
13220 }
13221
13222 static void KillPlayerUnlessExplosionProtected(int x, int y)
13223 {
13224   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13225     KillPlayer(PLAYERINFO(x, y));
13226 }
13227
13228 void BuryPlayer(struct PlayerInfo *player)
13229 {
13230   int jx = player->jx, jy = player->jy;
13231
13232   if (!player->active)
13233     return;
13234
13235   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13236   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13237
13238   player->GameOver = TRUE;
13239   RemovePlayer(player);
13240 }
13241
13242 void RemovePlayer(struct PlayerInfo *player)
13243 {
13244   int jx = player->jx, jy = player->jy;
13245   int i, found = FALSE;
13246
13247   player->present = FALSE;
13248   player->active = FALSE;
13249
13250   if (!ExplodeField[jx][jy])
13251     StorePlayer[jx][jy] = 0;
13252
13253   if (player->is_moving)
13254     DrawLevelField(player->last_jx, player->last_jy);
13255
13256   for (i = 0; i < MAX_PLAYERS; i++)
13257     if (stored_player[i].active)
13258       found = TRUE;
13259
13260   if (!found)
13261     AllPlayersGone = TRUE;
13262
13263   ExitX = ZX = jx;
13264   ExitY = ZY = jy;
13265 }
13266
13267 #if USE_NEW_SNAP_DELAY
13268 static void setFieldForSnapping(int x, int y, int element, int direction)
13269 {
13270   struct ElementInfo *ei = &element_info[element];
13271   int direction_bit = MV_DIR_TO_BIT(direction);
13272   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13273   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13274                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13275
13276   Feld[x][y] = EL_ELEMENT_SNAPPING;
13277   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13278
13279   ResetGfxAnimation(x, y);
13280
13281   GfxElement[x][y] = element;
13282   GfxAction[x][y] = action;
13283   GfxDir[x][y] = direction;
13284   GfxFrame[x][y] = -1;
13285 }
13286 #endif
13287
13288 /*
13289   =============================================================================
13290   checkDiagonalPushing()
13291   -----------------------------------------------------------------------------
13292   check if diagonal input device direction results in pushing of object
13293   (by checking if the alternative direction is walkable, diggable, ...)
13294   =============================================================================
13295 */
13296
13297 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13298                                     int x, int y, int real_dx, int real_dy)
13299 {
13300   int jx, jy, dx, dy, xx, yy;
13301
13302   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13303     return TRUE;
13304
13305   /* diagonal direction: check alternative direction */
13306   jx = player->jx;
13307   jy = player->jy;
13308   dx = x - jx;
13309   dy = y - jy;
13310   xx = jx + (dx == 0 ? real_dx : 0);
13311   yy = jy + (dy == 0 ? real_dy : 0);
13312
13313   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13314 }
13315
13316 /*
13317   =============================================================================
13318   DigField()
13319   -----------------------------------------------------------------------------
13320   x, y:                 field next to player (non-diagonal) to try to dig to
13321   real_dx, real_dy:     direction as read from input device (can be diagonal)
13322   =============================================================================
13323 */
13324
13325 int DigField(struct PlayerInfo *player,
13326              int oldx, int oldy, int x, int y,
13327              int real_dx, int real_dy, int mode)
13328 {
13329   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13330   boolean player_was_pushing = player->is_pushing;
13331   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13332   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13333   int jx = oldx, jy = oldy;
13334   int dx = x - jx, dy = y - jy;
13335   int nextx = x + dx, nexty = y + dy;
13336   int move_direction = (dx == -1 ? MV_LEFT  :
13337                         dx == +1 ? MV_RIGHT :
13338                         dy == -1 ? MV_UP    :
13339                         dy == +1 ? MV_DOWN  : MV_NONE);
13340   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13341   int dig_side = MV_DIR_OPPOSITE(move_direction);
13342   int old_element = Feld[jx][jy];
13343 #if USE_FIXED_DONT_RUN_INTO
13344   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13345 #else
13346   int element;
13347 #endif
13348   int collect_count;
13349
13350   if (is_player)                /* function can also be called by EL_PENGUIN */
13351   {
13352     if (player->MovPos == 0)
13353     {
13354       player->is_digging = FALSE;
13355       player->is_collecting = FALSE;
13356     }
13357
13358     if (player->MovPos == 0)    /* last pushing move finished */
13359       player->is_pushing = FALSE;
13360
13361     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13362     {
13363       player->is_switching = FALSE;
13364       player->push_delay = -1;
13365
13366       return MP_NO_ACTION;
13367     }
13368   }
13369
13370 #if !USE_FIXED_DONT_RUN_INTO
13371   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13372     return MP_NO_ACTION;
13373 #endif
13374
13375   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13376     old_element = Back[jx][jy];
13377
13378   /* in case of element dropped at player position, check background */
13379   else if (Back[jx][jy] != EL_EMPTY &&
13380            game.engine_version >= VERSION_IDENT(2,2,0,0))
13381     old_element = Back[jx][jy];
13382
13383   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13384     return MP_NO_ACTION;        /* field has no opening in this direction */
13385
13386   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13387     return MP_NO_ACTION;        /* field has no opening in this direction */
13388
13389 #if USE_FIXED_DONT_RUN_INTO
13390   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13391   {
13392     SplashAcid(x, y);
13393
13394     Feld[jx][jy] = player->artwork_element;
13395     InitMovingField(jx, jy, MV_DOWN);
13396     Store[jx][jy] = EL_ACID;
13397     ContinueMoving(jx, jy);
13398     BuryPlayer(player);
13399
13400     return MP_DONT_RUN_INTO;
13401   }
13402 #endif
13403
13404 #if USE_FIXED_DONT_RUN_INTO
13405   if (player_can_move && DONT_RUN_INTO(element))
13406   {
13407     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13408
13409     return MP_DONT_RUN_INTO;
13410   }
13411 #endif
13412
13413 #if USE_FIXED_DONT_RUN_INTO
13414   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13415     return MP_NO_ACTION;
13416 #endif
13417
13418 #if !USE_FIXED_DONT_RUN_INTO
13419   element = Feld[x][y];
13420 #endif
13421
13422   collect_count = element_info[element].collect_count_initial;
13423
13424   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13425     return MP_NO_ACTION;
13426
13427   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13428     player_can_move = player_can_move_or_snap;
13429
13430   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13431       game.engine_version >= VERSION_IDENT(2,2,0,0))
13432   {
13433     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13434                                player->index_bit, dig_side);
13435     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13436                                         player->index_bit, dig_side);
13437
13438     if (element == EL_DC_LANDMINE)
13439       Bang(x, y);
13440
13441     if (Feld[x][y] != element)          /* field changed by snapping */
13442       return MP_ACTION;
13443
13444     return MP_NO_ACTION;
13445   }
13446
13447 #if USE_PLAYER_GRAVITY
13448   if (player->gravity && is_player && !player->is_auto_moving &&
13449       canFallDown(player) && move_direction != MV_DOWN &&
13450       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13451     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13452 #else
13453   if (game.gravity && is_player && !player->is_auto_moving &&
13454       canFallDown(player) && move_direction != MV_DOWN &&
13455       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13456     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13457 #endif
13458
13459   if (player_can_move &&
13460       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13461   {
13462     int sound_element = SND_ELEMENT(element);
13463     int sound_action = ACTION_WALKING;
13464
13465     if (IS_RND_GATE(element))
13466     {
13467       if (!player->key[RND_GATE_NR(element)])
13468         return MP_NO_ACTION;
13469     }
13470     else if (IS_RND_GATE_GRAY(element))
13471     {
13472       if (!player->key[RND_GATE_GRAY_NR(element)])
13473         return MP_NO_ACTION;
13474     }
13475     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13476     {
13477       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13478         return MP_NO_ACTION;
13479     }
13480     else if (element == EL_EXIT_OPEN ||
13481              element == EL_EM_EXIT_OPEN ||
13482              element == EL_STEEL_EXIT_OPEN ||
13483              element == EL_EM_STEEL_EXIT_OPEN ||
13484              element == EL_SP_EXIT_OPEN ||
13485              element == EL_SP_EXIT_OPENING)
13486     {
13487       sound_action = ACTION_PASSING;    /* player is passing exit */
13488     }
13489     else if (element == EL_EMPTY)
13490     {
13491       sound_action = ACTION_MOVING;             /* nothing to walk on */
13492     }
13493
13494     /* play sound from background or player, whatever is available */
13495     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13496       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13497     else
13498       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13499   }
13500   else if (player_can_move &&
13501            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13502   {
13503     if (!ACCESS_FROM(element, opposite_direction))
13504       return MP_NO_ACTION;      /* field not accessible from this direction */
13505
13506     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13507       return MP_NO_ACTION;
13508
13509     if (IS_EM_GATE(element))
13510     {
13511       if (!player->key[EM_GATE_NR(element)])
13512         return MP_NO_ACTION;
13513     }
13514     else if (IS_EM_GATE_GRAY(element))
13515     {
13516       if (!player->key[EM_GATE_GRAY_NR(element)])
13517         return MP_NO_ACTION;
13518     }
13519     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13520     {
13521       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13522         return MP_NO_ACTION;
13523     }
13524     else if (IS_EMC_GATE(element))
13525     {
13526       if (!player->key[EMC_GATE_NR(element)])
13527         return MP_NO_ACTION;
13528     }
13529     else if (IS_EMC_GATE_GRAY(element))
13530     {
13531       if (!player->key[EMC_GATE_GRAY_NR(element)])
13532         return MP_NO_ACTION;
13533     }
13534     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13535     {
13536       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13537         return MP_NO_ACTION;
13538     }
13539     else if (element == EL_DC_GATE_WHITE ||
13540              element == EL_DC_GATE_WHITE_GRAY ||
13541              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13542     {
13543       if (player->num_white_keys == 0)
13544         return MP_NO_ACTION;
13545
13546       player->num_white_keys--;
13547     }
13548     else if (IS_SP_PORT(element))
13549     {
13550       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13551           element == EL_SP_GRAVITY_PORT_RIGHT ||
13552           element == EL_SP_GRAVITY_PORT_UP ||
13553           element == EL_SP_GRAVITY_PORT_DOWN)
13554 #if USE_PLAYER_GRAVITY
13555         player->gravity = !player->gravity;
13556 #else
13557         game.gravity = !game.gravity;
13558 #endif
13559       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13560                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13561                element == EL_SP_GRAVITY_ON_PORT_UP ||
13562                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13563 #if USE_PLAYER_GRAVITY
13564         player->gravity = TRUE;
13565 #else
13566         game.gravity = TRUE;
13567 #endif
13568       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13569                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13570                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13571                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13572 #if USE_PLAYER_GRAVITY
13573         player->gravity = FALSE;
13574 #else
13575         game.gravity = FALSE;
13576 #endif
13577     }
13578
13579     /* automatically move to the next field with double speed */
13580     player->programmed_action = move_direction;
13581
13582     if (player->move_delay_reset_counter == 0)
13583     {
13584       player->move_delay_reset_counter = 2;     /* two double speed steps */
13585
13586       DOUBLE_PLAYER_SPEED(player);
13587     }
13588
13589     PlayLevelSoundAction(x, y, ACTION_PASSING);
13590   }
13591   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13592   {
13593     RemoveField(x, y);
13594
13595     if (mode != DF_SNAP)
13596     {
13597       GfxElement[x][y] = GFX_ELEMENT(element);
13598       player->is_digging = TRUE;
13599     }
13600
13601     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13602
13603     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13604                                         player->index_bit, dig_side);
13605
13606     if (mode == DF_SNAP)
13607     {
13608 #if USE_NEW_SNAP_DELAY
13609       if (level.block_snap_field)
13610         setFieldForSnapping(x, y, element, move_direction);
13611       else
13612         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13613 #else
13614       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13615 #endif
13616
13617       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13618                                           player->index_bit, dig_side);
13619     }
13620   }
13621   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13622   {
13623     RemoveField(x, y);
13624
13625     if (is_player && mode != DF_SNAP)
13626     {
13627       GfxElement[x][y] = element;
13628       player->is_collecting = TRUE;
13629     }
13630
13631     if (element == EL_SPEED_PILL)
13632     {
13633       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13634     }
13635     else if (element == EL_EXTRA_TIME && level.time > 0)
13636     {
13637       TimeLeft += level.extra_time;
13638
13639 #if 1
13640       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13641
13642       DisplayGameControlValues();
13643 #else
13644       DrawGameValue_Time(TimeLeft);
13645 #endif
13646     }
13647     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13648     {
13649       player->shield_normal_time_left += level.shield_normal_time;
13650       if (element == EL_SHIELD_DEADLY)
13651         player->shield_deadly_time_left += level.shield_deadly_time;
13652     }
13653     else if (element == EL_DYNAMITE ||
13654              element == EL_EM_DYNAMITE ||
13655              element == EL_SP_DISK_RED)
13656     {
13657       if (player->inventory_size < MAX_INVENTORY_SIZE)
13658         player->inventory_element[player->inventory_size++] = element;
13659
13660       DrawGameDoorValues();
13661     }
13662     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13663     {
13664       player->dynabomb_count++;
13665       player->dynabombs_left++;
13666     }
13667     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13668     {
13669       player->dynabomb_size++;
13670     }
13671     else if (element == EL_DYNABOMB_INCREASE_POWER)
13672     {
13673       player->dynabomb_xl = TRUE;
13674     }
13675     else if (IS_KEY(element))
13676     {
13677       player->key[KEY_NR(element)] = TRUE;
13678
13679       DrawGameDoorValues();
13680     }
13681     else if (element == EL_DC_KEY_WHITE)
13682     {
13683       player->num_white_keys++;
13684
13685       /* display white keys? */
13686       /* DrawGameDoorValues(); */
13687     }
13688     else if (IS_ENVELOPE(element))
13689     {
13690       player->show_envelope = element;
13691     }
13692     else if (element == EL_EMC_LENSES)
13693     {
13694       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13695
13696       RedrawAllInvisibleElementsForLenses();
13697     }
13698     else if (element == EL_EMC_MAGNIFIER)
13699     {
13700       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13701
13702       RedrawAllInvisibleElementsForMagnifier();
13703     }
13704     else if (IS_DROPPABLE(element) ||
13705              IS_THROWABLE(element))     /* can be collected and dropped */
13706     {
13707       int i;
13708
13709       if (collect_count == 0)
13710         player->inventory_infinite_element = element;
13711       else
13712         for (i = 0; i < collect_count; i++)
13713           if (player->inventory_size < MAX_INVENTORY_SIZE)
13714             player->inventory_element[player->inventory_size++] = element;
13715
13716       DrawGameDoorValues();
13717     }
13718     else if (collect_count > 0)
13719     {
13720       local_player->gems_still_needed -= collect_count;
13721       if (local_player->gems_still_needed < 0)
13722         local_player->gems_still_needed = 0;
13723
13724 #if 1
13725       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13726
13727       DisplayGameControlValues();
13728 #else
13729       DrawGameValue_Emeralds(local_player->gems_still_needed);
13730 #endif
13731     }
13732
13733     RaiseScoreElement(element);
13734     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13735
13736     if (is_player)
13737       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13738                                           player->index_bit, dig_side);
13739
13740     if (mode == DF_SNAP)
13741     {
13742 #if USE_NEW_SNAP_DELAY
13743       if (level.block_snap_field)
13744         setFieldForSnapping(x, y, element, move_direction);
13745       else
13746         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13747 #else
13748       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13749 #endif
13750
13751       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13752                                           player->index_bit, dig_side);
13753     }
13754   }
13755   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13756   {
13757     if (mode == DF_SNAP && element != EL_BD_ROCK)
13758       return MP_NO_ACTION;
13759
13760     if (CAN_FALL(element) && dy)
13761       return MP_NO_ACTION;
13762
13763     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13764         !(element == EL_SPRING && level.use_spring_bug))
13765       return MP_NO_ACTION;
13766
13767     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13768         ((move_direction & MV_VERTICAL &&
13769           ((element_info[element].move_pattern & MV_LEFT &&
13770             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13771            (element_info[element].move_pattern & MV_RIGHT &&
13772             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13773          (move_direction & MV_HORIZONTAL &&
13774           ((element_info[element].move_pattern & MV_UP &&
13775             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13776            (element_info[element].move_pattern & MV_DOWN &&
13777             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13778       return MP_NO_ACTION;
13779
13780     /* do not push elements already moving away faster than player */
13781     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13782         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13783       return MP_NO_ACTION;
13784
13785     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13786     {
13787       if (player->push_delay_value == -1 || !player_was_pushing)
13788         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13789     }
13790     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13791     {
13792       if (player->push_delay_value == -1)
13793         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13794     }
13795     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13796     {
13797       if (!player->is_pushing)
13798         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13799     }
13800
13801     player->is_pushing = TRUE;
13802     player->is_active = TRUE;
13803
13804     if (!(IN_LEV_FIELD(nextx, nexty) &&
13805           (IS_FREE(nextx, nexty) ||
13806            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13807             IS_SB_ELEMENT(element)))))
13808       return MP_NO_ACTION;
13809
13810     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13811       return MP_NO_ACTION;
13812
13813     if (player->push_delay == -1)       /* new pushing; restart delay */
13814       player->push_delay = 0;
13815
13816     if (player->push_delay < player->push_delay_value &&
13817         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13818         element != EL_SPRING && element != EL_BALLOON)
13819     {
13820       /* make sure that there is no move delay before next try to push */
13821       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13822         player->move_delay = 0;
13823
13824       return MP_NO_ACTION;
13825     }
13826
13827     if (IS_SB_ELEMENT(element))
13828     {
13829       if (element == EL_SOKOBAN_FIELD_FULL)
13830       {
13831         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13832         local_player->sokobanfields_still_needed++;
13833       }
13834
13835       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13836       {
13837         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13838         local_player->sokobanfields_still_needed--;
13839       }
13840
13841       Feld[x][y] = EL_SOKOBAN_OBJECT;
13842
13843       if (Back[x][y] == Back[nextx][nexty])
13844         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13845       else if (Back[x][y] != 0)
13846         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13847                                     ACTION_EMPTYING);
13848       else
13849         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13850                                     ACTION_FILLING);
13851
13852       if (local_player->sokobanfields_still_needed == 0 &&
13853           game.emulation == EMU_SOKOBAN)
13854       {
13855         PlayerWins(player);
13856
13857         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13858       }
13859     }
13860     else
13861       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13862
13863     InitMovingField(x, y, move_direction);
13864     GfxAction[x][y] = ACTION_PUSHING;
13865
13866     if (mode == DF_SNAP)
13867       ContinueMoving(x, y);
13868     else
13869       MovPos[x][y] = (dx != 0 ? dx : dy);
13870
13871     Pushed[x][y] = TRUE;
13872     Pushed[nextx][nexty] = TRUE;
13873
13874     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13875       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13876     else
13877       player->push_delay_value = -1;    /* get new value later */
13878
13879     /* check for element change _after_ element has been pushed */
13880     if (game.use_change_when_pushing_bug)
13881     {
13882       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13883                                  player->index_bit, dig_side);
13884       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13885                                           player->index_bit, dig_side);
13886     }
13887   }
13888   else if (IS_SWITCHABLE(element))
13889   {
13890     if (PLAYER_SWITCHING(player, x, y))
13891     {
13892       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13893                                           player->index_bit, dig_side);
13894
13895       return MP_ACTION;
13896     }
13897
13898     player->is_switching = TRUE;
13899     player->switch_x = x;
13900     player->switch_y = y;
13901
13902     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13903
13904     if (element == EL_ROBOT_WHEEL)
13905     {
13906       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13907       ZX = x;
13908       ZY = y;
13909
13910       DrawLevelField(x, y);
13911     }
13912     else if (element == EL_SP_TERMINAL)
13913     {
13914       int xx, yy;
13915
13916       SCAN_PLAYFIELD(xx, yy)
13917       {
13918         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13919           Bang(xx, yy);
13920         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13921           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13922       }
13923     }
13924     else if (IS_BELT_SWITCH(element))
13925     {
13926       ToggleBeltSwitch(x, y);
13927     }
13928     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13929              element == EL_SWITCHGATE_SWITCH_DOWN ||
13930              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13931              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13932     {
13933       ToggleSwitchgateSwitch(x, y);
13934     }
13935     else if (element == EL_LIGHT_SWITCH ||
13936              element == EL_LIGHT_SWITCH_ACTIVE)
13937     {
13938       ToggleLightSwitch(x, y);
13939     }
13940     else if (element == EL_TIMEGATE_SWITCH ||
13941              element == EL_DC_TIMEGATE_SWITCH)
13942     {
13943       ActivateTimegateSwitch(x, y);
13944     }
13945     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13946              element == EL_BALLOON_SWITCH_RIGHT ||
13947              element == EL_BALLOON_SWITCH_UP    ||
13948              element == EL_BALLOON_SWITCH_DOWN  ||
13949              element == EL_BALLOON_SWITCH_NONE  ||
13950              element == EL_BALLOON_SWITCH_ANY)
13951     {
13952       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13953                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13954                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13955                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13956                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13957                              move_direction);
13958     }
13959     else if (element == EL_LAMP)
13960     {
13961       Feld[x][y] = EL_LAMP_ACTIVE;
13962       local_player->lights_still_needed--;
13963
13964       ResetGfxAnimation(x, y);
13965       DrawLevelField(x, y);
13966     }
13967     else if (element == EL_TIME_ORB_FULL)
13968     {
13969       Feld[x][y] = EL_TIME_ORB_EMPTY;
13970
13971       if (level.time > 0 || level.use_time_orb_bug)
13972       {
13973         TimeLeft += level.time_orb_time;
13974
13975 #if 1
13976         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13977
13978         DisplayGameControlValues();
13979 #else
13980         DrawGameValue_Time(TimeLeft);
13981 #endif
13982       }
13983
13984       ResetGfxAnimation(x, y);
13985       DrawLevelField(x, y);
13986     }
13987     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13988              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13989     {
13990       int xx, yy;
13991
13992       game.ball_state = !game.ball_state;
13993
13994       SCAN_PLAYFIELD(xx, yy)
13995       {
13996         int e = Feld[xx][yy];
13997
13998         if (game.ball_state)
13999         {
14000           if (e == EL_EMC_MAGIC_BALL)
14001             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14002           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14003             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14004         }
14005         else
14006         {
14007           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14008             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14009           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14010             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14011         }
14012       }
14013     }
14014
14015     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14016                                         player->index_bit, dig_side);
14017
14018     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14019                                         player->index_bit, dig_side);
14020
14021     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14022                                         player->index_bit, dig_side);
14023
14024     return MP_ACTION;
14025   }
14026   else
14027   {
14028     if (!PLAYER_SWITCHING(player, x, y))
14029     {
14030       player->is_switching = TRUE;
14031       player->switch_x = x;
14032       player->switch_y = y;
14033
14034       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14035                                  player->index_bit, dig_side);
14036       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14037                                           player->index_bit, dig_side);
14038
14039       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14040                                  player->index_bit, dig_side);
14041       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14042                                           player->index_bit, dig_side);
14043     }
14044
14045     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14046                                player->index_bit, dig_side);
14047     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14048                                         player->index_bit, dig_side);
14049
14050     return MP_NO_ACTION;
14051   }
14052
14053   player->push_delay = -1;
14054
14055   if (is_player)                /* function can also be called by EL_PENGUIN */
14056   {
14057     if (Feld[x][y] != element)          /* really digged/collected something */
14058     {
14059       player->is_collecting = !player->is_digging;
14060       player->is_active = TRUE;
14061     }
14062   }
14063
14064   return MP_MOVING;
14065 }
14066
14067 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14068 {
14069   int jx = player->jx, jy = player->jy;
14070   int x = jx + dx, y = jy + dy;
14071   int snap_direction = (dx == -1 ? MV_LEFT  :
14072                         dx == +1 ? MV_RIGHT :
14073                         dy == -1 ? MV_UP    :
14074                         dy == +1 ? MV_DOWN  : MV_NONE);
14075   boolean can_continue_snapping = (level.continuous_snapping &&
14076                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14077
14078   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14079     return FALSE;
14080
14081   if (!player->active || !IN_LEV_FIELD(x, y))
14082     return FALSE;
14083
14084   if (dx && dy)
14085     return FALSE;
14086
14087   if (!dx && !dy)
14088   {
14089     if (player->MovPos == 0)
14090       player->is_pushing = FALSE;
14091
14092     player->is_snapping = FALSE;
14093
14094     if (player->MovPos == 0)
14095     {
14096       player->is_moving = FALSE;
14097       player->is_digging = FALSE;
14098       player->is_collecting = FALSE;
14099     }
14100
14101     return FALSE;
14102   }
14103
14104 #if USE_NEW_CONTINUOUS_SNAPPING
14105   /* prevent snapping with already pressed snap key when not allowed */
14106   if (player->is_snapping && !can_continue_snapping)
14107     return FALSE;
14108 #else
14109   if (player->is_snapping)
14110     return FALSE;
14111 #endif
14112
14113   player->MovDir = snap_direction;
14114
14115   if (player->MovPos == 0)
14116   {
14117     player->is_moving = FALSE;
14118     player->is_digging = FALSE;
14119     player->is_collecting = FALSE;
14120   }
14121
14122   player->is_dropping = FALSE;
14123   player->is_dropping_pressed = FALSE;
14124   player->drop_pressed_delay = 0;
14125
14126   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14127     return FALSE;
14128
14129   player->is_snapping = TRUE;
14130   player->is_active = TRUE;
14131
14132   if (player->MovPos == 0)
14133   {
14134     player->is_moving = FALSE;
14135     player->is_digging = FALSE;
14136     player->is_collecting = FALSE;
14137   }
14138
14139   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14140     DrawLevelField(player->last_jx, player->last_jy);
14141
14142   DrawLevelField(x, y);
14143
14144   return TRUE;
14145 }
14146
14147 boolean DropElement(struct PlayerInfo *player)
14148 {
14149   int old_element, new_element;
14150   int dropx = player->jx, dropy = player->jy;
14151   int drop_direction = player->MovDir;
14152   int drop_side = drop_direction;
14153 #if 1
14154   int drop_element = get_next_drop_element(player);
14155 #else
14156   int drop_element = (player->inventory_size > 0 ?
14157                       player->inventory_element[player->inventory_size - 1] :
14158                       player->inventory_infinite_element != EL_UNDEFINED ?
14159                       player->inventory_infinite_element :
14160                       player->dynabombs_left > 0 ?
14161                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14162                       EL_UNDEFINED);
14163 #endif
14164
14165   player->is_dropping_pressed = TRUE;
14166
14167   /* do not drop an element on top of another element; when holding drop key
14168      pressed without moving, dropped element must move away before the next
14169      element can be dropped (this is especially important if the next element
14170      is dynamite, which can be placed on background for historical reasons) */
14171   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14172     return MP_ACTION;
14173
14174   if (IS_THROWABLE(drop_element))
14175   {
14176     dropx += GET_DX_FROM_DIR(drop_direction);
14177     dropy += GET_DY_FROM_DIR(drop_direction);
14178
14179     if (!IN_LEV_FIELD(dropx, dropy))
14180       return FALSE;
14181   }
14182
14183   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14184   new_element = drop_element;           /* default: no change when dropping */
14185
14186   /* check if player is active, not moving and ready to drop */
14187   if (!player->active || player->MovPos || player->drop_delay > 0)
14188     return FALSE;
14189
14190   /* check if player has anything that can be dropped */
14191   if (new_element == EL_UNDEFINED)
14192     return FALSE;
14193
14194   /* check if drop key was pressed long enough for EM style dynamite */
14195   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14196     return FALSE;
14197
14198   /* check if anything can be dropped at the current position */
14199   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14200     return FALSE;
14201
14202   /* collected custom elements can only be dropped on empty fields */
14203   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14204     return FALSE;
14205
14206   if (old_element != EL_EMPTY)
14207     Back[dropx][dropy] = old_element;   /* store old element on this field */
14208
14209   ResetGfxAnimation(dropx, dropy);
14210   ResetRandomAnimationValue(dropx, dropy);
14211
14212   if (player->inventory_size > 0 ||
14213       player->inventory_infinite_element != EL_UNDEFINED)
14214   {
14215     if (player->inventory_size > 0)
14216     {
14217       player->inventory_size--;
14218
14219       DrawGameDoorValues();
14220
14221       if (new_element == EL_DYNAMITE)
14222         new_element = EL_DYNAMITE_ACTIVE;
14223       else if (new_element == EL_EM_DYNAMITE)
14224         new_element = EL_EM_DYNAMITE_ACTIVE;
14225       else if (new_element == EL_SP_DISK_RED)
14226         new_element = EL_SP_DISK_RED_ACTIVE;
14227     }
14228
14229     Feld[dropx][dropy] = new_element;
14230
14231     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14232       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14233                           el2img(Feld[dropx][dropy]), 0);
14234
14235     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14236
14237     /* needed if previous element just changed to "empty" in the last frame */
14238     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14239
14240     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14241                                player->index_bit, drop_side);
14242     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14243                                         CE_PLAYER_DROPS_X,
14244                                         player->index_bit, drop_side);
14245
14246     TestIfElementTouchesCustomElement(dropx, dropy);
14247   }
14248   else          /* player is dropping a dyna bomb */
14249   {
14250     player->dynabombs_left--;
14251
14252     Feld[dropx][dropy] = new_element;
14253
14254     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14255       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14256                           el2img(Feld[dropx][dropy]), 0);
14257
14258     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14259   }
14260
14261   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14262     InitField_WithBug1(dropx, dropy, FALSE);
14263
14264   new_element = Feld[dropx][dropy];     /* element might have changed */
14265
14266   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14267       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14268   {
14269     int move_direction, nextx, nexty;
14270
14271     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14272       MovDir[dropx][dropy] = drop_direction;
14273
14274     move_direction = MovDir[dropx][dropy];
14275     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14276     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14277
14278     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14279
14280 #if USE_FIX_IMPACT_COLLISION
14281     /* do not cause impact style collision by dropping elements that can fall */
14282     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14283 #else
14284     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14285 #endif
14286   }
14287
14288   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14289   player->is_dropping = TRUE;
14290
14291   player->drop_pressed_delay = 0;
14292   player->is_dropping_pressed = FALSE;
14293
14294   player->drop_x = dropx;
14295   player->drop_y = dropy;
14296
14297   return TRUE;
14298 }
14299
14300 /* ------------------------------------------------------------------------- */
14301 /* game sound playing functions                                              */
14302 /* ------------------------------------------------------------------------- */
14303
14304 static int *loop_sound_frame = NULL;
14305 static int *loop_sound_volume = NULL;
14306
14307 void InitPlayLevelSound()
14308 {
14309   int num_sounds = getSoundListSize();
14310
14311   checked_free(loop_sound_frame);
14312   checked_free(loop_sound_volume);
14313
14314   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14315   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14316 }
14317
14318 static void PlayLevelSound(int x, int y, int nr)
14319 {
14320   int sx = SCREENX(x), sy = SCREENY(y);
14321   int volume, stereo_position;
14322   int max_distance = 8;
14323   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14324
14325   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14326       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14327     return;
14328
14329   if (!IN_LEV_FIELD(x, y) ||
14330       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14331       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14332     return;
14333
14334   volume = SOUND_MAX_VOLUME;
14335
14336   if (!IN_SCR_FIELD(sx, sy))
14337   {
14338     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14339     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14340
14341     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14342   }
14343
14344   stereo_position = (SOUND_MAX_LEFT +
14345                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14346                      (SCR_FIELDX + 2 * max_distance));
14347
14348   if (IS_LOOP_SOUND(nr))
14349   {
14350     /* This assures that quieter loop sounds do not overwrite louder ones,
14351        while restarting sound volume comparison with each new game frame. */
14352
14353     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14354       return;
14355
14356     loop_sound_volume[nr] = volume;
14357     loop_sound_frame[nr] = FrameCounter;
14358   }
14359
14360   PlaySoundExt(nr, volume, stereo_position, type);
14361 }
14362
14363 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14364 {
14365   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14366                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14367                  y < LEVELY(BY1) ? LEVELY(BY1) :
14368                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14369                  sound_action);
14370 }
14371
14372 static void PlayLevelSoundAction(int x, int y, int action)
14373 {
14374   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14375 }
14376
14377 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14378 {
14379   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14380
14381   if (sound_effect != SND_UNDEFINED)
14382     PlayLevelSound(x, y, sound_effect);
14383 }
14384
14385 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14386                                               int action)
14387 {
14388   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14389
14390   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14391     PlayLevelSound(x, y, sound_effect);
14392 }
14393
14394 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14395 {
14396   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14397
14398   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14399     PlayLevelSound(x, y, sound_effect);
14400 }
14401
14402 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14403 {
14404   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14405
14406   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14407     StopSound(sound_effect);
14408 }
14409
14410 static void PlayLevelMusic()
14411 {
14412   if (levelset.music[level_nr] != MUS_UNDEFINED)
14413     PlayMusic(levelset.music[level_nr]);        /* from config file */
14414   else
14415     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14416 }
14417
14418 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14419 {
14420   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14421   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14422   int x = xx - 1 - offset;
14423   int y = yy - 1 - offset;
14424
14425   switch (sample)
14426   {
14427     case SAMPLE_blank:
14428       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14429       break;
14430
14431     case SAMPLE_roll:
14432       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14433       break;
14434
14435     case SAMPLE_stone:
14436       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14437       break;
14438
14439     case SAMPLE_nut:
14440       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14441       break;
14442
14443     case SAMPLE_crack:
14444       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14445       break;
14446
14447     case SAMPLE_bug:
14448       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14449       break;
14450
14451     case SAMPLE_tank:
14452       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14453       break;
14454
14455     case SAMPLE_android_clone:
14456       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14457       break;
14458
14459     case SAMPLE_android_move:
14460       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14461       break;
14462
14463     case SAMPLE_spring:
14464       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14465       break;
14466
14467     case SAMPLE_slurp:
14468       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14469       break;
14470
14471     case SAMPLE_eater:
14472       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14473       break;
14474
14475     case SAMPLE_eater_eat:
14476       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14477       break;
14478
14479     case SAMPLE_alien:
14480       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14481       break;
14482
14483     case SAMPLE_collect:
14484       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14485       break;
14486
14487     case SAMPLE_diamond:
14488       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14489       break;
14490
14491     case SAMPLE_squash:
14492       /* !!! CHECK THIS !!! */
14493 #if 1
14494       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14495 #else
14496       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14497 #endif
14498       break;
14499
14500     case SAMPLE_wonderfall:
14501       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14502       break;
14503
14504     case SAMPLE_drip:
14505       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14506       break;
14507
14508     case SAMPLE_push:
14509       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14510       break;
14511
14512     case SAMPLE_dirt:
14513       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14514       break;
14515
14516     case SAMPLE_acid:
14517       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14518       break;
14519
14520     case SAMPLE_ball:
14521       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14522       break;
14523
14524     case SAMPLE_grow:
14525       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14526       break;
14527
14528     case SAMPLE_wonder:
14529       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14530       break;
14531
14532     case SAMPLE_door:
14533       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14534       break;
14535
14536     case SAMPLE_exit_open:
14537       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14538       break;
14539
14540     case SAMPLE_exit_leave:
14541       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14542       break;
14543
14544     case SAMPLE_dynamite:
14545       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14546       break;
14547
14548     case SAMPLE_tick:
14549       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14550       break;
14551
14552     case SAMPLE_press:
14553       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14554       break;
14555
14556     case SAMPLE_wheel:
14557       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14558       break;
14559
14560     case SAMPLE_boom:
14561       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14562       break;
14563
14564     case SAMPLE_die:
14565       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14566       break;
14567
14568     case SAMPLE_time:
14569       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14570       break;
14571
14572     default:
14573       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14574       break;
14575   }
14576 }
14577
14578 #if 0
14579 void ChangeTime(int value)
14580 {
14581   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14582
14583   *time += value;
14584
14585   /* EMC game engine uses value from time counter of RND game engine */
14586   level.native_em_level->lev->time = *time;
14587
14588   DrawGameValue_Time(*time);
14589 }
14590
14591 void RaiseScore(int value)
14592 {
14593   /* EMC game engine and RND game engine have separate score counters */
14594   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14595                 &level.native_em_level->lev->score : &local_player->score);
14596
14597   *score += value;
14598
14599   DrawGameValue_Score(*score);
14600 }
14601 #endif
14602
14603 void RaiseScore(int value)
14604 {
14605   local_player->score += value;
14606
14607 #if 1
14608   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14609
14610   DisplayGameControlValues();
14611 #else
14612   DrawGameValue_Score(local_player->score);
14613 #endif
14614 }
14615
14616 void RaiseScoreElement(int element)
14617 {
14618   switch (element)
14619   {
14620     case EL_EMERALD:
14621     case EL_BD_DIAMOND:
14622     case EL_EMERALD_YELLOW:
14623     case EL_EMERALD_RED:
14624     case EL_EMERALD_PURPLE:
14625     case EL_SP_INFOTRON:
14626       RaiseScore(level.score[SC_EMERALD]);
14627       break;
14628     case EL_DIAMOND:
14629       RaiseScore(level.score[SC_DIAMOND]);
14630       break;
14631     case EL_CRYSTAL:
14632       RaiseScore(level.score[SC_CRYSTAL]);
14633       break;
14634     case EL_PEARL:
14635       RaiseScore(level.score[SC_PEARL]);
14636       break;
14637     case EL_BUG:
14638     case EL_BD_BUTTERFLY:
14639     case EL_SP_ELECTRON:
14640       RaiseScore(level.score[SC_BUG]);
14641       break;
14642     case EL_SPACESHIP:
14643     case EL_BD_FIREFLY:
14644     case EL_SP_SNIKSNAK:
14645       RaiseScore(level.score[SC_SPACESHIP]);
14646       break;
14647     case EL_YAMYAM:
14648     case EL_DARK_YAMYAM:
14649       RaiseScore(level.score[SC_YAMYAM]);
14650       break;
14651     case EL_ROBOT:
14652       RaiseScore(level.score[SC_ROBOT]);
14653       break;
14654     case EL_PACMAN:
14655       RaiseScore(level.score[SC_PACMAN]);
14656       break;
14657     case EL_NUT:
14658       RaiseScore(level.score[SC_NUT]);
14659       break;
14660     case EL_DYNAMITE:
14661     case EL_EM_DYNAMITE:
14662     case EL_SP_DISK_RED:
14663     case EL_DYNABOMB_INCREASE_NUMBER:
14664     case EL_DYNABOMB_INCREASE_SIZE:
14665     case EL_DYNABOMB_INCREASE_POWER:
14666       RaiseScore(level.score[SC_DYNAMITE]);
14667       break;
14668     case EL_SHIELD_NORMAL:
14669     case EL_SHIELD_DEADLY:
14670       RaiseScore(level.score[SC_SHIELD]);
14671       break;
14672     case EL_EXTRA_TIME:
14673       RaiseScore(level.extra_time_score);
14674       break;
14675     case EL_KEY_1:
14676     case EL_KEY_2:
14677     case EL_KEY_3:
14678     case EL_KEY_4:
14679     case EL_EM_KEY_1:
14680     case EL_EM_KEY_2:
14681     case EL_EM_KEY_3:
14682     case EL_EM_KEY_4:
14683     case EL_EMC_KEY_5:
14684     case EL_EMC_KEY_6:
14685     case EL_EMC_KEY_7:
14686     case EL_EMC_KEY_8:
14687     case EL_DC_KEY_WHITE:
14688       RaiseScore(level.score[SC_KEY]);
14689       break;
14690     default:
14691       RaiseScore(element_info[element].collect_score);
14692       break;
14693   }
14694 }
14695
14696 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14697 {
14698   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14699   {
14700 #if defined(NETWORK_AVALIABLE)
14701     if (options.network)
14702       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14703     else
14704 #endif
14705     {
14706       if (quick_quit)
14707       {
14708 #if 1
14709
14710 #if 1
14711         FadeSkipNextFadeIn();
14712 #else
14713         fading = fading_none;
14714 #endif
14715
14716 #else
14717         OpenDoor(DOOR_CLOSE_1);
14718 #endif
14719
14720         game_status = GAME_MODE_MAIN;
14721
14722 #if 1
14723         DrawAndFadeInMainMenu(REDRAW_FIELD);
14724 #else
14725         DrawMainMenu();
14726 #endif
14727       }
14728       else
14729       {
14730 #if 0
14731         FadeOut(REDRAW_FIELD);
14732 #endif
14733
14734         game_status = GAME_MODE_MAIN;
14735
14736         DrawAndFadeInMainMenu(REDRAW_FIELD);
14737       }
14738     }
14739   }
14740   else          /* continue playing the game */
14741   {
14742     if (tape.playing && tape.deactivate_display)
14743       TapeDeactivateDisplayOff(TRUE);
14744
14745     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14746
14747     if (tape.playing && tape.deactivate_display)
14748       TapeDeactivateDisplayOn();
14749   }
14750 }
14751
14752 void RequestQuitGame(boolean ask_if_really_quit)
14753 {
14754   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14755   boolean skip_request = AllPlayersGone || quick_quit;
14756
14757   RequestQuitGameExt(skip_request, quick_quit,
14758                      "Do you really want to quit the game ?");
14759 }
14760
14761
14762 /* ------------------------------------------------------------------------- */
14763 /* random generator functions                                                */
14764 /* ------------------------------------------------------------------------- */
14765
14766 unsigned int InitEngineRandom_RND(long seed)
14767 {
14768   game.num_random_calls = 0;
14769
14770 #if 0
14771   unsigned int rnd_seed = InitEngineRandom(seed);
14772
14773   printf("::: START RND: %d\n", rnd_seed);
14774
14775   return rnd_seed;
14776 #else
14777
14778   return InitEngineRandom(seed);
14779
14780 #endif
14781
14782 }
14783
14784 unsigned int RND(int max)
14785 {
14786   if (max > 0)
14787   {
14788     game.num_random_calls++;
14789
14790     return GetEngineRandom(max);
14791   }
14792
14793   return 0;
14794 }
14795
14796
14797 /* ------------------------------------------------------------------------- */
14798 /* game engine snapshot handling functions                                   */
14799 /* ------------------------------------------------------------------------- */
14800
14801 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14802
14803 struct EngineSnapshotInfo
14804 {
14805   /* runtime values for custom element collect score */
14806   int collect_score[NUM_CUSTOM_ELEMENTS];
14807
14808   /* runtime values for group element choice position */
14809   int choice_pos[NUM_GROUP_ELEMENTS];
14810
14811   /* runtime values for belt position animations */
14812   int belt_graphic[4 * NUM_BELT_PARTS];
14813   int belt_anim_mode[4 * NUM_BELT_PARTS];
14814 };
14815
14816 struct EngineSnapshotNodeInfo
14817 {
14818   void *buffer_orig;
14819   void *buffer_copy;
14820   int size;
14821 };
14822
14823 static struct EngineSnapshotInfo engine_snapshot_rnd;
14824 static ListNode *engine_snapshot_list = NULL;
14825 static char *snapshot_level_identifier = NULL;
14826 static int snapshot_level_nr = -1;
14827
14828 void FreeEngineSnapshot()
14829 {
14830   while (engine_snapshot_list != NULL)
14831     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14832                        checked_free);
14833
14834   setString(&snapshot_level_identifier, NULL);
14835   snapshot_level_nr = -1;
14836 }
14837
14838 static void SaveEngineSnapshotValues_RND()
14839 {
14840   static int belt_base_active_element[4] =
14841   {
14842     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14843     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14844     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14845     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14846   };
14847   int i, j;
14848
14849   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14850   {
14851     int element = EL_CUSTOM_START + i;
14852
14853     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14854   }
14855
14856   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14857   {
14858     int element = EL_GROUP_START + i;
14859
14860     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14861   }
14862
14863   for (i = 0; i < 4; i++)
14864   {
14865     for (j = 0; j < NUM_BELT_PARTS; j++)
14866     {
14867       int element = belt_base_active_element[i] + j;
14868       int graphic = el2img(element);
14869       int anim_mode = graphic_info[graphic].anim_mode;
14870
14871       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14872       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14873     }
14874   }
14875 }
14876
14877 static void LoadEngineSnapshotValues_RND()
14878 {
14879   unsigned long num_random_calls = game.num_random_calls;
14880   int i, j;
14881
14882   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14883   {
14884     int element = EL_CUSTOM_START + i;
14885
14886     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14887   }
14888
14889   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14890   {
14891     int element = EL_GROUP_START + i;
14892
14893     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14894   }
14895
14896   for (i = 0; i < 4; i++)
14897   {
14898     for (j = 0; j < NUM_BELT_PARTS; j++)
14899     {
14900       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14901       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14902
14903       graphic_info[graphic].anim_mode = anim_mode;
14904     }
14905   }
14906
14907   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14908   {
14909     InitRND(tape.random_seed);
14910     for (i = 0; i < num_random_calls; i++)
14911       RND(1);
14912   }
14913
14914   if (game.num_random_calls != num_random_calls)
14915   {
14916     Error(ERR_INFO, "number of random calls out of sync");
14917     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14918     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14919     Error(ERR_EXIT, "this should not happen -- please debug");
14920   }
14921 }
14922
14923 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14924 {
14925   struct EngineSnapshotNodeInfo *bi =
14926     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14927
14928   bi->buffer_orig = buffer;
14929   bi->buffer_copy = checked_malloc(size);
14930   bi->size = size;
14931
14932   memcpy(bi->buffer_copy, buffer, size);
14933
14934   addNodeToList(&engine_snapshot_list, NULL, bi);
14935 }
14936
14937 void SaveEngineSnapshot()
14938 {
14939   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14940
14941   if (level_editor_test_game)   /* do not save snapshots from editor */
14942     return;
14943
14944   /* copy some special values to a structure better suited for the snapshot */
14945
14946   SaveEngineSnapshotValues_RND();
14947   SaveEngineSnapshotValues_EM();
14948
14949   /* save values stored in special snapshot structure */
14950
14951   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14952   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14953
14954   /* save further RND engine values */
14955
14956   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14957   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14959
14960   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14961   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14962   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14963   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14964
14965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14966   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14967   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14968   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14970
14971   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14972   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14973   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14974
14975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14976
14977   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14978
14979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14981
14982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14983   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14988   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14996   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15000
15001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15003
15004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15005   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15007
15008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15009   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15010
15011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15012   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15013   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15014   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15016
15017   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15018   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15019
15020   /* save level identification information */
15021
15022   setString(&snapshot_level_identifier, leveldir_current->identifier);
15023   snapshot_level_nr = level_nr;
15024
15025 #if 0
15026   ListNode *node = engine_snapshot_list;
15027   int num_bytes = 0;
15028
15029   while (node != NULL)
15030   {
15031     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15032
15033     node = node->next;
15034   }
15035
15036   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15037 #endif
15038 }
15039
15040 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15041 {
15042   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15043 }
15044
15045 void LoadEngineSnapshot()
15046 {
15047   ListNode *node = engine_snapshot_list;
15048
15049   if (engine_snapshot_list == NULL)
15050     return;
15051
15052   while (node != NULL)
15053   {
15054     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15055
15056     node = node->next;
15057   }
15058
15059   /* restore special values from snapshot structure */
15060
15061   LoadEngineSnapshotValues_RND();
15062   LoadEngineSnapshotValues_EM();
15063 }
15064
15065 boolean CheckEngineSnapshot()
15066 {
15067   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15068           snapshot_level_nr == level_nr);
15069 }
15070
15071
15072 /* ---------- new game button stuff ---------------------------------------- */
15073
15074 /* graphic position values for game buttons */
15075 #define GAME_BUTTON_XSIZE       30
15076 #define GAME_BUTTON_YSIZE       30
15077 #define GAME_BUTTON_XPOS        5
15078 #define GAME_BUTTON_YPOS        215
15079 #define SOUND_BUTTON_XPOS       5
15080 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15081
15082 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15083 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15084 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15085 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15086 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15087 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15088
15089 static struct
15090 {
15091   int *x, *y;
15092   int gd_x, gd_y;
15093   int gadget_id;
15094   char *infotext;
15095 } gamebutton_info[NUM_GAME_BUTTONS] =
15096 {
15097 #if 1
15098   {
15099     &game.button.stop.x,        &game.button.stop.y,
15100     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15101     GAME_CTRL_ID_STOP,
15102     "stop game"
15103   },
15104   {
15105     &game.button.pause.x,       &game.button.pause.y,
15106     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15107     GAME_CTRL_ID_PAUSE,
15108     "pause game"
15109   },
15110   {
15111     &game.button.play.x,        &game.button.play.y,
15112     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15113     GAME_CTRL_ID_PLAY,
15114     "play game"
15115   },
15116   {
15117     &game.button.sound_music.x, &game.button.sound_music.y,
15118     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15119     SOUND_CTRL_ID_MUSIC,
15120     "background music on/off"
15121   },
15122   {
15123     &game.button.sound_loops.x, &game.button.sound_loops.y,
15124     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15125     SOUND_CTRL_ID_LOOPS,
15126     "sound loops on/off"
15127   },
15128   {
15129     &game.button.sound_simple.x,&game.button.sound_simple.y,
15130     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15131     SOUND_CTRL_ID_SIMPLE,
15132     "normal sounds on/off"
15133   }
15134 #else
15135   {
15136     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15137     GAME_CTRL_ID_STOP,
15138     "stop game"
15139   },
15140   {
15141     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15142     GAME_CTRL_ID_PAUSE,
15143     "pause game"
15144   },
15145   {
15146     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15147     GAME_CTRL_ID_PLAY,
15148     "play game"
15149   },
15150   {
15151     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15152     SOUND_CTRL_ID_MUSIC,
15153     "background music on/off"
15154   },
15155   {
15156     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15157     SOUND_CTRL_ID_LOOPS,
15158     "sound loops on/off"
15159   },
15160   {
15161     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15162     SOUND_CTRL_ID_SIMPLE,
15163     "normal sounds on/off"
15164   }
15165 #endif
15166 };
15167
15168 void CreateGameButtons()
15169 {
15170   int i;
15171
15172   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15173   {
15174     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15175     struct GadgetInfo *gi;
15176     int button_type;
15177     boolean checked;
15178     unsigned long event_mask;
15179     int x, y;
15180     int gd_xoffset, gd_yoffset;
15181     int gd_x1, gd_x2, gd_y1, gd_y2;
15182     int id = i;
15183
15184     x = DX + *gamebutton_info[i].x;
15185     y = DY + *gamebutton_info[i].y;
15186     gd_xoffset = gamebutton_info[i].gd_x;
15187     gd_yoffset = gamebutton_info[i].gd_y;
15188     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15189     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15190
15191     if (id == GAME_CTRL_ID_STOP ||
15192         id == GAME_CTRL_ID_PAUSE ||
15193         id == GAME_CTRL_ID_PLAY)
15194     {
15195       button_type = GD_TYPE_NORMAL_BUTTON;
15196       checked = FALSE;
15197       event_mask = GD_EVENT_RELEASED;
15198       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15199       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15200     }
15201     else
15202     {
15203       button_type = GD_TYPE_CHECK_BUTTON;
15204       checked =
15205         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15206          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15207          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15208       event_mask = GD_EVENT_PRESSED;
15209       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15210       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15211     }
15212
15213     gi = CreateGadget(GDI_CUSTOM_ID, id,
15214                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15215 #if 1
15216                       GDI_X, x,
15217                       GDI_Y, y,
15218 #else
15219                       GDI_X, DX + gd_xoffset,
15220                       GDI_Y, DY + gd_yoffset,
15221 #endif
15222                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15223                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15224                       GDI_TYPE, button_type,
15225                       GDI_STATE, GD_BUTTON_UNPRESSED,
15226                       GDI_CHECKED, checked,
15227                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15228                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15229                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15230                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15231                       GDI_EVENT_MASK, event_mask,
15232                       GDI_CALLBACK_ACTION, HandleGameButtons,
15233                       GDI_END);
15234
15235     if (gi == NULL)
15236       Error(ERR_EXIT, "cannot create gadget");
15237
15238     game_gadget[id] = gi;
15239   }
15240 }
15241
15242 void FreeGameButtons()
15243 {
15244   int i;
15245
15246   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15247     FreeGadget(game_gadget[i]);
15248 }
15249
15250 static void MapGameButtons()
15251 {
15252   int i;
15253
15254   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15255     MapGadget(game_gadget[i]);
15256 }
15257
15258 void UnmapGameButtons()
15259 {
15260   int i;
15261
15262   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15263     UnmapGadget(game_gadget[i]);
15264 }
15265
15266 static void HandleGameButtons(struct GadgetInfo *gi)
15267 {
15268   int id = gi->custom_id;
15269
15270   if (game_status != GAME_MODE_PLAYING)
15271     return;
15272
15273   switch (id)
15274   {
15275     case GAME_CTRL_ID_STOP:
15276       if (tape.playing)
15277         TapeStop();
15278       else
15279         RequestQuitGame(TRUE);
15280       break;
15281
15282     case GAME_CTRL_ID_PAUSE:
15283       if (options.network)
15284       {
15285 #if defined(NETWORK_AVALIABLE)
15286         if (tape.pausing)
15287           SendToServer_ContinuePlaying();
15288         else
15289           SendToServer_PausePlaying();
15290 #endif
15291       }
15292       else
15293         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15294       break;
15295
15296     case GAME_CTRL_ID_PLAY:
15297       if (tape.pausing)
15298       {
15299 #if defined(NETWORK_AVALIABLE)
15300         if (options.network)
15301           SendToServer_ContinuePlaying();
15302         else
15303 #endif
15304         {
15305           tape.pausing = FALSE;
15306           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15307         }
15308       }
15309       break;
15310
15311     case SOUND_CTRL_ID_MUSIC:
15312       if (setup.sound_music)
15313       { 
15314         setup.sound_music = FALSE;
15315         FadeMusic();
15316       }
15317       else if (audio.music_available)
15318       { 
15319         setup.sound = setup.sound_music = TRUE;
15320
15321         SetAudioMode(setup.sound);
15322
15323         PlayLevelMusic();
15324       }
15325       break;
15326
15327     case SOUND_CTRL_ID_LOOPS:
15328       if (setup.sound_loops)
15329         setup.sound_loops = FALSE;
15330       else if (audio.loops_available)
15331       {
15332         setup.sound = setup.sound_loops = TRUE;
15333         SetAudioMode(setup.sound);
15334       }
15335       break;
15336
15337     case SOUND_CTRL_ID_SIMPLE:
15338       if (setup.sound_simple)
15339         setup.sound_simple = FALSE;
15340       else if (audio.sound_available)
15341       {
15342         setup.sound = setup.sound_simple = TRUE;
15343         SetAudioMode(setup.sound);
15344       }
15345       break;
15346
15347     default:
15348       break;
15349   }
15350 }